From db9f14d7c2f112382c68930fa420ca7cffea6858 Mon Sep 17 00:00:00 2001 From: takahirom Date: Mon, 17 Jun 2024 11:19:37 +0900 Subject: [PATCH 1/2] Introduce compose preview scanner --- app-android/build.gradle.kts | 1 + .../main/res/values-night/splash_theme.xml | 1 - .../src/main/res/values/splash_theme.xml | 1 - .../droidkaigi/confsched/PreviewTest.kt | 139 +++--------------- .../confsched/ShowkaseRootModule.kt | 7 - build-logic/build.gradle.kts | 4 - .../primitive/AndroidRoborazziPlugin.kt | 3 - .../primitive/KmpAndroidShowkasePlugin.kt | 26 ---- .../confsched/primitive/KmpRoborazziPlugin.kt | 11 -- .../primitive/KoverEntryPointPlugin.kt | 1 - core/designsystem/build.gradle.kts | 1 - gradle/libs.versions.toml | 5 +- settings.gradle.kts | 1 + 13 files changed, 27 insertions(+), 174 deletions(-) delete mode 100644 app-android/src/test/java/io/github/droidkaigi/confsched/ShowkaseRootModule.kt delete mode 100644 build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpAndroidShowkasePlugin.kt diff --git a/app-android/build.gradle.kts b/app-android/build.gradle.kts index 287a18875..3438a8617 100644 --- a/app-android/build.gradle.kts +++ b/app-android/build.gradle.kts @@ -113,4 +113,5 @@ dependencies { implementation(libs.firebaseDynamicLinks) debugImplementation(projects.core.testingManifest) testImplementation(projects.core.testing) + testImplementation(libs.composablePreviewScanner) } diff --git a/app-android/src/main/res/values-night/splash_theme.xml b/app-android/src/main/res/values-night/splash_theme.xml index 503d3f9e3..35fafb677 100644 --- a/app-android/src/main/res/values-night/splash_theme.xml +++ b/app-android/src/main/res/values-night/splash_theme.xml @@ -5,7 +5,6 @@ @drawable/splash_icon @android:color/black 5000 - @style/Theme.App @drawable/splash_branding_image diff --git a/app-android/src/main/res/values/splash_theme.xml b/app-android/src/main/res/values/splash_theme.xml index 280cf5162..dc3334cee 100644 --- a/app-android/src/main/res/values/splash_theme.xml +++ b/app-android/src/main/res/values/splash_theme.xml @@ -5,7 +5,6 @@ @drawable/splash_icon @android:color/white 5000 - @style/Theme.App @drawable/splash_branding_image diff --git a/app-android/src/test/java/io/github/droidkaigi/confsched/PreviewTest.kt b/app-android/src/test/java/io/github/droidkaigi/confsched/PreviewTest.kt index 2242c5e31..8c184a4fe 100644 --- a/app-android/src/test/java/io/github/droidkaigi/confsched/PreviewTest.kt +++ b/app-android/src/test/java/io/github/droidkaigi/confsched/PreviewTest.kt @@ -1,143 +1,50 @@ package io.github.droidkaigi.confsched import android.content.res.Configuration -import android.os.LocaleList -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.platform.LocalConfiguration -import com.airbnb.android.showkase.models.Showkase -import com.airbnb.android.showkase.models.ShowkaseBrowserComponent import com.github.takahirom.roborazzi.DEFAULT_ROBORAZZI_OUTPUT_DIR_PATH import com.github.takahirom.roborazzi.captureRoboImage -import io.github.droidkaigi.confsched.designsystem.preview.MultiLanguagePreviewDefinition -import io.github.droidkaigi.confsched.designsystem.preview.MultiThemePreviewDefinition -import io.github.droidkaigi.confsched.designsystem.preview.ShowkaseMultiplePreviewsWorkaround import org.junit.Test import org.junit.runner.RunWith import org.robolectric.ParameterizedRobolectricTestRunner -import java.util.Locale +import org.robolectric.RuntimeEnvironment +import sergio.sastre.composable.preview.scanner.android.AndroidComposablePreviewScanner +import sergio.sastre.composable.preview.scanner.android.AndroidPreviewInfo +import sergio.sastre.composable.preview.scanner.core.preview.ComposablePreview @RunWith(ParameterizedRobolectricTestRunner::class) class PreviewTest( - val showkaseBrowserComponent: ShowkaseBrowserComponent, + private val preview: ComposablePreview, ) { + object RobolectricPreviewInfosApplier { + fun applyFor(preview: ComposablePreview) { + val uiMode = + when (preview.previewInfo.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES) { + true -> "+night" + false -> "+notnight" + } + RuntimeEnvironment.setQualifiers(uiMode) + } + } @Test fun previewScreenshot() { val filePath = - DEFAULT_ROBORAZZI_OUTPUT_DIR_PATH + "/" + showkaseBrowserComponent.componentKey + ".png" + DEFAULT_ROBORAZZI_OUTPUT_DIR_PATH + "/" + preview.methodName + ".png" + RobolectricPreviewInfosApplier.applyFor(preview) captureRoboImage( - filePath, + filePath = filePath, ) { - ProvidesPreviewValues(group = showkaseBrowserComponent.group, componentKey = showkaseBrowserComponent.componentKey) { - showkaseBrowserComponent.component() - } - } - } - - @Suppress("TestFunctionName") - @ShowkaseMultiplePreviewsWorkaround - @Composable - private fun ProvidesPreviewValues(group: String, componentKey: String, content: @Composable () -> Unit) { - val appliers = arrayListOf<(Configuration) -> Unit>() - - if (isCustomGroup(group = group)) { - val previewValue = extractPreviewValues(group = group, componentKey) - - when (group) { - MultiLanguagePreviewDefinition.Group -> { - appliers += { c -> - c.setLocales(newLocales(baseLocales = c.locales, previewValue = previewValue)) - } - } - MultiThemePreviewDefinition.Group -> { - appliers += { c -> - c.uiMode = newUiMode(baseUiMode = c.uiMode, previewValue = previewValue) - } - } - } - } - - val newConfiguration = appliers.fold(LocalConfiguration.current) { c, a -> c.apply(a) } - - CompositionLocalProvider(LocalConfiguration provides newConfiguration) { - // Notify locale changes to lang() through the following invocation. - LocaleList.setDefault(LocalConfiguration.current.locales) - - content() - } - } - - /** - * Depends on the naming rule from Showkase. - * We must not include "_${group}_" in an original preview function name. - */ - @ShowkaseMultiplePreviewsWorkaround - private fun extractPreviewValues(group: String, componentKey: String): String { - val components = componentKey.split("_") - - // _${group_ is expected here - val groupIndex = requireNotNull(components.indexOf(group).takeIf { it > 0 }) { - "Failed to extract a preview value for $group: $group is not found in $components" - } - - val modifiedPreviewName = requireNotNull(components.getOrNull(groupIndex + 1)) { - "Failed to extract a preview value for $group: $components is unexpectedly aligned" - } - - // ${preview_name}_${preview_value}_${...others} - val match = requireNotNull(Regex("\\w+-([\\w-]+)-[_\\w]+").matchEntire(modifiedPreviewName)) { - "Failed to extract a preview value for $group: no value was found in $modifiedPreviewName" - } - - return requireNotNull(match.groupValues.getOrNull(1)) { - "Failed to extract a preview value for $group: this may be a development issue" + preview() } } - @ShowkaseMultiplePreviewsWorkaround - private fun newLocales(baseLocales: LocaleList, previewValue: String): LocaleList { - val locale = when (previewValue) { - MultiLanguagePreviewDefinition.English.Name -> { - MultiLanguagePreviewDefinition.English.Locale - } - MultiLanguagePreviewDefinition.Japanese.Name -> { - MultiLanguagePreviewDefinition.Japanese.Locale - } - else -> return baseLocales - } - - return LocaleList(Locale.getAvailableLocales().first { it.toString() == locale }) - } - - @ShowkaseMultiplePreviewsWorkaround - private fun newUiMode(baseUiMode: Int, previewValue: String): Int { - val nightMode = when (previewValue) { - // FIXME -// MultiThemePreviewDefinition.DarkMode.Name -> { -// MultiThemePreviewDefinition.DarkMode.UiMode -// } -// MultiThemePreviewDefinition.LightMode.Name -> { -// MultiThemePreviewDefinition.LightMode.UiMode -// } - else -> baseUiMode - } - - val currentNightMode = baseUiMode and Configuration.UI_MODE_NIGHT_MASK - return baseUiMode xor currentNightMode or nightMode - } - companion object { - fun isCustomGroup(group: String): Boolean { - return group != "Default Group" - } - @ParameterizedRobolectricTestRunner.Parameters @JvmStatic - fun components(): Iterable> { - return Showkase.getMetadata().componentList.map { showkaseBrowserComponent -> - arrayOf(showkaseBrowserComponent) - } + fun components(): List> { + return AndroidComposablePreviewScanner() + .scanPackageTrees("io.github.droidkaigi.confsched") + .getPreviews() } } } diff --git a/app-android/src/test/java/io/github/droidkaigi/confsched/ShowkaseRootModule.kt b/app-android/src/test/java/io/github/droidkaigi/confsched/ShowkaseRootModule.kt deleted file mode 100644 index a7373b1d7..000000000 --- a/app-android/src/test/java/io/github/droidkaigi/confsched/ShowkaseRootModule.kt +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.droidkaigi.confsched - -import com.airbnb.android.showkase.annotation.ShowkaseRoot -import com.airbnb.android.showkase.annotation.ShowkaseRootModule - -@ShowkaseRoot -class ShowkaseRootModule : ShowkaseRootModule diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index dad835a5b..5e3769751 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -95,10 +95,6 @@ gradlePlugin { id = "droidkaigi.primitive.kmp.android.hilt" implementationClass = "io.github.droidkaigi.confsched.primitive.KmpAndroidHiltPlugin" } - register("kotlinMppAndroidShowkase") { - id = "droidkaigi.primitive.kmp.android.showkase" - implementationClass = "io.github.droidkaigi.confsched.primitive.KmpAndroidShowkasePlugin" - } register("kotlinMppKotlinSerialization") { id = "droidkaigi.primitive.kmp.serialization" implementationClass = "io.github.droidkaigi.confsched.primitive.KotlinSerializationPlugin" diff --git a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/AndroidRoborazziPlugin.kt b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/AndroidRoborazziPlugin.kt index c90d48f06..b100fcb4d 100644 --- a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/AndroidRoborazziPlugin.kt +++ b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/AndroidRoborazziPlugin.kt @@ -31,9 +31,6 @@ class AndroidRoborazziPlugin : Plugin { testImplementation(libs.library("androidxTestExtJunit")) testImplementation(libs.library("roborazzi")) testImplementation(libs.library("roborazziCompose")) - // For preview screenshot tests - implementation(libs.library("showkaseRuntime")) - ksp(libs.library("showkaseProcessor")) } } } diff --git a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpAndroidShowkasePlugin.kt b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpAndroidShowkasePlugin.kt deleted file mode 100644 index 9b8350af9..000000000 --- a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpAndroidShowkasePlugin.kt +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.droidkaigi.confsched.primitive - -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.kotlin.dsl.dependencies - -@Suppress("unused") -class KmpAndroidShowkasePlugin : Plugin { - override fun apply(target: Project) { - with(target) { - with(pluginManager) { - apply(libs.plugin("kspGradlePlugin").pluginId) - } - kotlin { - sourceSets.getByName("androidMain") { - dependencies { - implementation(libs.library("showkaseRuntime")) - } - } - } - dependencies { - add("kspAndroid", libs.library("showkaseProcessor")) - } - } - } -} diff --git a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpRoborazziPlugin.kt b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpRoborazziPlugin.kt index 31cda28d2..a23810f7c 100644 --- a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpRoborazziPlugin.kt +++ b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpRoborazziPlugin.kt @@ -31,18 +31,7 @@ class KmpRoborazziPlugin : Plugin { kotlin { if (plugins.hasPlugin("com.android.library")) { sourceSets.getByName("androidUnitTest") { - val kspConfiguration = configurations["kspAndroid"] - kspConfiguration.dependencies.add( - libs.library("showkaseProcessor").let { - DefaultExternalModuleDependency( - it.module.group, - it.module.name, - it.versionConstraint.requiredVersion - ) - } - ) dependencies { - implementation(libs.library("showkaseRuntime")) implementation(libs.library("androidxTestEspressoEspressoCore")) implementation(libs.library("junit")) implementation(libs.library("robolectric")) diff --git a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KoverEntryPointPlugin.kt b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KoverEntryPointPlugin.kt index e46daf49e..cdcb975a8 100644 --- a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KoverEntryPointPlugin.kt +++ b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KoverEntryPointPlugin.kt @@ -21,7 +21,6 @@ class KoverEntryPointPlugin : Plugin { filters { excludes { packages( - "com.airbnb.android.showkase", "dagger.hilt.*", "hilt_aggregated_deps", ) diff --git a/core/designsystem/build.gradle.kts b/core/designsystem/build.gradle.kts index 4d51defd2..5d5cac859 100644 --- a/core/designsystem/build.gradle.kts +++ b/core/designsystem/build.gradle.kts @@ -5,7 +5,6 @@ plugins { id("droidkaigi.primitive.kmp.compose") id("droidkaigi.primitive.kmp.android.hilt") id("droidkaigi.primitive.detekt") - id("droidkaigi.primitive.kmp.android.showkase") } android.namespace = "io.github.droidkaigi.confsched.core.designsystem" diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index cde74e533..20e2e2b9d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -29,7 +29,6 @@ ktlint = "0.49.1" kotlinxSerialization = "1.6.3" ktor = "2.3.10" roborazzi = "1.20.0" -showkase = "1.0.0-beta18" ksp = "1.9.24-1.0.20" firebaseBom = "33.1.0" multiplatformFirebase = "1.8.1" @@ -45,6 +44,7 @@ molecule = "1.4.1" kover = "0.7.6" androidxLifecycleProcess = "2.8.2" skie = "0.8.2" +composablePreviewScanner = "0.1.1" [libraries] androidGradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } @@ -168,8 +168,7 @@ roborazzi = { module = "io.github.takahirom.roborazzi:roborazzi", version.ref = roborazziCompose = { module = "io.github.takahirom.roborazzi:roborazzi-compose", version.ref = "roborazzi" } roborazziIos = { module = "io.github.takahirom.roborazzi:roborazzi-compose-ios", version.ref = "roborazzi" } roborazziRule = { module = "io.github.takahirom.roborazzi:roborazzi-junit-rule", version.ref = "roborazzi" } -showkaseRuntime = { group = "com.airbnb.android", name = "showkase", version.ref = "showkase" } -showkaseProcessor = { group = "com.airbnb.android", name = "showkase-processor", version.ref = "showkase" } +composablePreviewScanner = { module = "com.github.sergio-sastre.ComposablePreviewScanner:android", version.ref = "composablePreviewScanner" } [plugins] androidGradlePlugin = { id = "com.android.application", version.ref = "androidGradlePlugin" } diff --git a/settings.gradle.kts b/settings.gradle.kts index ad3779140..5196bcff9 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -15,6 +15,7 @@ dependencyResolutionManagement { maven { url = uri("https://maven.pkg.jetbrains.space/public/p/compose/dev/") } + maven { url = uri("https://jitpack.io") } } } rootProject.name = "conference-app-2024" From 3b826a596b0e38ae2436eb2693bd5e596dfa9ea5 Mon Sep 17 00:00:00 2001 From: takahirom Date: Wed, 19 Jun 2024 10:04:35 +0900 Subject: [PATCH 2/2] Fix theme --- app-android/src/main/res/values-night/splash_theme.xml | 10 ---------- app-android/src/main/res/values/splash_theme.xml | 1 + 2 files changed, 1 insertion(+), 10 deletions(-) delete mode 100644 app-android/src/main/res/values-night/splash_theme.xml diff --git a/app-android/src/main/res/values-night/splash_theme.xml b/app-android/src/main/res/values-night/splash_theme.xml deleted file mode 100644 index 35fafb677..000000000 --- a/app-android/src/main/res/values-night/splash_theme.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - diff --git a/app-android/src/main/res/values/splash_theme.xml b/app-android/src/main/res/values/splash_theme.xml index dc3334cee..677c84f09 100644 --- a/app-android/src/main/res/values/splash_theme.xml +++ b/app-android/src/main/res/values/splash_theme.xml @@ -5,6 +5,7 @@ @drawable/splash_icon @android:color/white 5000 + @style/Theme.KaigiApp @drawable/splash_branding_image