From ec69d31cd49140eae71464e9cad24c2a6059e8d6 Mon Sep 17 00:00:00 2001 From: Sven Obser Date: Sat, 15 Jul 2023 10:22:26 +0200 Subject: [PATCH 1/4] Night/Dark mode support for Android and iOS Closes #24 --- README.md | 15 ++ .../plugin/LibresImagesGenerationTask.kt | 65 +++++--- .../skeptick/libres/plugin/ResourcesPlugin.kt | 9 +- .../libres/plugin/images/models/ImageProps.kt | 9 +- .../libres/plugin/images/models/ImageSet.kt | 12 ++ .../plugin/images/models/ImageSetContents.kt | 37 ++--- .../processing/ImageSpecs+Operations.kt | 142 ++++++++++++++---- 7 files changed, 218 insertions(+), 71 deletions(-) create mode 100644 gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/images/models/ImageSet.kt diff --git a/README.md b/README.md index 6dbad79..1c22272 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,9 @@ Multiplatform: │ ├── images │ │ ├── vector_image.svg │ │ └── raster_image.png +│ ├── images-night +│ │ ├── vector_image.svg +│ │ └── raster_image.png │ └── strings │ ├── strings_en.xml │ └── strings_ru.xml @@ -74,6 +77,9 @@ Android or JVM: │ ├── images │ │ ├── vector_image.svg │ │ └── raster_image.png +│ ├── images-night +│ │ ├── vector_image.svg +│ │ └── raster_image.png │ └── strings │ ├── strings_en.xml │ └── strings_ru.xml @@ -171,6 +177,15 @@ Sample: Image size in Figma is **240x89**. Final image name is **pic_(orig)_(240).png** +#### Night/Dark Mode Images +Night/Dark Mode images are supported for Android and iOS by placing a night version of the image into the +`libres/images-night/` folder. + +The filename and type of the image must match the corresponding day/light version in `libres/images/`. + +- For Android this creates night images in `drawable-night-nodpi`. +- For iOS this creates a `"appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ]` entry in the `imageset`. + ## Jetpack Compose ```kotlin diff --git a/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/LibresImagesGenerationTask.kt b/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/LibresImagesGenerationTask.kt index 989f1e3..9d5ce63 100644 --- a/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/LibresImagesGenerationTask.kt +++ b/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/LibresImagesGenerationTask.kt @@ -1,20 +1,21 @@ package io.github.skeptick.libres.plugin -import org.gradle.api.DefaultTask -import org.gradle.api.file.FileCollection -import org.gradle.api.tasks.* -import org.gradle.work.ChangeType -import org.gradle.work.Incremental -import org.gradle.work.InputChanges import io.github.skeptick.libres.plugin.common.declarations.saveToDirectory import io.github.skeptick.libres.plugin.common.extensions.deleteFilesInDirectory -import io.github.skeptick.libres.plugin.common.project.appleBundleName import io.github.skeptick.libres.plugin.images.ImagesTypeSpecsBuilder import io.github.skeptick.libres.plugin.images.declarations.EmptyImagesObject import io.github.skeptick.libres.plugin.images.declarations.ImagesObjectFile import io.github.skeptick.libres.plugin.images.models.ImageProps +import io.github.skeptick.libres.plugin.images.models.ImageSet import io.github.skeptick.libres.plugin.images.processing.removeImage +import io.github.skeptick.libres.plugin.images.processing.saveImageSet import io.github.skeptick.libres.plugin.images.processing.saveImage +import org.gradle.api.DefaultTask +import org.gradle.api.file.FileCollection +import org.gradle.api.tasks.* +import org.gradle.work.ChangeType +import org.gradle.work.Incremental +import org.gradle.work.InputChanges import java.io.File @CacheableTask @@ -28,6 +29,11 @@ abstract class LibresImagesGenerationTask : DefaultTask() { @get:PathSensitive(PathSensitivity.RELATIVE) internal abstract var inputDirectory: FileCollection + @get:Incremental + @get:InputFiles + @get:PathSensitive(PathSensitivity.RELATIVE) + internal abstract var nightInputDirectory: FileCollection + @get:OutputDirectories internal abstract var outputSourcesDirectories: Map @@ -36,18 +42,42 @@ abstract class LibresImagesGenerationTask : DefaultTask() { @TaskAction fun apply(inputChanges: InputChanges) { - inputChanges.getFileChanges(inputDirectory).forEach { change -> - @Suppress("WHEN_ENUM_CAN_BE_NULL_IN_JAVA") - when (change.changeType) { - ChangeType.REMOVED -> ImageProps(change.file).removeImage(outputResourcesDirectories) - ChangeType.MODIFIED, ChangeType.ADDED -> ImageProps(change.file).saveImage(outputResourcesDirectories) + // Update images for changed files + sequenceOf( + inputChanges.getFileChanges(inputDirectory), + inputChanges.getFileChanges(nightInputDirectory), + ).flatten() + .forEach { change -> + val image = ImageProps(change.file) + + @Suppress("WHEN_ENUM_CAN_BE_NULL_IN_JAVA") + when (change.changeType) { + ChangeType.MODIFIED, ChangeType.ADDED -> image.saveImage(outputResourcesDirectories) + ChangeType.REMOVED -> image.removeImage(outputResourcesDirectories) + } } - } - inputDirectory.files + // Generate image catalog + sequenceOf( + inputDirectory.files, + nightInputDirectory.files, + ).flatten() + .map(::ImageProps) + .groupBy(ImageProps::name) + .map { (name, files) -> ImageSet(name, files) } + .forEach { catalog -> + catalog.saveImageSet(outputResourcesDirectories) + } + + // Generate code + sequenceOf( + inputDirectory.files, + nightInputDirectory.files, + ).flatten().toSet() .takeIf { files -> files.isNotEmpty() } - ?.map { file -> ImageProps(file) } - ?.let { imageProps -> buildImages(imageProps) } + ?.distinctBy(File::nameWithoutExtension) + ?.map(::ImageProps) + ?.let(::buildImages) ?: buildEmptyImages() } @@ -67,5 +97,4 @@ abstract class LibresImagesGenerationTask : DefaultTask() { imagesObjectFileSpec.saveToDirectory(directory) } } - -} \ No newline at end of file +} diff --git a/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/ResourcesPlugin.kt b/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/ResourcesPlugin.kt index 1b5a1bd..4d067fb 100644 --- a/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/ResourcesPlugin.kt +++ b/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/ResourcesPlugin.kt @@ -118,6 +118,7 @@ class ResourcesPlugin : Plugin { private fun Project.registerGeneratorsTasks() { val stringsInputDirectory = File(inputDirectory, "strings") val imagesInputDirectory = File(inputDirectory, "images") + val nightImagesInputDirectory = File(inputDirectory, "images-night") val stringsOutputPackageName = listOfNotNull(outputPackageName, "strings").joinToString(".") val imagesOutputPackageName = listOfNotNull(outputPackageName, "images").joinToString(".") @@ -151,6 +152,11 @@ class ResourcesPlugin : Plugin { !element.isDirectory && element.file.extension.lowercase() in IMAGES_EXTENSIONS } } + task.nightInputDirectory = fileTree(nightImagesInputDirectory) { config -> + config.include { element -> + !element.isDirectory && element.file.extension.lowercase() in IMAGES_EXTENSIONS + } + } task.outputSourcesDirectories = allSourceSets.associateBy(PluginSourceSet::platform) { it.sourcesDir.toOutputDirectory(imagesOutputPackageName) } @@ -248,5 +254,4 @@ class ResourcesPlugin : Plugin { File(absolutePath, packageName.replace('.', File.separatorChar)) } - -} \ No newline at end of file +} diff --git a/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/images/models/ImageProps.kt b/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/images/models/ImageProps.kt index 1d8ed76..5166d07 100644 --- a/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/images/models/ImageProps.kt +++ b/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/images/models/ImageProps.kt @@ -8,20 +8,23 @@ internal class ImageProps(val file: File) { val extension: String val targetSize: Int? val isTintable: Boolean + val isNightMode: Boolean init { + val directoryName = file.parentFile.name val nameWithoutExtension = file.nameWithoutExtension val parameters = ParametersRegex.findAll(nameWithoutExtension).toList() + this.name = nameWithoutExtension.substringBefore("_(").lowercase() this.extension = file.extension.lowercase() - this.targetSize = if (extension != "svg") parameters.firstNotNullOfOrNull { it.groupValues[1].toIntOrNull() } else null + this.targetSize = if (!isVector) parameters.firstNotNullOfOrNull { it.groupValues[1].toIntOrNull() } else null this.isTintable = parameters.none { it.groupValues[1].startsWith("orig") } + this.isNightMode = directoryName.endsWith("-night") } companion object { private val ParametersRegex = Regex("_\\((.*?)\\)") } - } -internal val ImageProps.isVector: Boolean get() = extension == "svg" \ No newline at end of file +internal val ImageProps.isVector: Boolean get() = extension == "svg" diff --git a/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/images/models/ImageSet.kt b/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/images/models/ImageSet.kt new file mode 100644 index 0000000..7dfa753 --- /dev/null +++ b/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/images/models/ImageSet.kt @@ -0,0 +1,12 @@ +package io.github.skeptick.libres.plugin.images.models + +internal class ImageSet( + val name: String, + val images: Iterable, +) { + val isVector: Boolean + get() = images.all { it.isVector } + + val isTintable: Boolean + get() = images.all { it.isTintable } +} diff --git a/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/images/models/ImageSetContents.kt b/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/images/models/ImageSetContents.kt index ff67433..64a26f7 100644 --- a/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/images/models/ImageSetContents.kt +++ b/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/images/models/ImageSetContents.kt @@ -23,9 +23,24 @@ internal data class ImageSetContents( data class Image( val filename: String, val scale: ImageScale? = null, - val idiom: String = "universal" + val idiom: String = "universal", + val appearances: List? = null, ) + sealed interface Appearance { + val appearance: String + val value: String + + sealed interface Luminosity : Appearance { + override val appearance: String + get() = "luminosity" + + object Dark : Luminosity { + override val value: String = "dark" + } + } + } + data class Info( val author: String = "xcode", val version: Int = 1 @@ -35,24 +50,4 @@ internal data class ImageSetContents( @JsonProperty("preserves-vector-representation") val preserveVectorRepresentation: Boolean?, @JsonProperty("template-rendering-intent") val templateRenderingIntent: VectorRenderingType ) - } - -internal fun ImageProps.toImageSetContents() = - ImageSetContents( - images = when (targetSize) { - null -> listOf( - ImageSetContents.Image(filename = "$name.$extension") - ) - else -> ImageSetContents.ImageScale.values().map { - ImageSetContents.Image(filename = "${this.name}_${it.name}.$extension", scale = it) - } - }, - properties = ImageSetContents.Properties( - preserveVectorRepresentation = if (isVector) true else null, - templateRenderingIntent = when (isTintable) { - true -> ImageSetContents.VectorRenderingType.Template - false -> ImageSetContents.VectorRenderingType.Original - } - ) - ) \ No newline at end of file diff --git a/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/images/processing/ImageSpecs+Operations.kt b/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/images/processing/ImageSpecs+Operations.kt index e2a02ef..cd4e1e1 100644 --- a/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/images/processing/ImageSpecs+Operations.kt +++ b/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/images/processing/ImageSpecs+Operations.kt @@ -5,14 +5,18 @@ import com.fasterxml.jackson.annotation.JsonInclude import com.fasterxml.jackson.core.util.DefaultIndenter import com.fasterxml.jackson.core.util.DefaultPrettyPrinter import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import org.bytedeco.opencv.global.opencv_imgcodecs -import org.bytedeco.opencv.global.opencv_imgproc -import org.bytedeco.opencv.opencv_core.Mat import io.github.skeptick.libres.plugin.KotlinPlatform -import io.github.skeptick.libres.plugin.images.models.* -import io.github.skeptick.libres.plugin.images.models.ImageScale import io.github.skeptick.libres.plugin.images.models.ImageProps +import io.github.skeptick.libres.plugin.images.models.ImageScale +import io.github.skeptick.libres.plugin.images.models.ImageSet +import io.github.skeptick.libres.plugin.images.models.ImageSetContents +import io.github.skeptick.libres.plugin.images.models.ImageSetContents.Appearance import io.github.skeptick.libres.plugin.images.models.androidName +import io.github.skeptick.libres.plugin.images.models.isVector +import io.github.skeptick.libres.plugin.images.models.times +import org.bytedeco.opencv.global.opencv_imgcodecs +import org.bytedeco.opencv.global.opencv_imgproc +import org.bytedeco.opencv.opencv_core.Mat import java.io.File import java.io.FileOutputStream import java.io.OutputStream @@ -25,10 +29,6 @@ private val jsonWriter = jacksonObjectMapper().let { } internal fun ImageProps.saveImage(directories: Map) { - directories[KotlinPlatform.Apple]?.let { - saveImageSetContents(it) - } - if (targetSize == null) { saveOriginal(directories) } else { @@ -50,10 +50,10 @@ internal fun ImageProps.removeImage(directories: Map) { when { platform == KotlinPlatform.Common -> continue platform == KotlinPlatform.Apple -> File(directory, "$name.imageset").deleteRecursively() - platform == KotlinPlatform.Android && targetSize != null -> ImageScale.values().forEach { - File(directory, targetFilePath(platform, it)).delete() + platform == KotlinPlatform.Android && targetSize != null -> ImageScale.values().forEach { scale -> + targetFile(directory, platform, scale).delete() } - else -> File(directory, targetFilePath(platform)).delete() + else -> targetFile(directory, platform).delete() } } } @@ -63,14 +63,18 @@ private fun ImageProps.saveOriginal(directories: Map) { when { platform == KotlinPlatform.Common -> continue platform == KotlinPlatform.Android && isVector -> { - val targetFile = File(directory, targetFilePath(platform)) - targetFile.parentFile.mkdirs() + val targetFile = targetFile(directory, platform).apply { + parentFile.mkdirs() + } + val output = FileOutputStream(targetFile) parseSvgToXml(file, output) } else -> { - val targetFile = File(directory, targetFilePath(platform)) - targetFile.parentFile.mkdirs() + val targetFile = targetFile(directory, platform).apply { + parentFile.mkdirs() + } + file.copyTo(targetFile, overwrite = true) } } @@ -80,8 +84,10 @@ private fun ImageProps.saveOriginal(directories: Map) { private fun ImageProps.saveOriginal(scale: ImageScale, directories: Map) { for ((platform, directory) in directories) { if (platform in scale.supportedPlatforms) { - val targetFile = File(directory, targetFilePath(platform, scale)) - file.parentFile.mkdirs() + val targetFile = targetFile(directory, platform, scale).apply { + parentFile.mkdirs() + } + file.copyTo(targetFile, overwrite = true) } } @@ -94,28 +100,110 @@ private fun ImageProps.resizeAndSave(src: Mat, scale: ImageScale, size: Int, dir for ((platform, directory) in directories) { if (platform in scale.supportedPlatforms) { - val targetPath = directory.absolutePath + targetFilePath(platform, scale) - File(targetPath).parentFile.mkdirs() - opencv_imgcodecs.imwrite(targetPath, destinationImage) + val targetFile = targetFile(directory, platform, scale).apply { + parentFile.mkdirs() + } + + opencv_imgcodecs.imwrite(targetFile.absolutePath, destinationImage) + } + } +} + +internal fun ImageSet.saveImageSet(directories: Map) { + for ((platform, directory) in directories) { + when (platform) { + KotlinPlatform.Apple -> saveImageSetContents(directory) + KotlinPlatform.Android, KotlinPlatform.Common, KotlinPlatform.Jvm, KotlinPlatform.Js -> Unit } } } -private fun ImageProps.saveImageSetContents(directory: File) { - val text = jsonWriter.writeValueAsString(toImageSetContents()) +private fun ImageSet.saveImageSetContents(directory: File) { + val text = jsonWriter.writeValueAsString(toImageSetContents(directory)) val file = File(directory, "$name.imageset/Contents.json") file.parentFile.mkdirs() file.writeText(text) } +private fun ImageSet.toImageSetContents(directory: File) = + ImageSetContents( + images = images.map { image -> + val appearances = image.appearances() + when (image.targetSize) { + null -> listOf( + ImageSetContents.Image( + filename = image.targetFile(directory, KotlinPlatform.Apple).name, + appearances = appearances, + ) + ) + + else -> ImageSetContents.ImageScale.values().map { scale -> + ImageSetContents.Image( + filename = image.targetFile(directory, KotlinPlatform.Apple, scale.toImageScale()).name, + scale = scale, + appearances = appearances, + ) + } + } + }.flatten(), + properties = ImageSetContents.Properties( + preserveVectorRepresentation = if (isVector) true else null, + templateRenderingIntent = when (isTintable) { + true -> ImageSetContents.VectorRenderingType.Template + false -> ImageSetContents.VectorRenderingType.Original + } + ) + ) + +private fun ImageSetContents.ImageScale.toImageScale() = + when (this) { + ImageSetContents.ImageScale.x1 -> ImageScale.x1 + ImageSetContents.ImageScale.x2 -> ImageScale.x2 + ImageSetContents.ImageScale.x3 -> ImageScale.x3 + } + +private fun ImageProps.appearances(): List? = + listOfNotNull( + if (isNightMode) Appearance.Luminosity.Dark else null, + ).takeIf(List<*>::isNotEmpty) + +private fun ImageProps.targetFile( + directory: File, + platform: KotlinPlatform, + scale: ImageScale? = null, +) = File(directory, targetFilePath(platform, scale)) + private fun ImageProps.targetFilePath(platform: KotlinPlatform, scale: ImageScale? = null): String = when (platform) { - KotlinPlatform.Android -> "/drawable-${scale?.androidName ?: "nodpi"}/$name.${if (isVector) "xml" else extension}" - KotlinPlatform.Apple -> "/$name.imageset/$name${if (scale != null) "_${scale.name}" else ""}.$extension" + KotlinPlatform.Android -> androidTargetFilePath(scale) + KotlinPlatform.Apple -> appleTargetFilePath(scale) KotlinPlatform.Jvm, KotlinPlatform.Js -> "/$name.$extension" - KotlinPlatform.Common -> throw IllegalArgumentException() + KotlinPlatform.Common -> error("Can not generate targetFilePath for platform '$platform'.") } +private fun ImageProps.androidTargetFilePath(scale: ImageScale?): String { + val folderName = listOfNotNull( + "drawable", + if (isNightMode) "night" else null, + scale?.androidName ?: "nodpi", + ).joinToString("-") + val fileName = name + val extension = if (isVector) "xml" else extension + + return "/$folderName/$fileName.$extension" +} + +private fun ImageProps.appleTargetFilePath(scale: ImageScale?): String { + val folderName = "$name.imageset" + val fileName = listOfNotNull( + name, + scale?.name, + if (isNightMode) "night" else null, + ).joinToString("_") + + return "/$folderName/$fileName.$extension" +} + /** * Workaround for Svg2Vector::parseSvgToXml */ @@ -125,4 +213,4 @@ private fun parseSvgToXml(inputSvg: File, output: OutputStream) { File::class.java -> method(null, inputSvg, output) else -> method(null, inputSvg.toPath(), output) } -} \ No newline at end of file +} From 3715759eb23949c5e903b91b66cdd87cfa79a524 Mon Sep 17 00:00:00 2001 From: Sven Obser Date: Sat, 15 Jul 2023 13:03:49 +0200 Subject: [PATCH 2/4] Use existing image parameter infrastructure instead of additional folder. --- README.md | 25 +++++++----------- .../plugin/LibresImagesGenerationTask.kt | 26 +++++-------------- .../skeptick/libres/plugin/ResourcesPlugin.kt | 9 ++----- .../libres/plugin/images/models/ImageProps.kt | 3 +-- 4 files changed, 19 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 1c22272..d26de74 100644 --- a/README.md +++ b/README.md @@ -61,9 +61,6 @@ Multiplatform: │ ├── images │ │ ├── vector_image.svg │ │ └── raster_image.png -│ ├── images-night -│ │ ├── vector_image.svg -│ │ └── raster_image.png │ └── strings │ ├── strings_en.xml │ └── strings_ru.xml @@ -77,9 +74,6 @@ Android or JVM: │ ├── images │ │ ├── vector_image.svg │ │ └── raster_image.png -│ ├── images-night -│ │ ├── vector_image.svg -│ │ └── raster_image.png │ └── strings │ ├── strings_en.xml │ └── strings_ru.xml @@ -147,17 +141,27 @@ Recommended for multicolor images. > - For iOS images are generated from 1x to 3x (where 1x is 1:1 in pixels to the specified size) > - For JVM and JS a single image of the specified size is generated. +> **night** +> +> Night/Dark Mode images are supported for Android and iOS by adding the `(night)` modifier. The filename and type of +> the image must match the corresponding day/light version without the `(night)` modifier. +> +> - For Android this creates night images in `drawable-night-nodpi`. +> - For iOS this creates a `"appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ]` entry in the `imageset`. + Filename examples: ``` some_hd_image_(100).jpg app_logo_(orig).svg my_colorful_bitmap_(orig)_(150).png +image_with_night_support_(night).png ``` Kotlin: ```kotlin MainRes.image.some_hd_image MainRes.image.app_logo MainRes.image.my_colorful_bitmap +MainRes.image.image_with_night_support ``` Swift: ```swift @@ -177,15 +181,6 @@ Sample: Image size in Figma is **240x89**. Final image name is **pic_(orig)_(240).png** -#### Night/Dark Mode Images -Night/Dark Mode images are supported for Android and iOS by placing a night version of the image into the -`libres/images-night/` folder. - -The filename and type of the image must match the corresponding day/light version in `libres/images/`. - -- For Android this creates night images in `drawable-night-nodpi`. -- For iOS this creates a `"appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ]` entry in the `imageset`. - ## Jetpack Compose ```kotlin diff --git a/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/LibresImagesGenerationTask.kt b/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/LibresImagesGenerationTask.kt index 9d5ce63..51e6530 100644 --- a/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/LibresImagesGenerationTask.kt +++ b/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/LibresImagesGenerationTask.kt @@ -8,8 +8,8 @@ import io.github.skeptick.libres.plugin.images.declarations.ImagesObjectFile import io.github.skeptick.libres.plugin.images.models.ImageProps import io.github.skeptick.libres.plugin.images.models.ImageSet import io.github.skeptick.libres.plugin.images.processing.removeImage -import io.github.skeptick.libres.plugin.images.processing.saveImageSet import io.github.skeptick.libres.plugin.images.processing.saveImage +import io.github.skeptick.libres.plugin.images.processing.saveImageSet import org.gradle.api.DefaultTask import org.gradle.api.file.FileCollection import org.gradle.api.tasks.* @@ -29,11 +29,6 @@ abstract class LibresImagesGenerationTask : DefaultTask() { @get:PathSensitive(PathSensitivity.RELATIVE) internal abstract var inputDirectory: FileCollection - @get:Incremental - @get:InputFiles - @get:PathSensitive(PathSensitivity.RELATIVE) - internal abstract var nightInputDirectory: FileCollection - @get:OutputDirectories internal abstract var outputSourcesDirectories: Map @@ -43,10 +38,7 @@ abstract class LibresImagesGenerationTask : DefaultTask() { @TaskAction fun apply(inputChanges: InputChanges) { // Update images for changed files - sequenceOf( - inputChanges.getFileChanges(inputDirectory), - inputChanges.getFileChanges(nightInputDirectory), - ).flatten() + inputChanges.getFileChanges(inputDirectory) .forEach { change -> val image = ImageProps(change.file) @@ -57,11 +49,8 @@ abstract class LibresImagesGenerationTask : DefaultTask() { } } - // Generate image catalog - sequenceOf( - inputDirectory.files, - nightInputDirectory.files, - ).flatten() + // Generate image sets + inputDirectory.files .map(::ImageProps) .groupBy(ImageProps::name) .map { (name, files) -> ImageSet(name, files) } @@ -70,13 +59,10 @@ abstract class LibresImagesGenerationTask : DefaultTask() { } // Generate code - sequenceOf( - inputDirectory.files, - nightInputDirectory.files, - ).flatten().toSet() + inputDirectory.files .takeIf { files -> files.isNotEmpty() } - ?.distinctBy(File::nameWithoutExtension) ?.map(::ImageProps) + ?.distinctBy(ImageProps::name) ?.let(::buildImages) ?: buildEmptyImages() } diff --git a/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/ResourcesPlugin.kt b/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/ResourcesPlugin.kt index 4d067fb..1b5a1bd 100644 --- a/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/ResourcesPlugin.kt +++ b/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/ResourcesPlugin.kt @@ -118,7 +118,6 @@ class ResourcesPlugin : Plugin { private fun Project.registerGeneratorsTasks() { val stringsInputDirectory = File(inputDirectory, "strings") val imagesInputDirectory = File(inputDirectory, "images") - val nightImagesInputDirectory = File(inputDirectory, "images-night") val stringsOutputPackageName = listOfNotNull(outputPackageName, "strings").joinToString(".") val imagesOutputPackageName = listOfNotNull(outputPackageName, "images").joinToString(".") @@ -152,11 +151,6 @@ class ResourcesPlugin : Plugin { !element.isDirectory && element.file.extension.lowercase() in IMAGES_EXTENSIONS } } - task.nightInputDirectory = fileTree(nightImagesInputDirectory) { config -> - config.include { element -> - !element.isDirectory && element.file.extension.lowercase() in IMAGES_EXTENSIONS - } - } task.outputSourcesDirectories = allSourceSets.associateBy(PluginSourceSet::platform) { it.sourcesDir.toOutputDirectory(imagesOutputPackageName) } @@ -254,4 +248,5 @@ class ResourcesPlugin : Plugin { File(absolutePath, packageName.replace('.', File.separatorChar)) } -} + +} \ No newline at end of file diff --git a/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/images/models/ImageProps.kt b/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/images/models/ImageProps.kt index 5166d07..3b9e686 100644 --- a/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/images/models/ImageProps.kt +++ b/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/images/models/ImageProps.kt @@ -11,7 +11,6 @@ internal class ImageProps(val file: File) { val isNightMode: Boolean init { - val directoryName = file.parentFile.name val nameWithoutExtension = file.nameWithoutExtension val parameters = ParametersRegex.findAll(nameWithoutExtension).toList() @@ -19,7 +18,7 @@ internal class ImageProps(val file: File) { this.extension = file.extension.lowercase() this.targetSize = if (!isVector) parameters.firstNotNullOfOrNull { it.groupValues[1].toIntOrNull() } else null this.isTintable = parameters.none { it.groupValues[1].startsWith("orig") } - this.isNightMode = directoryName.endsWith("-night") + this.isNightMode = parameters.any { it.groupValues[1] == "night" } } companion object { From 8c9fe3d9f1e639ad9d1758f63010b0dc4dee8869 Mon Sep 17 00:00:00 2001 From: Sven Obser Date: Thu, 30 Nov 2023 13:49:20 +0100 Subject: [PATCH 3/4] jitpack.yml --- jitpack.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 jitpack.yml diff --git a/jitpack.yml b/jitpack.yml new file mode 100644 index 0000000..d1f7068 --- /dev/null +++ b/jitpack.yml @@ -0,0 +1,4 @@ +jdk: + - openjdk17 +install: + - ./gradlew publishToMavenLocal From 5253bc9a16538f2c71b08f45d083edf7f4719eed Mon Sep 17 00:00:00 2001 From: Sven Obser Date: Tue, 12 Dec 2023 17:44:06 +0100 Subject: [PATCH 4/4] Export vectors as `anydpi` --- .../libres/plugin/images/processing/ImageSpecs+Operations.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/images/processing/ImageSpecs+Operations.kt b/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/images/processing/ImageSpecs+Operations.kt index cd4e1e1..c83b5b3 100644 --- a/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/images/processing/ImageSpecs+Operations.kt +++ b/gradle-plugin/src/main/java/io/github/skeptick/libres/plugin/images/processing/ImageSpecs+Operations.kt @@ -185,7 +185,7 @@ private fun ImageProps.androidTargetFilePath(scale: ImageScale?): String { val folderName = listOfNotNull( "drawable", if (isNightMode) "night" else null, - scale?.androidName ?: "nodpi", + scale?.androidName ?: if (isVector) "anydpi" else "nodpi", ).joinToString("-") val fileName = name val extension = if (isVector) "xml" else extension