diff --git a/alpaka/plugin/src/main/java/ch/ubique/gradle/alpaka/AlpakaPlugin.kt b/alpaka/plugin/src/main/java/ch/ubique/gradle/alpaka/AlpakaPlugin.kt index df2f34d..1b011c7 100644 --- a/alpaka/plugin/src/main/java/ch/ubique/gradle/alpaka/AlpakaPlugin.kt +++ b/alpaka/plugin/src/main/java/ch/ubique/gradle/alpaka/AlpakaPlugin.kt @@ -3,9 +3,11 @@ package ch.ubique.gradle.alpaka import ch.ubique.gradle.alpaka.config.AlpakaPluginConfig +import ch.ubique.gradle.alpaka.extensions.applicationvariant.launcherIconLabel +import ch.ubique.gradle.alpaka.extensions.capitalize import ch.ubique.gradle.alpaka.extensions.getMergedManifestFile +import ch.ubique.gradle.alpaka.extensions.productflavor.launcherIconLabel as flavorLauncherIconLabel import ch.ubique.gradle.alpaka.extensions.productflavor.alpakaUploadKey -import ch.ubique.gradle.alpaka.extensions.productflavor.launcherIconLabel import ch.ubique.gradle.alpaka.model.UploadRequest import ch.ubique.gradle.alpaka.task.IconTask import ch.ubique.gradle.alpaka.task.InjectMetadataIntoManifestTask @@ -17,12 +19,16 @@ import com.android.build.gradle.internal.tasks.factory.dependsOn import org.gradle.api.GradleException import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.file.Directory +import org.gradle.api.file.DirectoryProperty import org.gradle.api.plugins.ExtensionAware +import org.gradle.api.provider.Provider import org.jetbrains.kotlin.gradle.plugin.extraProperties import java.io.File abstract class AlpakaPlugin : Plugin { + @Suppress("DefaultLocale") override fun apply(project: Project) { val pluginExtension = project.extensions.create("alpaka", AlpakaPluginConfig::class.java, project) val androidExtension = getAndroidExtension(project) @@ -98,32 +104,28 @@ abstract class AlpakaPlugin : Plugin { } } + androidExtension.productFlavors.configureEach { flavor -> + // Add the property 'launcherIconLabel' to each flavor and set the default value to its name + val flavorName = flavor.name + flavor.launcherIconLabel = if (flavorName == "prod") null else flavorName + } + // Hook iconTask into android build process project.afterEvaluate { - val buildDir = project.getBuildDirectory() val labelAppIcons = pluginExtension.labelAppIcons.getOrElse(true) - androidExtension.buildTypes.configureEach { buildType -> - androidExtension.productFlavors.configureEach { flavor -> - val flavorName = flavor.name - - // Add the property 'launcherIconLabel' to each flavor and set the default value to its name - flavor.launcherIconLabel = if (flavorName == "prod") null else flavorName - - if (labelAppIcons) { - // make sure generated sources are used by build process - // Add generated icon path to res-SourceSet. This must be here otherwise it is too late! - val sourceSet = androidExtension.sourceSets.maybeCreate("$flavorName${buildType.name.capitalize()}") - sourceSet.res.srcDir("$buildDir/generated/res/launcher-icon/${flavorName}/") - } - } - } - androidExtension.applicationVariants.configureEach { variant -> val variantName = variant.name val flavor = variant.flavorName val buildType = variant.buildType.name - val labelValue = launcherIconLabel(variant, androidExtension) + val labelValue = getLauncherIconLabel(variant, androidExtension) + + if (labelAppIcons) { + // make sure generated sources are used by build process + // Add generated icon path to res-SourceSet. This must be here otherwise it is too late! + val sourceSet = androidExtension.sourceSets.maybeCreate(variantName) + sourceSet.res.srcDir(getGeneratedIconDir(project.layout.buildDirectory, flavor, buildType)) + } val iconTask = project.tasks.register( "generateAppIcon${variantName.capitalize()}", @@ -133,9 +135,9 @@ abstract class AlpakaPlugin : Plugin { iconTask.flavor = flavor iconTask.buildType = buildType iconTask.labelValue = if (labelAppIcons) labelValue else null - iconTask.targetWebIconPath = getWebIconPath(buildDir, flavor) - - iconTask.outputs.upToDateWhen { false } //always run the task + iconTask.targetWebIconFile = getGeneratedWebIconFile(project.layout.buildDirectory, flavor, buildType) + iconTask.generatedIconDir = getGeneratedIconDir(project.layout.buildDirectory, flavor, buildType) + iconTask.outputs.upToDateWhen { false } // always run the task } project.tasks.named("map${variantName.capitalize()}SourceSetPaths") { it.dependsOn(iconTask) } @@ -148,13 +150,11 @@ abstract class AlpakaPlugin : Plugin { // Hook uploadTask into android build process project.afterEvaluate { - val buildDir = project.getBuildDirectory() - androidExtension.applicationVariants.configureEach { variant -> val variantName = variant.name val flavor = variant.flavorName val buildType = variant.buildType.name - val uploadKey = uploadKey(variant, androidExtension) + val uploadKey = getUploadKey(variant, androidExtension) if (buildType != "release") return@configureEach val packageName = variant.applicationId @@ -162,32 +162,31 @@ abstract class AlpakaPlugin : Plugin { val targetSdk = requireNotNull(androidExtension.defaultConfig.targetSdk) val versionName = requireNotNull(androidExtension.defaultConfig.versionName) - val uploadRequest = variant.outputs.first().let { - UploadRequest( - apk = it.outputFile, - appIcon = File(getWebIconPath(buildDir, flavor)), - appName = "", // Will be set from manifest inside the task - packageName = packageName, - flavor = flavor, - branch = buildBranch, - minSdk = minSdk, - targetSdk = targetSdk, - usesFeature = emptyList(), // Will be set from manifest inside the task - buildId = buildId, - buildNumber = buildNumber, - buildTime = buildTimestamp, - buildBatch = buildBatch, - changelog = "", // Will be set inside the task - signature = "", // Will be set inside the task - version = versionName, - ) - } - project.tasks.register( "uploadToAlpaka${variantName.capitalize()}", UploadToAlpakaBackendTask::class.java ) { uploadTask -> - uploadTask.dependsOn("assemble${variantName.capitalize()}") + val uploadRequest = variant.outputs.first().let { + UploadRequest( + apk = it.outputFile, + appIcon = getGeneratedWebIconFile(project.layout.buildDirectory, flavor, buildType), + appName = "", // Will be set from manifest inside the task + packageName = packageName, + flavor = flavor, + branch = buildBranch, + minSdk = minSdk, + targetSdk = targetSdk, + usesFeature = emptyList(), // Will be set from manifest inside the task + buildId = buildId, + buildNumber = buildNumber, + buildTime = buildTimestamp, + buildBatch = buildBatch, + changelog = "", // Will be set inside the task + signature = "", // Will be set inside the task + version = versionName, + ) + } + uploadTask.variant = variant uploadTask.flavor = flavor uploadTask.buildType = buildType @@ -195,6 +194,8 @@ abstract class AlpakaPlugin : Plugin { uploadTask.proxy = pluginExtension.proxy.orNull uploadTask.commitCount = pluginExtension.changelogCommitCount.orNull uploadTask.uploadRequest = uploadRequest + + uploadTask.dependsOn("assemble${variantName.capitalize()}") } } } @@ -206,23 +207,22 @@ abstract class AlpakaPlugin : Plugin { return ext } - private fun Project.getBuildDirectory(): File { - return project.layout.buildDirectory.asFile.get() + private fun getGeneratedWebIconFile(buildDir: DirectoryProperty, flavor: String, buildType: String): Provider { + return getGeneratedIconDir(buildDir, flavor, buildType).map { it.file("web-icon.png").asFile } } - private fun getWebIconPath(buildDir: File, flavor: String): String { - return "$buildDir/generated/res/launcher-icon/$flavor/web-icon.png" + private fun getGeneratedIconDir(buildDir: DirectoryProperty, flavor: String, buildType: String): Provider { + return buildDir.dir("generated/res/launcher-icon/$flavor/$buildType/res") } - private fun uploadKey(applicationVariant: ApplicationVariant, androidExtension: AppExtension): String? { + private fun getUploadKey(applicationVariant: ApplicationVariant, androidExtension: AppExtension): String? { val productFlavor = applicationVariant.productFlavors.firstOrNull() return productFlavor?.alpakaUploadKey ?: androidExtension.defaultConfig.alpakaUploadKey - } - private fun launcherIconLabel(applicationVariant: ApplicationVariant, androidExtension: AppExtension): String? { + private fun getLauncherIconLabel(applicationVariant: ApplicationVariant, androidExtension: AppExtension): String? { val productFlavor = applicationVariant.productFlavors.firstOrNull() - return productFlavor?.launcherIconLabel ?: androidExtension.defaultConfig.launcherIconLabel + return productFlavor?.flavorLauncherIconLabel ?: androidExtension.defaultConfig.launcherIconLabel } } diff --git a/alpaka/plugin/src/main/java/ch/ubique/gradle/alpaka/extensions/applicationvariant/ApplicationVariantDimensionExtensions.kt b/alpaka/plugin/src/main/java/ch/ubique/gradle/alpaka/extensions/applicationvariant/ApplicationVariantDimensionExtensions.kt index e1ab1ee..5cffad1 100644 --- a/alpaka/plugin/src/main/java/ch/ubique/gradle/alpaka/extensions/applicationvariant/ApplicationVariantDimensionExtensions.kt +++ b/alpaka/plugin/src/main/java/ch/ubique/gradle/alpaka/extensions/applicationvariant/ApplicationVariantDimensionExtensions.kt @@ -9,10 +9,15 @@ private fun ApplicationVariantDimension.setProperty(key: String, value: T) { extensionAware.extraProperties[key] = value } +private fun ApplicationVariantDimension.getProperty(key: String): T { + val extensionAware = this as ExtensionAware + return extensionAware.extraProperties[key] as T +} + var ApplicationVariantDimension.launcherIconLabel: String? - get() = error("only setter") + get() = getProperty("launcherIconLabel") set(value) = setProperty("launcherIconLabel", value) var ApplicationVariantDimension.alpakaUploadKey: String? - get() = error("only setter") - set(value) = setProperty("alpakaUploadKey", value) \ No newline at end of file + get() = getProperty("alpakaUploadKey") + set(value) = setProperty("alpakaUploadKey", value) diff --git a/alpaka/plugin/src/main/java/ch/ubique/gradle/alpaka/model/UploadRequest.kt b/alpaka/plugin/src/main/java/ch/ubique/gradle/alpaka/model/UploadRequest.kt index 8aa08f6..e383a3a 100644 --- a/alpaka/plugin/src/main/java/ch/ubique/gradle/alpaka/model/UploadRequest.kt +++ b/alpaka/plugin/src/main/java/ch/ubique/gradle/alpaka/model/UploadRequest.kt @@ -1,11 +1,12 @@ package ch.ubique.gradle.alpaka.model import ch.ubique.gradle.alpaka.network.dto.UploadDataDto +import org.gradle.api.provider.Provider import java.io.File data class UploadRequest( val apk: File, - val appIcon: File, + val appIcon: Provider, val appName: String, val packageName: String, val flavor: String, diff --git a/alpaka/plugin/src/main/java/ch/ubique/gradle/alpaka/network/BackendRepository.kt b/alpaka/plugin/src/main/java/ch/ubique/gradle/alpaka/network/BackendRepository.kt index 98607d5..b358546 100644 --- a/alpaka/plugin/src/main/java/ch/ubique/gradle/alpaka/network/BackendRepository.kt +++ b/alpaka/plugin/src/main/java/ch/ubique/gradle/alpaka/network/BackendRepository.kt @@ -52,7 +52,7 @@ class BackendRepository { val response = service.appsUpload( apk = uploadRequest.apk.toPartMap("apk", "application/octet-stream"), - icon = uploadRequest.appIcon.toPartMap("icon", "image/png"), + icon = uploadRequest.appIcon.get().toPartMap("icon", "image/png"), data = data ).execute() diff --git a/alpaka/plugin/src/main/java/ch/ubique/gradle/alpaka/task/IconTask.kt b/alpaka/plugin/src/main/java/ch/ubique/gradle/alpaka/task/IconTask.kt index c1ff229..5e69e9a 100644 --- a/alpaka/plugin/src/main/java/ch/ubique/gradle/alpaka/task/IconTask.kt +++ b/alpaka/plugin/src/main/java/ch/ubique/gradle/alpaka/task/IconTask.kt @@ -6,6 +6,8 @@ import ch.ubique.gradle.alpaka.extensions.olderThan import ch.ubique.gradle.alpaka.utils.IconUtils import org.gradle.api.DefaultTask import org.gradle.api.GradleException +import org.gradle.api.file.Directory +import org.gradle.api.provider.Provider import org.gradle.api.tasks.* import java.io.File @@ -31,30 +33,27 @@ abstract class IconTask : DefaultTask() { abstract var labelValue: String? @get:Input - abstract var targetWebIconPath: String + abstract var targetWebIconFile: Provider @get:OutputDirectory - abstract var generatedIconDir: File - - init { - val buildDir = project.layout.buildDirectory.asFile.get() - generatedIconDir = File("$buildDir/generated/res/launcher-icon/") - } + abstract var generatedIconDir: Provider @TaskAction fun iconAction() { val moduleDir = File(project.rootDir, project.name) - val targetWebIcon = File(targetWebIconPath).also { + val targetWebIcon = targetWebIconFile.get().also { it.parentFile.mkdirs() it.createNewFile() } - val gradleLastModified = listOf( File(moduleDir, "build.gradle").lastModified(), File(moduleDir, "build.gradle.kts").lastModified(), File(project.rootDir, "build.gradle").lastModified(), - File(project.rootDir, "build.gradle.kts").lastModified() + File(project.rootDir, "build.gradle.kts").lastModified(), + File(project.rootDir, "settings.gradle").lastModified(), + File(project.rootDir, "settings.gradle.kts").lastModified(), + File(project.rootDir, "gradle/libs.versions.toml").lastModified(), ).max() val manifestFile = project.getMergedManifestFile(variantName) @@ -85,7 +84,7 @@ abstract class IconTask : DefaultTask() { allIcons.forEach iconsForEach@{ original -> val resTypeName = original.parentFile.name val originalBaseName = original.name.substringBefore(".") - val targetDir = File("${generatedIconDir}/$flavor/$resTypeName") + val targetDir = File(generatedIconDir.get().asFile, resTypeName) val modified = targetDir.listFiles { file -> file.name.matches(Regex("$originalBaseName\\.[^.]+")) diff --git a/alpaka/plugin/src/main/java/ch/ubique/gradle/alpaka/utils/StringUtils.kt b/alpaka/plugin/src/main/java/ch/ubique/gradle/alpaka/utils/StringUtils.kt index a6e4a73..496e96b 100644 --- a/alpaka/plugin/src/main/java/ch/ubique/gradle/alpaka/utils/StringUtils.kt +++ b/alpaka/plugin/src/main/java/ch/ubique/gradle/alpaka/utils/StringUtils.kt @@ -36,7 +36,7 @@ object StringUtils { val xmlParser = XmlParser(file) xmlParser.findTagValue("string", mapOf("name" to labelName)) .takeIf { it.isNullOrEmpty().not() } - ?.trim('"') // Strip PoEditor double quotes + ?.trim('"') // Strip double quotes } } diff --git a/examplegroovy/build.gradle b/examplegroovy/build.gradle index 934cd69..4e7b0d8 100644 --- a/examplegroovy/build.gradle +++ b/examplegroovy/build.gradle @@ -19,16 +19,32 @@ android { } flavorDimensions = ["default"] + productFlavors { dev { dimension "default" applicationIdSuffix ".dev" - alpakaUploadKey = "dev flavor upload key" - launcherIconLabel = "develop" + if (launcherIconLabel != "dev") { + throw new GradleException("Expected launcherIconLabel 'dev' but was $launcherIconLabel") + } + } + tescht { + dimension "default" + applicationIdSuffix ".test" + alpakaUploadKey = "test flavor upload key" + launcherIconLabel = "test" + + if (launcherIconLabel != "test") { + throw new GradleException("Expected launcherIconLabel 'test' but was $launcherIconLabel") + } } prod { dimension "default" + + if (launcherIconLabel != null) { + throw new GradleException("Expected launcherIconLabel null but was $launcherIconLabel") + } } } @@ -36,6 +52,7 @@ android { sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } + kotlinOptions { jvmTarget = JavaVersion.VERSION_17.toString() } diff --git a/examplekts/build.gradle.kts b/examplekts/build.gradle.kts index 05020e5..4115643 100644 --- a/examplekts/build.gradle.kts +++ b/examplekts/build.gradle.kts @@ -22,16 +22,32 @@ android { } flavorDimensions += "default" + productFlavors { create("dev") { dimension = "default" applicationIdSuffix = ".dev" - alpakaUploadKey = "dev flavor upload key" - launcherIconLabel = "develop" + if (launcherIconLabel != "dev") { + error("Expected launcherIconLabel 'dev' but was $launcherIconLabel") + } + } + create("tescht") { + dimension = "default" + applicationIdSuffix = ".test" + alpakaUploadKey = "test flavor upload key" + launcherIconLabel = "test" + + if (launcherIconLabel != "test") { + error("Expected launcherIconLabel 'test' but was $launcherIconLabel") + } } create("prod") { dimension = "default" + + if (launcherIconLabel != null) { + error("Expected launcherIconLabel null but was $launcherIconLabel") + } } } @@ -39,6 +55,7 @@ android { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } + kotlinOptions { jvmTarget = JavaVersion.VERSION_17.toString() }