From fbf5dbe20fbd34c11cd48ef64ac93e050b8c3388 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Mon, 12 Feb 2024 17:57:12 +0100 Subject: [PATCH] Fix fonts duplication in android app. (#4284) --- .../resources/ResourceReader.android.kt | 18 +++++++ .../resources/ResourceReader.desktop.kt} | 0 .../compose/resources/ResourcesGenerator.kt | 48 +++++++++++++++---- .../test/tests/integration/ResourcesTest.kt | 8 +++- 4 files changed, 64 insertions(+), 10 deletions(-) create mode 100644 components/resources/library/src/androidMain/kotlin/org/jetbrains/compose/resources/ResourceReader.android.kt rename components/resources/library/src/{jvmAndAndroidMain/kotlin/org/jetbrains/compose/resources/ResourceReader.jvmAndAndroid.kt => desktopMain/kotlin/org/jetbrains/compose/resources/ResourceReader.desktop.kt} (100%) diff --git a/components/resources/library/src/androidMain/kotlin/org/jetbrains/compose/resources/ResourceReader.android.kt b/components/resources/library/src/androidMain/kotlin/org/jetbrains/compose/resources/ResourceReader.android.kt new file mode 100644 index 0000000000..b04857b424 --- /dev/null +++ b/components/resources/library/src/androidMain/kotlin/org/jetbrains/compose/resources/ResourceReader.android.kt @@ -0,0 +1,18 @@ +package org.jetbrains.compose.resources + +import java.io.File + +private object AndroidResourceReader + +@OptIn(ExperimentalResourceApi::class) +@InternalResourceApi +actual suspend fun readResourceBytes(path: String): ByteArray { + val classLoader = Thread.currentThread().contextClassLoader ?: AndroidResourceReader.javaClass.classLoader + val resource = classLoader.getResourceAsStream(path) ?: run { + //try to find a font in the android assets + if (File(path).parentFile?.name.orEmpty() == "font") { + classLoader.getResourceAsStream("assets/$path") + } else null + } ?: throw MissingResourceException(path) + return resource.readBytes() +} \ No newline at end of file diff --git a/components/resources/library/src/jvmAndAndroidMain/kotlin/org/jetbrains/compose/resources/ResourceReader.jvmAndAndroid.kt b/components/resources/library/src/desktopMain/kotlin/org/jetbrains/compose/resources/ResourceReader.desktop.kt similarity index 100% rename from components/resources/library/src/jvmAndAndroidMain/kotlin/org/jetbrains/compose/resources/ResourceReader.jvmAndAndroid.kt rename to components/resources/library/src/desktopMain/kotlin/org/jetbrains/compose/resources/ResourceReader.desktop.kt diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesGenerator.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesGenerator.kt index 801c8af054..1d1c8081fa 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesGenerator.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesGenerator.kt @@ -15,8 +15,6 @@ import org.jetbrains.compose.desktop.application.internal.ComposeProperties import org.jetbrains.compose.internal.KOTLIN_JVM_PLUGIN_ID import org.jetbrains.compose.internal.KOTLIN_MPP_PLUGIN_ID import org.jetbrains.compose.internal.utils.* -import org.jetbrains.compose.internal.utils.dependsOn -import org.jetbrains.compose.internal.utils.registerTask import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension @@ -74,17 +72,31 @@ private fun Project.configureAndroidComposeResources( kotlinExtension: KotlinMultiplatformExtension, androidExtension: BaseExtension ) { + val commonResourcesDir = projectDir.resolve("src/${KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME}/$COMPOSE_RESOURCES_DIR") + + //Copy common compose resources except fonts to android resources + val copyCommonAndroidComposeResources = registerTask( + "copyCommonAndroidComposeResources" + ) { + from.set(commonResourcesDir) + outputDirectory.set(layout.buildDirectory.dir("$RES_GEN_DIR/commonAndroidComposeResources")) + } + tasks.configureEachWithType { dependsOn(copyCommonAndroidComposeResources) } + //mark all composeResources as Android resources - kotlinExtension.targets.matching { it is KotlinAndroidTarget }.all { androidTarget -> + kotlinExtension.targets.withType(KotlinAndroidTarget::class.java).all { androidTarget -> androidTarget.compilations.all { compilation: KotlinCompilation<*> -> compilation.defaultSourceSet.androidSourceSetInfoOrNull?.let { kotlinAndroidSourceSet -> androidExtension.sourceSets .matching { it.name == kotlinAndroidSourceSet.androidSourceSetName } .all { androidSourceSet -> - (compilation.allKotlinSourceSets as ObservableSet).forAll { kotlinSourceSet -> - androidSourceSet.resources.srcDir( - projectDir.resolve("src/${kotlinSourceSet.name}/$COMPOSE_RESOURCES_DIR") - ) + androidSourceSet.resources.srcDir(copyCommonAndroidComposeResources.flatMap { it.outputDirectory.asFile }) + (compilation.allKotlinSourceSets as? ObservableSet)?.forAll { kotlinSourceSet -> + if (kotlinSourceSet.name != KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME) { + androidSourceSet.resources.srcDir( + projectDir.resolve("src/${kotlinSourceSet.name}/$COMPOSE_RESOURCES_DIR") + ) + } } } } @@ -93,7 +105,6 @@ private fun Project.configureAndroidComposeResources( //copy fonts from the compose resources dir to android assets val androidComponents = project.extensions.findByType(AndroidComponentsExtension::class.java) ?: return - val commonResourcesDir = projectDir.resolve("src/${KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME}/$COMPOSE_RESOURCES_DIR") androidComponents.onVariants { variant -> val copyFonts = registerTask( "copy${variant.name.uppercaseFirstChar()}FontsToAndroidAssets" @@ -155,6 +166,27 @@ private fun Project.configureResourceGenerator(commonComposeResourcesDir: File, } } +internal abstract class CopyCommonAndroidComposeResources : DefaultTask() { + @get:Inject + abstract val fileSystem: FileSystemOperations + + @get:InputFiles + abstract val from: Property + + @get:OutputDirectory + abstract val outputDirectory: DirectoryProperty + + @TaskAction + fun action() { + fileSystem.copy { + it.includeEmptyDirs = false + it.from(from) + it.exclude("**/font*/*") + it.into(outputDirectory) + } + } +} + //Copy task doesn't work with 'variant.sources?.assets?.addGeneratedSourceDirectory' API internal abstract class CopyAndroidFontsToAssetsTask : DefaultTask() { @get:Inject diff --git a/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt b/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt index 534216f3ee..8958aafacc 100644 --- a/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt +++ b/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt @@ -203,8 +203,12 @@ class ResourcesTest : GradlePluginTestBase() { assertTrue(apk.exists()) ZipFile(apk).use { zip -> commonResourcesFiles.forEach { res -> - assertNotNull(zip.getEntry(res)) - //todo fix duplicate fonts + if (res == "font/emptyFont.otf") { + //android fonts should be only in assets + assertNull(zip.getEntry(res)) + } else { + assertNotNull(zip.getEntry(res)) + } } assertNotNull(zip.getEntry("assets/font/emptyFont.otf")) val platformTxt = zip.getEntry("files/platform.txt")