diff --git a/.gitignore b/.gitignore index ea4a4521..95d393ff 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ .idea/modules /.intellijPlatform/ .idea/runConfigurations.xml +.idea/shelf *.iml *.ipr .DS_Store diff --git a/roborazzi-compose-preview-scanner-support/src/main/java/com/github/takahirom/roborazzi/RoborazziPreviewScannerSupport.kt b/roborazzi-compose-preview-scanner-support/src/main/java/com/github/takahirom/roborazzi/RoborazziPreviewScannerSupport.kt index f7128999..ba2011bb 100644 --- a/roborazzi-compose-preview-scanner-support/src/main/java/com/github/takahirom/roborazzi/RoborazziPreviewScannerSupport.kt +++ b/roborazzi-compose-preview-scanner-support/src/main/java/com/github/takahirom/roborazzi/RoborazziPreviewScannerSupport.kt @@ -13,49 +13,52 @@ import sergio.sastre.composable.preview.scanner.core.preview.ComposablePreview fun ComposablePreview.captureRoboImage( filePath: String, roborazziOptions: RoborazziOptions = provideRoborazziContext().options, - configBuilder: RoborazziComposeConfigBuilder = this.toRoborazziComposeConfigBuilder() + roborazziComposeConfig: RoborazziComposeConfig = this.toRoborazziComposeConfig() ) { if (!roborazziOptions.taskType.isEnabled()) return val composablePreview = this - captureRoboImage(filePath, roborazziOptions, configBuilder) { + captureRoboImage(filePath, roborazziOptions, roborazziComposeConfig) { composablePreview() } } @ExperimentalRoborazziApi -fun ComposablePreview.toRoborazziComposeConfigBuilder() = - RoborazziComposeConfigBuilder() - .size( +fun ComposablePreview.toRoborazziComposeConfig(): RoborazziComposeConfig { + return RoborazziComposeConfig { + size( widthDp = previewInfo.widthDp, heightDp = previewInfo.heightDp ) - .background( + background( showBackground = previewInfo.showBackground, backgroundColor = previewInfo.backgroundColor ) - .locale(previewInfo.locale) - .uiMode(previewInfo.uiMode) - .previewDevice(previewInfo.device) - .fontScale(previewInfo.fontScale) + locale(previewInfo.locale) + uiMode(previewInfo.uiMode) + previewDevice(previewInfo.device) + fontScale(previewInfo.fontScale) + } +} @Suppress("UnusedReceiverParameter") @Deprecated( - message = "Use previewInfo.toRoborazziComposeConfigBuilder().apply(scenario, composeContent) or ComposablePreview.captureRoboImage() instead", - replaceWith = ReplaceWith("previewInfo.toRoborazziComposeConfigBuilder().apply(scenario, composeContent)"), + message = "Use previewInfo.toRoborazziComposeConfig().apply(scenario, composeContent) or ComposablePreview.captureRoboImage() instead", + replaceWith = ReplaceWith("previewInfo.toRoborazziComposeConfig().apply(scenario, composeContent)"), level = DeprecationLevel.ERROR ) fun ComposablePreview.applyToRobolectricConfiguration() { - throw UnsupportedOperationException("Use previewInfo.toRoborazziComposeConfigBuilder().apply(scenario, composeContent) or ComposablePreview.captureRoboImage() instead") + throw UnsupportedOperationException("Use previewInfo.toRoborazziComposeConfig().apply(scenario, composeContent) or ComposablePreview.captureRoboImage() instead") } @ExperimentalRoborazziApi -fun RoborazziComposeConfigBuilder.previewDevice(previewDevice: String) = - with(RoborazziComposePreviewDeviceConfig(previewDevice)) +fun RoborazziComposeConfig.Builder.previewDevice(previewDevice: String): RoborazziComposeConfig.Builder { + return add(RoborazziComposePreviewDeviceConfigurator(previewDevice)) +} @ExperimentalRoborazziApi -data class RoborazziComposePreviewDeviceConfig(private val previewDevice: String) : - RoborazziComposeSetupConfig { +data class RoborazziComposePreviewDeviceConfigurator(private val previewDevice: String) : + RoborazziComposeSetupConfigurator { override fun configure() { if (previewDevice.isNotBlank()) { // Requires `io.github.sergio-sastre.ComposablePreviewScanner:android:0.4.0` or later diff --git a/roborazzi-compose/src/main/java/com/github/takahirom/roborazzi/RoborazziCompose.kt b/roborazzi-compose/src/main/java/com/github/takahirom/roborazzi/RoborazziCompose.kt index b3a1f4ff..f340d138 100644 --- a/roborazzi-compose/src/main/java/com/github/takahirom/roborazzi/RoborazziCompose.kt +++ b/roborazzi-compose/src/main/java/com/github/takahirom/roborazzi/RoborazziCompose.kt @@ -30,7 +30,7 @@ fun captureRoboImage( captureRoboImage( file = file, roborazziOptions = roborazziOptions, - configBuilder = RoborazziComposeConfigBuilder(), + roborazziComposeConfig = RoborazziComposeConfig {}, content = content ) } @@ -39,13 +39,13 @@ fun captureRoboImage( fun captureRoboImage( filePath: String = DefaultFileNameGenerator.generateFilePath(), roborazziOptions: RoborazziOptions = provideRoborazziContext().options, - configBuilder: RoborazziComposeConfigBuilder = RoborazziComposeConfigBuilder(), + roborazziComposeConfig: RoborazziComposeConfig = RoborazziComposeConfig {}, content: @Composable () -> Unit, ) { captureRoboImage( file = fileWithRecordFilePathStrategy(filePath), roborazziOptions = roborazziOptions, - configBuilder = configBuilder, + roborazziComposeConfig = roborazziComposeConfig, content = content ) } @@ -54,13 +54,13 @@ fun captureRoboImage( fun captureRoboImage( file: File, roborazziOptions: RoborazziOptions = provideRoborazziContext().options, - configBuilder: RoborazziComposeConfigBuilder = RoborazziComposeConfigBuilder(), + roborazziComposeConfig: RoborazziComposeConfig = RoborazziComposeConfig {}, content: @Composable () -> Unit, ) { if (!roborazziOptions.taskType.isEnabled()) return launchRoborazziTransparentActivity { activityScenario -> - val configuredContent = configBuilder - .configure(activityScenario) { + val configuredContent = roborazziComposeConfig + .configured(activityScenario) { content() } activityScenario.captureRoboImage(file, roborazziOptions) { diff --git a/roborazzi-compose/src/main/java/com/github/takahirom/roborazzi/RoborazziComposeConfig.kt b/roborazzi-compose/src/main/java/com/github/takahirom/roborazzi/RoborazziComposeConfig.kt index 5feed029..162b5a5b 100644 --- a/roborazzi-compose/src/main/java/com/github/takahirom/roborazzi/RoborazziComposeConfig.kt +++ b/roborazzi-compose/src/main/java/com/github/takahirom/roborazzi/RoborazziComposeConfig.kt @@ -18,72 +18,101 @@ import org.robolectric.shadows.ShadowDisplay.getDefaultDisplay import kotlin.math.roundToInt @ExperimentalRoborazziApi -interface RoborazziComposeConfig +interface RoborazziComposeConfigurator @ExperimentalRoborazziApi -interface RoborazziComposeSetupConfig : RoborazziComposeConfig { +interface RoborazziComposeSetupConfigurator : RoborazziComposeConfigurator { fun configure() } @ExperimentalRoborazziApi -interface RoborazziComposeActivityScenarioConfig : RoborazziComposeConfig { +interface RoborazziComposeActivityScenarioConfigurator : RoborazziComposeConfigurator { fun configureWithActivityScenario(scenario: ActivityScenario) } @ExperimentalRoborazziApi -interface RoborazziComposeComposableConfig : RoborazziComposeConfig { +interface RoborazziComposeComposableConfigurator : RoborazziComposeConfigurator { fun configureWithComposable(content: @Composable () -> Unit): @Composable () -> Unit } @ExperimentalRoborazziApi -class RoborazziComposeConfigBuilder { - private val activityScenarioConfigs = - mutableListOf() - private val composableConfigs = mutableListOf() - private val setupConfigs = mutableListOf() - - fun with(config: RoborazziComposeConfig): RoborazziComposeConfigBuilder { - if (config is RoborazziComposeActivityScenarioConfig) { - activityScenarioConfigs.add(config) - } - if (config is RoborazziComposeComposableConfig) { - composableConfigs.add(config) +class RoborazziComposeConfig private constructor( + private val activityScenarioConfigurators: List, + private val composableConfigurators: List, + private val setupConfigurators: List +) { + class Builder { + private val activityScenarioConfigurators = + mutableListOf() + private val composableConfigurators = mutableListOf() + private val setupConfigurators = mutableListOf() + + fun add(configurator: RoborazziComposeConfigurator): Builder { + if (configurator is RoborazziComposeActivityScenarioConfigurator) { + activityScenarioConfigurators.add(configurator) + } + if (configurator is RoborazziComposeComposableConfigurator) { + composableConfigurators.add(configurator) + } + if (configurator is RoborazziComposeSetupConfigurator) { + setupConfigurators.add(configurator) + } + return this } - if (config is RoborazziComposeSetupConfig) { - setupConfigs.add(config) + + fun build(): RoborazziComposeConfig { + return RoborazziComposeConfig( + activityScenarioConfigurators = activityScenarioConfigurators, + composableConfigurators = composableConfigurators, + setupConfigurators = setupConfigurators + ) } - return this + } + + fun builder(): Builder { + return Builder() + .apply { + activityScenarioConfigurators.forEach { add(it) } + composableConfigurators.forEach { add(it) } + setupConfigurators.forEach { add(it) } + } } @ExperimentalRoborazziApi - fun configure( - scenario: ActivityScenario, + fun configured( + activityScenario: ActivityScenario, content: @Composable () -> Unit ): @Composable () -> Unit { - setupConfigs.forEach { it.configure() } - activityScenarioConfigs.forEach { it.configureWithActivityScenario(scenario) } + setupConfigurators.forEach { it.configure() } + activityScenarioConfigurators.forEach { it.configureWithActivityScenario(activityScenario) } var appliedContent = content - composableConfigs.forEach { config -> + composableConfigurators.forEach { config -> appliedContent = config.configureWithComposable(appliedContent) } return { appliedContent() } } + + companion object { + operator fun invoke(block: Builder.() -> Unit): RoborazziComposeConfig { + return Builder().apply(block).build() + } + } } @ExperimentalRoborazziApi -fun RoborazziComposeConfigBuilder.size( +fun RoborazziComposeConfig.Builder.size( widthDp: Int = 0, heightDp: Int = 0 -): RoborazziComposeConfigBuilder { - return with(RoborazziComposeSizeConfig(widthDp, heightDp)) +): RoborazziComposeConfig.Builder { + return add(RoborazziComposeSizeConfigurator(widthDp, heightDp)) } @ExperimentalRoborazziApi -data class RoborazziComposeSizeConfig(val widthDp: Int, val heightDp: Int) : - RoborazziComposeActivityScenarioConfig, - RoborazziComposeComposableConfig { +data class RoborazziComposeSizeConfigurator(val widthDp: Int, val heightDp: Int) : + RoborazziComposeActivityScenarioConfigurator, + RoborazziComposeComposableConfigurator { override fun configureWithActivityScenario(scenario: ActivityScenario) { scenario.onActivity { activity -> activity.setDisplaySize(widthDp = widthDp, heightDp = heightDp) @@ -130,18 +159,18 @@ data class RoborazziComposeSizeConfig(val widthDp: Int, val heightDp: Int) : } @ExperimentalRoborazziApi -fun RoborazziComposeConfigBuilder.background( +fun RoborazziComposeConfig.Builder.background( showBackground: Boolean, backgroundColor: Long = 0L -): RoborazziComposeConfigBuilder { - return with(RoborazziComposeBackgroundConfig(showBackground, backgroundColor)) +): RoborazziComposeConfig.Builder { + return add(RoborazziComposeBackgroundConfigurator(showBackground, backgroundColor)) } @ExperimentalRoborazziApi -data class RoborazziComposeBackgroundConfig( +data class RoborazziComposeBackgroundConfigurator( private val showBackground: Boolean, private val backgroundColor: Long -) : RoborazziComposeActivityScenarioConfig { +) : RoborazziComposeActivityScenarioConfigurator { override fun configureWithActivityScenario(scenario: ActivityScenario) { when (showBackground) { false -> { @@ -164,13 +193,13 @@ data class RoborazziComposeBackgroundConfig( } @ExperimentalRoborazziApi -fun RoborazziComposeConfigBuilder.uiMode(uiMode: Int): RoborazziComposeConfigBuilder { - return with(RoborazziComposeUiModeConfig(uiMode)) +fun RoborazziComposeConfig.Builder.uiMode(uiMode: Int): RoborazziComposeConfig.Builder { + return add(RoborazziComposeUiModeConfigurator(uiMode)) } @ExperimentalRoborazziApi -data class RoborazziComposeUiModeConfig(private val uiMode: Int) : - RoborazziComposeSetupConfig { +data class RoborazziComposeUiModeConfigurator(private val uiMode: Int) : + RoborazziComposeSetupConfigurator { override fun configure() { val nightMode = when (uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES) { @@ -182,13 +211,13 @@ data class RoborazziComposeUiModeConfig(private val uiMode: Int) : } @ExperimentalRoborazziApi -fun RoborazziComposeConfigBuilder.locale(locale: String): RoborazziComposeConfigBuilder { - return with(RoborazziComposeLocaleConfig(locale)) +fun RoborazziComposeConfig.Builder.locale(locale: String): RoborazziComposeConfig.Builder { + return add(RoborazziComposeLocaleConfigurator(locale)) } @ExperimentalRoborazziApi -data class RoborazziComposeLocaleConfig(private val locale: String) : - RoborazziComposeSetupConfig { +data class RoborazziComposeLocaleConfigurator(private val locale: String) : + RoborazziComposeSetupConfigurator { override fun configure() { val localeWithFallback = locale.ifBlank { "en" } setQualifiers("+$localeWithFallback") @@ -196,16 +225,17 @@ data class RoborazziComposeLocaleConfig(private val locale: String) : } @ExperimentalRoborazziApi -fun RoborazziComposeConfigBuilder.fontScale(fontScale: Float): RoborazziComposeConfigBuilder { - return with(RoborazziComposeFontScaleConfig(fontScale)) +fun RoborazziComposeConfig.Builder.fontScale(fontScale: Float): RoborazziComposeConfig.Builder { + return add(RoborazziComposeFontScaleConfigurator(fontScale)) } @ExperimentalRoborazziApi -data class RoborazziComposeFontScaleConfig(private val fontScale: Float) : - RoborazziComposeSetupConfig { +data class RoborazziComposeFontScaleConfigurator(private val fontScale: Float) : + RoborazziComposeSetupConfigurator { init { require(fontScale > 0) { "fontScale must be greater than 0" } } + override fun configure() { setFontScale(fontScale) }