Adding Room database in generated Projects #246
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces an optional Room-backed data layer in generated Android projects, controlled via a new --room-database CLI flag and corresponding versions.android.roomDatabase switch.
Changes:
- Add Room version catalog entries + conditional dependency/plugin wiring (KSP/KAPT) when Room generation is enabled.
- Introduce a new “Room-based” Android class generation pipeline (planner/classes/tests/app wiring), while preserving the previous behavior via
*Legacygenerators. - Update Android Gradle plugin namespaces/applicationId to
com.awesomeapp.*and extend YAML/CLI version generation to include Room settings.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| project-generator/src/main/kotlin/io/github/cdsap/projectgenerator/writer/AndroidModulesWriter.kt | Switches between Room vs legacy planners/generators based on versions.android.roomDatabase. |
| project-generator/src/main/kotlin/io/github/cdsap/projectgenerator/model/Versions.kt | Adds android.room version and android.roomDatabase feature flag. |
| project-generator/src/main/kotlin/io/github/cdsap/projectgenerator/model/ClassTypeAndroid.kt | Adds new generated types (ENTITY/DAO/DATABASE/SCREEN) and dependency metadata. |
| project-generator/src/main/kotlin/io/github/cdsap/projectgenerator/generator/toml/AndroidToml.kt | Adds Room to version catalog + conditionally emits Room deps; switches annotation processor deps to add(...) form. |
| project-generator/src/main/kotlin/io/github/cdsap/projectgenerator/generator/test/TestGeneratorAndroidLegacy.kt | New legacy test generator to preserve pre-Room generated tests. |
| project-generator/src/main/kotlin/io/github/cdsap/projectgenerator/generator/test/TestGeneratorAndroid.kt | Adds Room-oriented tests for entity/dao/db/repository/usecase + Robolectric runner setup for DB tests. |
| project-generator/src/main/kotlin/io/github/cdsap/projectgenerator/generator/resources/ResourceGenerator.kt | Propagates Room flag into Application class generation. |
| project-generator/src/main/kotlin/io/github/cdsap/projectgenerator/generator/plugins/android/CompositeBuildPluginAndroidLib.kt | Updates namespace and applies KSP when Room is enabled (or Hilt). |
| project-generator/src/main/kotlin/io/github/cdsap/projectgenerator/generator/plugins/android/CompositeBuildPluginAndroidApp.kt | Updates namespace/applicationId and applies KSP when Room is enabled (or Hilt). |
| project-generator/src/main/kotlin/io/github/cdsap/projectgenerator/generator/planner/ModuleClassPlannerAndroidLegacy.kt | New legacy planner preserving previous class planning behavior. |
| project-generator/src/main/kotlin/io/github/cdsap/projectgenerator/generator/planner/ModuleClassPlannerAndroid.kt | New Room-first class plan including DB stack + Compose screen. |
| project-generator/src/main/kotlin/io/github/cdsap/projectgenerator/generator/classes/ClassGeneratorAndroidLegacy.kt | New legacy class generator preserving previous class outputs. |
| project-generator/src/main/kotlin/io/github/cdsap/projectgenerator/generator/classes/ClassGeneratorAndroid.kt | Updates generation to emit Room entity/dao/db + repository/usecase/viewmodel/screen flow. |
| project-generator/src/main/kotlin/io/github/cdsap/projectgenerator/generator/buildfiles/BuildFilesGeneratorAndroid.kt | Passes Room flag into dependency emission. |
| project-generator/src/main/kotlin/io/github/cdsap/projectgenerator/generator/android/AndroidApplication.kt | For Metro, adds Room provisioning in generated MainApplication when enabled. |
| cli/src/main/kotlin/io/github/cdsap/projectgenerator/cli/Main.kt | Adds --room-database flag and propagates it into Versions. |
| cli/src/main/kotlin/io/github/cdsap/projectgenerator/cli/GenerateVersionsYaml.kt | Emits room + roomDatabase into generated versions.yaml. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| val baseClasses = listOf( | ||
| ClassTypeAndroid.ENTITY, | ||
| ClassTypeAndroid.MODEL, | ||
| ClassTypeAndroid.DATABASE, | ||
| ClassTypeAndroid.DAO, | ||
| ClassTypeAndroid.REPOSITORY, | ||
| ClassTypeAndroid.USECASE, | ||
| ClassTypeAndroid.STATE, | ||
| ClassTypeAndroid.VIEWMODEL, | ||
| ClassTypeAndroid.SCREEN | ||
| ) | ||
|
|
||
| // If we have more than 1 class, add Activity | ||
| if (projectGraph.classes > 1) { | ||
| coreClasses.add( | ||
| ClassDefinitionAndroid( | ||
| type = ClassTypeAndroid.ACTIVITY, | ||
| index = currentIndex++, | ||
| dependencies = mutableListOf() // Activity doesn't need Fragment dependency yet | ||
| ) | ||
| ) | ||
| baseClasses.forEach { type -> | ||
| val deps = when (type) { | ||
| ClassTypeAndroid.REPOSITORY -> localDep(ClassTypeAndroid.DAO) | ||
| ClassTypeAndroid.USECASE -> localDep(ClassTypeAndroid.REPOSITORY) | ||
| ClassTypeAndroid.VIEWMODEL -> localDep(ClassTypeAndroid.USECASE) | ||
| ClassTypeAndroid.SCREEN -> localDep(ClassTypeAndroid.VIEWMODEL) | ||
| else -> mutableListOf() | ||
| } | ||
| classes.add(ClassDefinitionAndroid(type = type, index = currentIndex++, dependencies = deps)) | ||
| } |
There was a problem hiding this comment.
planModuleClasses currently unconditionally adds all baseClasses (9 types) and then optionally adds Activity/Fragment, regardless of projectGraph.classes. If the requested class count is smaller than this baseline, the planner will still generate more classes than requested (and remainingClasses goes negative), which changes the generator’s behavior and can dramatically increase generated code size/build time. Consider only adding up to projectGraph.classes (e.g., take the first N base types and then conditionally append Activity/Fragment only when within the limit), or otherwise enforce classes.size <= projectGraph.classes before returning.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| |class $className ${injectAnnotation}constructor( | ||
| | $constructorParams | ||
| |) : ViewModel() { |
There was a problem hiding this comment.
For DependencyInjection.NONE, this generator still emits a ViewModel primary constructor that requires a useCase parameter. The generated activities/fragments use the default ViewModelProvider APIs, which cannot instantiate ViewModels with non-empty constructors without a custom factory, so the resulting generated app will crash at runtime when --di none is used with --room-database. Either disallow that flag combination (e.g., validate in CLI) or generate a no-arg ViewModel / factory-based ViewModel creation path for the NONE case.
| private val roomDatabase by option("--room-database").flag(default = false) | ||
|
|
||
|
|
||
| override fun run() { | ||
| val typeOfProjectRequested = TypeProjectRequested.valueOf(type.uppercase()) | ||
| val shape = Shape.valueOf(shape.uppercase()) | ||
| val dependencyInjection = DependencyInjection.valueOf(di.uppercase()) | ||
| val versions = getVersions(versionsFile, develocityUrl, agp9).copy(di = dependencyInjection) | ||
| val versions = getVersions(versionsFile, develocityUrl, agp9, roomDatabase).copy(di = dependencyInjection) | ||
| val develocityEnabled = getDevelocityEnabled(develocity, develocityUrl) |
There was a problem hiding this comment.
--room-database can currently be combined with --di none, but the Room-based generator path produces constructor-injected classes (Repository/UseCase/ViewModel) without providing a way to build them when DI is disabled. This will generate projects that compile but fail at runtime. Consider adding CLI validation to reject --room-database when di == none (or implicitly switch DI to a supported option), unless you also generate the required manual wiring / factories for the NONE case.
No description provided.