diff --git a/README.md b/README.md index 24fc1ee..227c303 100644 --- a/README.md +++ b/README.md @@ -58,8 +58,8 @@ To apply the Gradle build plugin, you have to add it as a project dependency in ... } dependencies { - classpath 'com.android.tools.build:gradle:8.2.0' // Android build plugin - classpath 'ch.ubique.gradle:ubdiag-android:8.2.0' // UbDiag build plugin + classpath 'com.android.tools.build:gradle:8.3.0' // Android build plugin + classpath 'ch.ubique.gradle:ubdiag-android:8.3.0' // UbDiag build plugin } } diff --git a/build.gradle b/build.gradle index ec3db52..270760d 100644 --- a/build.gradle +++ b/build.gradle @@ -20,12 +20,12 @@ repositories { dependencies { implementation localGroovy() implementation gradleApi() - implementation 'com.android.tools.build:gradle:8.2.0' + implementation 'com.android.tools.build:gradle:8.3.2' } // MAVEN PUBLISHING mavenPublishing { - coordinates("ch.ubique.gradle", "ubdiag-android", "8.2.1") + coordinates("ch.ubique.gradle", "ubdiag-android", "8.3.0") publishToMavenCentral(SonatypeHost.S01, true) signAllPublications() } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 811a477..af04217 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Thu Apr 25 09:45:26 CEST 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip \ No newline at end of file diff --git a/src/main/groovy/ch/ubique/gradle/ubdiag/BuildPlugin.groovy b/src/main/groovy/ch/ubique/gradle/ubdiag/BuildPlugin.groovy index a059c04..3a72c42 100644 --- a/src/main/groovy/ch/ubique/gradle/ubdiag/BuildPlugin.groovy +++ b/src/main/groovy/ch/ubique/gradle/ubdiag/BuildPlugin.groovy @@ -9,6 +9,8 @@ import com.android.builder.model.ProductFlavor import org.gradle.api.GradleException import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.tasks.TaskProvider class BuildPlugin implements Plugin { @@ -59,13 +61,15 @@ class BuildPlugin implements Plugin { project.afterEvaluate { // setup manifest manipulation task - android.applicationVariants.configureEach { ApplicationVariant variant -> + android.applicationVariants.configureEach { variant -> variant.outputs.each { output -> - output.processManifestProvider.get().doLast { - buildFlavor = variant.flavorName - File manifestFile = ManifestUtils.getMergedManifestFile(project, variant) - if (manifestFile.exists()) { - manipulateManifestFile(manifestFile) + output.processManifestProvider.configure { + it.doLast { + buildFlavor = variant.flavorName + File manifestFile = ManifestUtils.getMergedManifestFile(project, variant) + if (manifestFile.exists()) { + manipulateManifestFile(manifestFile) + } } } } @@ -74,15 +78,14 @@ class BuildPlugin implements Plugin { // launcher icon manipulation task android.applicationVariants.configureEach { variant -> variant.outputs.each { output -> - def overlayIconTask = IconOverlayTask.create(project, android, variant, targetWebIcon) - - /* hook overlayIconTask into android build chain */ - def tasks = project.getTasks() - def targetName = variant.name.capitalize() - def targetTask = tasks.findByName("generate${targetName}Resources") - - overlayIconTask.dependsOn output.processManifestProvider.get() - targetTask.dependsOn(overlayIconTask) + TaskProvider overlayIconTask = IconOverlayTask.create(project, android, variant, targetWebIcon) + overlayIconTask.configure { + it.dependsOn(output.processManifestProvider) + } + def variantName = variant.name.capitalize() + project.tasks.named("generate${variantName}Resources") { + it.dependsOn(overlayIconTask) + } } } } diff --git a/src/main/groovy/ch/ubique/gradle/ubdiag/IconOverlayTask.groovy b/src/main/groovy/ch/ubique/gradle/ubdiag/IconOverlayTask.groovy index 8ac0d9c..a59c16c 100644 --- a/src/main/groovy/ch/ubique/gradle/ubdiag/IconOverlayTask.groovy +++ b/src/main/groovy/ch/ubique/gradle/ubdiag/IconOverlayTask.groovy @@ -8,110 +8,112 @@ import com.android.builder.model.ProductFlavor import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.artifacts.ProjectDependency +import org.gradle.api.tasks.TaskProvider + import java.nio.file.Files import java.nio.file.StandardCopyOption class IconOverlayTask { - static Task create(Project project, AppExtension android, ApplicationVariant variant, File targetWebIcon) { - + static TaskProvider create(Project project, AppExtension android, ApplicationVariant variant, File targetWebIcon) { String taskName = "overlayIcon${variant.name.capitalize()}" - - return project.task(taskName).doFirst { - File moduleDir = new File(project.rootDir, project.name) - - long gradleLastModified = Math.max( - new File(moduleDir, "build.gradle").lastModified(), - new File(project.rootDir, "build.gradle").lastModified() - ) - - File generatedResDir = new File("${project.buildDir}/generated/res/launcher-icon/") - - // get banner label - ProductFlavor flavor = variant.productFlavors[0] - Boolean defaultLabelEnabled = android.defaultConfig.launcherIconLabelEnabled - Boolean flavorLabelEnabled = flavor.launcherIconLabelEnabled - String bannerLabel - if (flavorLabelEnabled - || flavorLabelEnabled == null && defaultLabelEnabled - || flavorLabelEnabled == null && defaultLabelEnabled == null && !flavor.name.startsWith("prod") - ) { - if (flavor.launcherIconLabel != null) { - bannerLabel = flavor.launcherIconLabel + return project.tasks.register(taskName) { task -> + task.doFirst { + File moduleDir = new File(project.rootDir, project.name) + + long gradleLastModified = Math.max( + new File(moduleDir, "build.gradle").lastModified(), + new File(project.rootDir, "build.gradle").lastModified() + ) + + File generatedResDir = new File("${project.buildDir}/generated/res/launcher-icon/") + + // get banner label + ProductFlavor flavor = variant.productFlavors[0] + Boolean defaultLabelEnabled = android.defaultConfig.launcherIconLabelEnabled + Boolean flavorLabelEnabled = flavor.launcherIconLabelEnabled + String bannerLabel + if (flavorLabelEnabled + || flavorLabelEnabled == null && defaultLabelEnabled + || flavorLabelEnabled == null && defaultLabelEnabled == null && !flavor.name.startsWith("prod") + ) { + if (flavor.launcherIconLabel != null) { + bannerLabel = flavor.launcherIconLabel + } else { + bannerLabel = variant.flavorName + } } else { - bannerLabel = variant.flavorName + bannerLabel = null } - } else { - bannerLabel = null - } - - File manifestFile = ManifestUtils.getMergedManifestFile(project, variant) - - List androidModules = project.configurations*.dependencies - *.findAll { it instanceof ProjectDependency } - .flatten() - .collect { it.dependencyProject } - .unique() - .collect { it.extensions.findByType(BaseExtension) } - .findAll { it != null } - - List resDirs = androidModules - .collect { [it.sourceSets.findByName(variant.flavorName), it.sourceSets.findByName("main")] } - .flatten() - .findAll { it != null } - .collect { AndroidSourceSet ass -> ass.res.srcDirs } - .flatten() - .findAll { File file -> !file.path.contains("generated") } - - println("$taskName: resource directories: " + resDirs) - List allIcons = IconUtils.findIcons(resDirs, manifestFile) - - if (targetWebIcon != null) { - targetWebIcon.delete() - - // search for web icon source - File webIconSource = ((new File(moduleDir, "src/${variant.flavorName}").listFiles() ?: new File[0]) + - (new File(moduleDir, "src/main").listFiles() ?: new File[0]) + - (moduleDir.listFiles() ?: new File[0])).find { - it.name.matches(".*(web|playstore|512)\\.(png|webp)") + File manifestFile = ManifestUtils.getMergedManifestFile(project, variant) + + List androidModules = project.configurations*.dependencies + *.findAll { it instanceof ProjectDependency } + .flatten() + .collect { it.dependencyProject } + .unique() + .collect { it.extensions.findByType(BaseExtension) } + .findAll { it != null } + + List resDirs = androidModules + .collect { [it.sourceSets.findByName(variant.flavorName), it.sourceSets.findByName("main")] } + .flatten() + .findAll { it != null } + .collect { AndroidSourceSet ass -> ass.res.srcDirs } + .flatten() + .findAll { File file -> !file.path.contains("generated") } + + println("$taskName: resource directories: " + resDirs) + + List allIcons = IconUtils.findIcons(resDirs, manifestFile) + + if (targetWebIcon != null) { + targetWebIcon.delete() + + // search for web icon source + File webIconSource = ((new File(moduleDir, "src/${variant.flavorName}").listFiles() ?: new File[0]) + + (new File(moduleDir, "src/main").listFiles() ?: new File[0]) + + (moduleDir.listFiles() ?: new File[0])).find { + it.name.matches(".*(web|playstore|512)\\.(png|webp)") + } + + if (webIconSource == null) { + // set fallbackWebIcon + webIconSource = IconUtils.findLargestIcon(allIcons) + } + + if (webIconSource == null) { + println("$taskName: web icon source not found") + } else if (bannerLabel == null || bannerLabel.empty) { + // no label so we only copy the sourceIcon and use this + println("$taskName: web icon: $webIconSource") + Files.copy(webIconSource.toPath(), targetWebIcon.toPath()) + } else { + println("$taskName: web icon: $webIconSource") + IconUtils.drawLabel(webIconSource, targetWebIcon, bannerLabel, false) + } } - if (webIconSource == null) { - // set fallbackWebIcon - webIconSource = IconUtils.findLargestIcon(allIcons) + if (bannerLabel == null || bannerLabel.empty) { + // no label + println("$taskName: skipped icon labelling") + return } - if (webIconSource == null) { - println("$taskName: web icon source not found") - } else if (bannerLabel == null || bannerLabel.empty) { - // no label so we only copy the sourceIcon and use this - println("$taskName: web icon: $webIconSource") - Files.copy(webIconSource.toPath(), targetWebIcon.toPath()) - } else { - println("$taskName: web icon: $webIconSource") - IconUtils.drawLabel(webIconSource, targetWebIcon, bannerLabel, false) + allIcons.each { File original -> + String resTypeName = original.parentFile.name + String originalBaseName = original.name.takeBefore(".") + File targetDir = new File("${generatedResDir.toString()}/${variant.flavorName}/$resTypeName") + File modified = targetDir.listFiles({ File file -> file.name.matches("${originalBaseName}\\.[^.]+") } as FileFilter)?.find() as File + if (modified != null && original.lastModified() <= modified.lastModified() && gradleLastModified <= modified.lastModified()) return + println("$taskName: found modified launcher icon: " + original.absolutePath) + File target = new File(targetDir, original.name) + targetDir.mkdirs() + Files.copy(original.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING) + IconUtils.createLayeredLabel(target, bannerLabel, originalBaseName.endsWith("_foreground")) } } - - if (bannerLabel == null || bannerLabel.empty) { - // no label - println("$taskName: skipped icon labelling") - return - } - - allIcons.each { File original -> - String resTypeName = original.parentFile.name - String originalBaseName = original.name.takeBefore(".") - File targetDir = new File("${generatedResDir.toString()}/${variant.flavorName}/$resTypeName") - File modified = targetDir.listFiles({ File file -> file.name.matches("${originalBaseName}\\.[^.]+") } as FileFilter)?.find() as File - if (modified != null && original.lastModified() <= modified.lastModified() && gradleLastModified <= modified.lastModified()) return - println("$taskName: found modified launcher icon: " + original.absolutePath) - File target = new File(targetDir, original.name) - targetDir.mkdirs() - Files.copy(original.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING) - IconUtils.createLayeredLabel(target, bannerLabel, originalBaseName.endsWith("_foreground")) - } } } diff --git a/src/main/groovy/ch/ubique/gradle/ubdiag/IconUtils.groovy b/src/main/groovy/ch/ubique/gradle/ubdiag/IconUtils.groovy index ac08dfb..a67ac9c 100644 --- a/src/main/groovy/ch/ubique/gradle/ubdiag/IconUtils.groovy +++ b/src/main/groovy/ch/ubique/gradle/ubdiag/IconUtils.groovy @@ -1,6 +1,7 @@ package ch.ubique.gradle.ubdiag import groovy.io.FileType +import groovy.xml.XmlSlurper import javax.imageio.ImageIO import java.awt.*