From 2a259c6b06c3501530b5f49465464881425a9e68 Mon Sep 17 00:00:00 2001 From: Nikita Kozlov Date: Thu, 8 Feb 2018 08:44:36 +0100 Subject: [PATCH 1/5] Introduced `ActivityBlueprint` --- .../android_modules/ActivityGenerator.kt | 30 ++++++++----------- .../models/ActivityBlueprint.kt | 3 ++ 2 files changed, 16 insertions(+), 17 deletions(-) create mode 100644 src/main/kotlin/com/google/androidstudiopoet/models/ActivityBlueprint.kt diff --git a/src/main/kotlin/com/google/androidstudiopoet/generators/android_modules/ActivityGenerator.kt b/src/main/kotlin/com/google/androidstudiopoet/generators/android_modules/ActivityGenerator.kt index 20cb993e..bc535861 100644 --- a/src/main/kotlin/com/google/androidstudiopoet/generators/android_modules/ActivityGenerator.kt +++ b/src/main/kotlin/com/google/androidstudiopoet/generators/android_modules/ActivityGenerator.kt @@ -16,7 +16,7 @@ limitations under the License. package com.google.androidstudiopoet.generators.android_modules -import com.google.androidstudiopoet.GenerationResult +import com.google.androidstudiopoet.models.ActivityBlueprint import com.google.androidstudiopoet.models.AndroidModuleBlueprint import com.google.androidstudiopoet.writers.FileWriter import java.io.File @@ -28,30 +28,28 @@ class ActivityGenerator(var fileWriter: FileWriter) { */ fun generate(blueprint: AndroidModuleBlueprint) { - // generate activities - var index = 0 File(blueprint.packagePath).mkdirs() - while (index < blueprint.numOfActivities) { - generateClass(blueprint.activityNames[index], blueprint.layoutNames[index], blueprint.packagePath, blueprint.packageName) - index++ - } + (0..blueprint.numOfActivities) + .map { ActivityBlueprint(blueprint.activityNames[it], blueprint.layoutNames[it], blueprint.packagePath, blueprint.packageName) } + .forEach({ generateClass(it) }) + } - private fun generateClass(className: String, layout: String, where: String, packageName: String) { + private fun generateClass(blueprint: ActivityBlueprint) { // TODO add methods // TODO move to java poet val classText = - "package $packageName;\n" + + "package ${blueprint.packageName};\n" + "import android.app.Activity;\n" + "import android.os.Bundle;\n" + - "import $packageName.R;\n" + + "import ${blueprint.packageName}.R;\n" + "\n" + "\n" + - "public class " + className + " extends Activity {\n" + - " public $className() {\n" + + "public class " + blueprint.className + " extends Activity {\n" + + " public ${blueprint.className}() {\n" + " }\n" + "\n" + " /**\n" + @@ -63,16 +61,14 @@ class ActivityGenerator(var fileWriter: FileWriter) { "\n" + " // Set the layout for this activity. You can find it\n" + " // in res/layout/hello_activity.xml\n" + - " setContentView(R.layout." + layout + ");\n" + + " setContentView(R.layout." + blueprint.layout + ");\n" + " }\n" + "}\n" - println("$where/$className.java") + println("${blueprint.where}/${blueprint.className}.java") - fileWriter.writeToFile(classText, "$where/$className.java") + fileWriter.writeToFile(classText, "${blueprint.where}/${blueprint.className}.java") } - - data class ActivityGenerationResult(val activityNames: List) : GenerationResult } diff --git a/src/main/kotlin/com/google/androidstudiopoet/models/ActivityBlueprint.kt b/src/main/kotlin/com/google/androidstudiopoet/models/ActivityBlueprint.kt new file mode 100644 index 00000000..c6a80430 --- /dev/null +++ b/src/main/kotlin/com/google/androidstudiopoet/models/ActivityBlueprint.kt @@ -0,0 +1,3 @@ +package com.google.androidstudiopoet.models + +data class ActivityBlueprint(val className: String, val layout: String, val where: String, val packageName: String) \ No newline at end of file From 85a7272242988b14ac43dc346c73c7378a25dc1e Mon Sep 17 00:00:00 2001 From: Nikita Kozlov Date: Thu, 8 Feb 2018 08:50:11 +0100 Subject: [PATCH 2/5] Refactored `ActivityGenerator` so that it expects `ActivityBlueprint` --- .../android_modules/ActivityGenerator.kt | 24 ++------------ .../android_modules/AndroidModuleGenerator.kt | 3 +- .../models/AndroidModuleBlueprint.kt | 31 ++++++++++--------- 3 files changed, 21 insertions(+), 37 deletions(-) diff --git a/src/main/kotlin/com/google/androidstudiopoet/generators/android_modules/ActivityGenerator.kt b/src/main/kotlin/com/google/androidstudiopoet/generators/android_modules/ActivityGenerator.kt index bc535861..9ee1d38d 100644 --- a/src/main/kotlin/com/google/androidstudiopoet/generators/android_modules/ActivityGenerator.kt +++ b/src/main/kotlin/com/google/androidstudiopoet/generators/android_modules/ActivityGenerator.kt @@ -17,30 +17,11 @@ limitations under the License. package com.google.androidstudiopoet.generators.android_modules import com.google.androidstudiopoet.models.ActivityBlueprint -import com.google.androidstudiopoet.models.AndroidModuleBlueprint import com.google.androidstudiopoet.writers.FileWriter -import java.io.File class ActivityGenerator(var fileWriter: FileWriter) { - /** - * generates activity classes by blueprint, list of layouts and methods to call. - */ - fun generate(blueprint: AndroidModuleBlueprint) { - - - File(blueprint.packagePath).mkdirs() - - (0..blueprint.numOfActivities) - .map { ActivityBlueprint(blueprint.activityNames[it], blueprint.layoutNames[it], blueprint.packagePath, blueprint.packageName) } - .forEach({ generateClass(it) }) - - } - - private fun generateClass(blueprint: ActivityBlueprint) { - - // TODO add methods - // TODO move to java poet + fun generate(blueprint: ActivityBlueprint) { val classText = "package ${blueprint.packageName};\n" + "import android.app.Activity;\n" + @@ -64,11 +45,10 @@ class ActivityGenerator(var fileWriter: FileWriter) { " setContentView(R.layout." + blueprint.layout + ");\n" + " }\n" + "}\n" - println("${blueprint.where}/${blueprint.className}.java") - fileWriter.writeToFile(classText, "${blueprint.where}/${blueprint.className}.java") } + } diff --git a/src/main/kotlin/com/google/androidstudiopoet/generators/android_modules/AndroidModuleGenerator.kt b/src/main/kotlin/com/google/androidstudiopoet/generators/android_modules/AndroidModuleGenerator.kt index 0653aab9..17fa4cb3 100644 --- a/src/main/kotlin/com/google/androidstudiopoet/generators/android_modules/AndroidModuleGenerator.kt +++ b/src/main/kotlin/com/google/androidstudiopoet/generators/android_modules/AndroidModuleGenerator.kt @@ -40,7 +40,7 @@ class AndroidModuleGenerator(private val resourcesGenerator: ResourcesGenerator, buildGradleGenerator.generate(blueprint) blueprint.resourcesBlueprint?.let { resourcesGenerator.generate(it) } packagesGenerator.writePackages(blueprint.packagesBlueprint) - activityGenerator.generate(blueprint) + blueprint.activityBlueprints.forEach({ activityGenerator.generate(it) }) manifestGenerator.generate(blueprint) } @@ -52,5 +52,6 @@ class AndroidModuleGenerator(private val resourcesGenerator: ResourcesGenerator, fileWriter.mkdir(blueprint.mainPath) fileWriter.mkdir(blueprint.codePath) fileWriter.mkdir(blueprint.resDirPath) + fileWriter.mkdir(blueprint.packagePath) } } diff --git a/src/main/kotlin/com/google/androidstudiopoet/models/AndroidModuleBlueprint.kt b/src/main/kotlin/com/google/androidstudiopoet/models/AndroidModuleBlueprint.kt index b3210b1b..49192aff 100644 --- a/src/main/kotlin/com/google/androidstudiopoet/models/AndroidModuleBlueprint.kt +++ b/src/main/kotlin/com/google/androidstudiopoet/models/AndroidModuleBlueprint.kt @@ -23,18 +23,18 @@ import com.google.androidstudiopoet.utils.joinPath import com.google.androidstudiopoet.utils.joinPaths class AndroidModuleBlueprint(name: String, - val numOfActivities: Int, - private val resourcesConfig: ResourcesConfig?, - projectRoot: String, - val hasLaunchActivity: Boolean, - useKotlin: Boolean, - dependencies: List, - productFlavorConfigs: List?, - buildTypeConfigs: List?, - javaPackageCount: Int, javaClassCount: Int, javaMethodsPerClass: Int, - kotlinPackageCount: Int, kotlinClassCount: Int, kotlinMethodsPerClass: Int, - extraLines: List?, - generateTests : Boolean + val numOfActivities: Int, + private val resourcesConfig: ResourcesConfig?, + projectRoot: String, + val hasLaunchActivity: Boolean, + useKotlin: Boolean, + dependencies: List, + productFlavorConfigs: List?, + buildTypeConfigs: List?, + javaPackageCount: Int, javaClassCount: Int, javaMethodsPerClass: Int, + kotlinPackageCount: Int, kotlinClassCount: Int, kotlinMethodsPerClass: Int, + extraLines: List?, + generateTests: Boolean ) : ModuleBlueprint(name, projectRoot, useKotlin, dependencies, javaPackageCount, javaClassCount, javaMethodsPerClass, kotlinPackageCount, kotlinClassCount, kotlinMethodsPerClass, extraLines, generateTests) { @@ -58,7 +58,7 @@ class AndroidModuleBlueprint(name: String, } } - val layoutNames by lazy { + private val layoutNames by lazy { resourcesBlueprint?.layoutNames ?: listOf() } val activityNames = 0.until(numOfActivities).map { "Activity$it" } @@ -70,5 +70,8 @@ class AndroidModuleBlueprint(name: String, val productFlavors = productFlavorConfigs?.map { Flavor(it.name, it.dimension) }?.toSet() val flavorDimensions = productFlavors?.mapNotNull { it.dimension }?.toSet() - val buildTypes = buildTypeConfigs?.map {BuildType(it.name, it.body)}?.toSet() + val buildTypes = buildTypeConfigs?.map { BuildType(it.name, it.body) }?.toSet() + val activityBlueprints by lazy { + (0..numOfActivities).map { ActivityBlueprint(activityNames[it], layoutNames[it], packagePath, packageName) } + } } From f23afb34f9a6f73c0bb43b05030fff86862bb25a Mon Sep 17 00:00:00 2001 From: Nikita Kozlov Date: Thu, 8 Feb 2018 21:12:08 +0100 Subject: [PATCH 3/5] Added a class to refer to activity blueprint --- .../google/androidstudiopoet/models/ActivityBlueprint.kt | 3 ++- .../androidstudiopoet/models/AndroidModuleBlueprint.kt | 9 ++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/google/androidstudiopoet/models/ActivityBlueprint.kt b/src/main/kotlin/com/google/androidstudiopoet/models/ActivityBlueprint.kt index c6a80430..183701d1 100644 --- a/src/main/kotlin/com/google/androidstudiopoet/models/ActivityBlueprint.kt +++ b/src/main/kotlin/com/google/androidstudiopoet/models/ActivityBlueprint.kt @@ -1,3 +1,4 @@ package com.google.androidstudiopoet.models -data class ActivityBlueprint(val className: String, val layout: String, val where: String, val packageName: String) \ No newline at end of file +data class ActivityBlueprint(val className: String, val layout: String, val where: String, val packageName: String, + val classBlueprint: ClassBlueprint) \ No newline at end of file diff --git a/src/main/kotlin/com/google/androidstudiopoet/models/AndroidModuleBlueprint.kt b/src/main/kotlin/com/google/androidstudiopoet/models/AndroidModuleBlueprint.kt index 49192aff..ed62c81c 100644 --- a/src/main/kotlin/com/google/androidstudiopoet/models/AndroidModuleBlueprint.kt +++ b/src/main/kotlin/com/google/androidstudiopoet/models/AndroidModuleBlueprint.kt @@ -72,6 +72,13 @@ class AndroidModuleBlueprint(name: String, val buildTypes = buildTypeConfigs?.map { BuildType(it.name, it.body) }?.toSet() val activityBlueprints by lazy { - (0..numOfActivities).map { ActivityBlueprint(activityNames[it], layoutNames[it], packagePath, packageName) } + (0..numOfActivities).map { ActivityBlueprint(activityNames[it], layoutNames[it], packagePath, packageName, + classToReferFromActivity) } + } + + private val classToReferFromActivity: ClassBlueprint by lazy { + (packagesBlueprint.javaPackageBlueprints.asSequence() + packagesBlueprint.kotlinPackageBlueprints.asSequence()) + .flatMap { it.classBlueprints.asSequence() } + .first() } } From 00157208adf45ccc07a6d77a571aa50587c55d92 Mon Sep 17 00:00:00 2001 From: Nikita Kozlov Date: Thu, 8 Feb 2018 21:35:40 +0100 Subject: [PATCH 4/5] Added tests for created activity blueprints --- .../models/AndroidModuleBlueprint.kt | 4 +- .../models/ResourcesBlueprint.kt | 17 ++++++--- .../models/AndroidModuleBlueprintTest.kt | 37 ++++++++++++++++++- 3 files changed, 49 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/com/google/androidstudiopoet/models/AndroidModuleBlueprint.kt b/src/main/kotlin/com/google/androidstudiopoet/models/AndroidModuleBlueprint.kt index ed62c81c..3178ac94 100644 --- a/src/main/kotlin/com/google/androidstudiopoet/models/AndroidModuleBlueprint.kt +++ b/src/main/kotlin/com/google/androidstudiopoet/models/AndroidModuleBlueprint.kt @@ -61,7 +61,7 @@ class AndroidModuleBlueprint(name: String, private val layoutNames by lazy { resourcesBlueprint?.layoutNames ?: listOf() } - val activityNames = 0.until(numOfActivities).map { "Activity$it" } + val activityNames = (0 until numOfActivities).map { "Activity$it" } val resourcesToReferFromOutside by lazy { resourcesBlueprint?.resourcesToReferFromOutside ?: ResourcesToRefer(listOf(), listOf(), listOf()) @@ -72,7 +72,7 @@ class AndroidModuleBlueprint(name: String, val buildTypes = buildTypeConfigs?.map { BuildType(it.name, it.body) }?.toSet() val activityBlueprints by lazy { - (0..numOfActivities).map { ActivityBlueprint(activityNames[it], layoutNames[it], packagePath, packageName, + (0 until numOfActivities).map { ActivityBlueprint(activityNames[it], layoutNames[it], packagePath, packageName, classToReferFromActivity) } } diff --git a/src/main/kotlin/com/google/androidstudiopoet/models/ResourcesBlueprint.kt b/src/main/kotlin/com/google/androidstudiopoet/models/ResourcesBlueprint.kt index 665b7ee0..566d1749 100644 --- a/src/main/kotlin/com/google/androidstudiopoet/models/ResourcesBlueprint.kt +++ b/src/main/kotlin/com/google/androidstudiopoet/models/ResourcesBlueprint.kt @@ -25,23 +25,30 @@ data class ResourcesBlueprint(private val moduleName: String, private val imageCount: Int, private val layoutCount: Int, private val resourcesToReferWithin: ResourcesToRefer) : Blueprint { - val stringNames = (0..stringCount).map { "${moduleName}string$it" } + val stringNames = (0 until stringCount).map { "${moduleName}string$it" } val imageNames = (0 until imageCount).map { "${moduleName.toLowerCase()}image$it" } val layoutNames = (0 until layoutCount).map { "${moduleName.toLowerCase()}activity_main$it" } val layoutsDir = resDirPath.joinPath("layout") - private val stringsPerLayout = (stringNames + resourcesToReferWithin.strings).splitPerLayout(layoutCount) - private val imagesPerLayout = (imageNames + resourcesToReferWithin.images).splitPerLayout(layoutCount) + private val stringsPerLayout by lazy { + (stringNames + resourcesToReferWithin.strings).splitPerLayout(layoutCount) + } + private val imagesPerLayout by lazy { + (imageNames + resourcesToReferWithin.images).splitPerLayout(layoutCount) + } val layoutBlueprints = layoutNames.mapIndexed { index, layoutName -> LayoutBlueprint(layoutsDir.joinPath(layoutName) + ".xml", stringsPerLayout.getOrElse(index, { listOf() }), imagesPerLayout.getOrElse(index, { listOf() }), - if (index == 0) resourcesToReferWithin.layouts else listOf()) } + if (index == 0) resourcesToReferWithin.layouts else listOf()) + } - var resourcesToReferFromOutside = ResourcesToRefer(listOf(stringNames.last()), listOf(imageNames.last()), listOf(layoutNames.last())) + val resourcesToReferFromOutside by lazy { + ResourcesToRefer(listOf(stringNames.last()), listOf(imageNames.last()), listOf(layoutNames.last())) + } } fun List.splitPerLayout(limit: Int) = diff --git a/src/test/kotlin/com/google/androidstudiopoet/models/AndroidModuleBlueprintTest.kt b/src/test/kotlin/com/google/androidstudiopoet/models/AndroidModuleBlueprintTest.kt index e1715de6..d97e31e0 100644 --- a/src/test/kotlin/com/google/androidstudiopoet/models/AndroidModuleBlueprintTest.kt +++ b/src/test/kotlin/com/google/androidstudiopoet/models/AndroidModuleBlueprintTest.kt @@ -4,12 +4,13 @@ import com.google.androidstudiopoet.input.BuildTypeConfig import com.google.androidstudiopoet.input.FlavorConfig import com.google.androidstudiopoet.input.ResourcesConfig import com.google.androidstudiopoet.testutils.assertEquals +import com.google.androidstudiopoet.testutils.assertOn import com.google.androidstudiopoet.testutils.mock +import com.nhaarman.mockito_kotlin.whenever import org.junit.Test class AndroidModuleBlueprintTest { private val resourcesConfig0: ResourcesConfig = mock() - private val dependency: ModuleDependency = mock() @Test fun `blueprint create proper activity names`() { @@ -39,6 +40,38 @@ class AndroidModuleBlueprintTest { )) } + @Test + fun `blueprint creates activity blueprint with java class when java code exists`() { + whenever(resourcesConfig0.layoutCount).thenReturn(1) + + val androidModuleBlueprint = getAndroidModuleBlueprint() + + assertOn(androidModuleBlueprint) { + activityBlueprints.assertEquals(listOf(ActivityBlueprint( + "Activity0", + androidModuleBlueprint.resourcesBlueprint!!.layoutNames[0], + androidModuleBlueprint.packagePath, + androidModuleBlueprint.packageName, + androidModuleBlueprint.packagesBlueprint.javaPackageBlueprints[0].classBlueprints[0]))) + } + } + + @Test + fun `blueprint creates activity blueprint with koltin class when java code doesn't exist`() { + whenever(resourcesConfig0.layoutCount).thenReturn(1) + + val androidModuleBlueprint = getAndroidModuleBlueprint( + javaClassCount = 0, + javaMethodsPerClass = 0, + javaPackageCount = 0 + ) + + assertOn(androidModuleBlueprint) { + activityBlueprints[0].classBlueprint.assertEquals( + androidModuleBlueprint.packagesBlueprint.kotlinPackageBlueprints[0].classBlueprints[0]) + } + } + private fun getAndroidModuleBlueprint( name: String = "androidAppModule1", @@ -47,7 +80,7 @@ class AndroidModuleBlueprintTest { projectRoot: String = "root", hasLaunchActivity: Boolean = true, useKotlin: Boolean = false, - dependencies: List = listOf(dependency), + dependencies: List = listOf(), productFlavorConfigs: List? = null, buildTypeConfigs: List? = null, javaPackageCount: Int = 1, From 1580d344f99692cdec7af6ee6c11f9ee73fa74b9 Mon Sep 17 00:00:00 2001 From: Nikita Kozlov Date: Thu, 8 Feb 2018 22:55:44 +0100 Subject: [PATCH 5/5] Refactored `ActivityGenerator` to use JavaPoet and to call method Added a method call to the class from blueprint --- .../android_modules/ActivityGenerator.kt | 63 ++++++++++++------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/src/main/kotlin/com/google/androidstudiopoet/generators/android_modules/ActivityGenerator.kt b/src/main/kotlin/com/google/androidstudiopoet/generators/android_modules/ActivityGenerator.kt index 9ee1d38d..9d999985 100644 --- a/src/main/kotlin/com/google/androidstudiopoet/generators/android_modules/ActivityGenerator.kt +++ b/src/main/kotlin/com/google/androidstudiopoet/generators/android_modules/ActivityGenerator.kt @@ -17,37 +17,52 @@ limitations under the License. package com.google.androidstudiopoet.generators.android_modules import com.google.androidstudiopoet.models.ActivityBlueprint +import com.google.androidstudiopoet.models.ClassBlueprint import com.google.androidstudiopoet.writers.FileWriter +import com.squareup.javapoet.* +import javax.lang.model.element.Modifier + class ActivityGenerator(var fileWriter: FileWriter) { fun generate(blueprint: ActivityBlueprint) { - val classText = - "package ${blueprint.packageName};\n" + - "import android.app.Activity;\n" + - "import android.os.Bundle;\n" + - "import ${blueprint.packageName}.R;\n" + - "\n" + - "\n" + - "public class " + blueprint.className + " extends Activity {\n" + - " public ${blueprint.className}() {\n" + - " }\n" + - "\n" + - " /**\n" + - " * Called with the activity is first created.\n" + - " */\n" + - " @Override\n" + - " public void onCreate(Bundle savedInstanceState) {\n" + - " super.onCreate(savedInstanceState);\n" + - "\n" + - " // Set the layout for this activity. You can find it\n" + - " // in res/layout/hello_activity.xml\n" + - " setContentView(R.layout." + blueprint.layout + ");\n" + - " }\n" + - "}\n" + + val classBlueprint = blueprint.classBlueprint + val onCreateMethodStatements = getMethodStatementFromClassToCall(classBlueprint)?.let { listOf(it) } ?: listOf() + + val onCreateMethod = getOnCreateMethod(blueprint.layout, onCreateMethodStatements) + + val activityClass = getClazzSpec(blueprint.className) + .addMethod(onCreateMethod) + .build() + + val javaFile = JavaFile.builder(blueprint.packageName, activityClass).build() + println("${blueprint.where}/${blueprint.className}.java") - fileWriter.writeToFile(classText, "${blueprint.where}/${blueprint.className}.java") + fileWriter.writeToFile(javaFile.toString(), "${blueprint.where}/${blueprint.className}.java") + + } + + private fun getMethodStatementFromClassToCall(classBlueprint: ClassBlueprint) = + classBlueprint.getMethodToCallFromOutside()?.let { "new ${it.className}().${it.methodName}()" } + + private fun getClazzSpec(activityClassName: String) = + TypeSpec.classBuilder(activityClassName) + .superclass(TypeVariableName.get("android.app.Activity")) + .addModifiers(Modifier.PUBLIC) + + private fun getOnCreateMethod(layout: String, statements: List): MethodSpec { + + val builder = MethodSpec.methodBuilder("onCreate") + .addAnnotation(Override::class.java) + .addModifiers(Modifier.PUBLIC) + .returns(Void.TYPE) + .addParameter(TypeVariableName.get("android.os.Bundle"), "savedInstanceState") + .addStatement("super.onCreate(savedInstanceState)") + .addStatement("setContentView(R.layout.$layout)") + statements.forEach { builder.addStatement(it) } + return builder.build(); } }