Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor RoborazziComposeConfigBuilder #590

Merged
merged 1 commit into from
Dec 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
.idea/modules
/.intellijPlatform/
.idea/runConfigurations.xml
.idea/shelf
*.iml
*.ipr
.DS_Store
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,49 +13,52 @@ import sergio.sastre.composable.preview.scanner.core.preview.ComposablePreview
fun ComposablePreview<AndroidPreviewInfo>.captureRoboImage(
filePath: String,
roborazziOptions: RoborazziOptions = provideRoborazziContext().options,
configBuilder: RoborazziComposeConfigBuilder = this.toRoborazziComposeConfigBuilder()
roborazziComposeOptions: RoborazziComposeOptions = this.toRoborazziComposeOptions()
Copy link
Owner Author

@takahirom takahirom Dec 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sergio-sastre
Thank you for reviewing PRs. I was almost ready to release this, but while writing the release notes, I realized it might be a bit strange to pass a builder to the APIs. Additionally, the names RoborazziComposeConfig and RoborazziComposeConfigBuilder felt odd because RoborazziComposeConfig is not actually created by RoborazziComposeConfigBuilder.

I also think we don’t have a strong preference for using Config over Options, especially since we’re already using RoborazziOptions. Therefore, I renamed it to RoborazziComposeOptions.

I made some adjustments. Could you take a look?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sergio-sastre This is going to block other tasks, so I'll merge this now. If you have any concerns, let me know. I think we can address them in a patch release.

) {
if (!roborazziOptions.taskType.isEnabled()) return
val composablePreview = this
captureRoboImage(filePath, roborazziOptions, configBuilder) {
captureRoboImage(filePath, roborazziOptions, roborazziComposeOptions) {
composablePreview()
}
}

@ExperimentalRoborazziApi
fun ComposablePreview<AndroidPreviewInfo>.toRoborazziComposeConfigBuilder() =
RoborazziComposeConfigBuilder()
.size(
fun ComposablePreview<AndroidPreviewInfo>.toRoborazziComposeOptions(): RoborazziComposeOptions {
return RoborazziComposeOptions {
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<AndroidPreviewInfo>.captureRoboImage() instead",
replaceWith = ReplaceWith("previewInfo.toRoborazziComposeConfigBuilder().apply(scenario, composeContent)"),
message = "Use previewInfo.toRoborazziComposeOptions().configured(scenario, composeContent) or ComposablePreview<AndroidPreviewInfo>.captureRoboImage() instead",
replaceWith = ReplaceWith("previewInfo.toRoborazziComposeOptions().configured(scenario, composeContent)"),
level = DeprecationLevel.ERROR
)
fun ComposablePreview<AndroidPreviewInfo>.applyToRobolectricConfiguration() {
throw UnsupportedOperationException("Use previewInfo.toRoborazziComposeConfigBuilder().apply(scenario, composeContent) or ComposablePreview<AndroidPreviewInfo>.captureRoboImage() instead")
throw UnsupportedOperationException("Use previewInfo.toRoborazziComposeOptions().configured(scenario, composeContent) or ComposablePreview<AndroidPreviewInfo>.captureRoboImage() instead")
}

@ExperimentalRoborazziApi
fun RoborazziComposeConfigBuilder.previewDevice(previewDevice: String) =
with(RoborazziComposePreviewDeviceConfig(previewDevice))
fun RoborazziComposeOptions.Builder.previewDevice(previewDevice: String): RoborazziComposeOptions.Builder {
return addOption(RoborazziComposePreviewDeviceOption(previewDevice))
}

@ExperimentalRoborazziApi
data class RoborazziComposePreviewDeviceConfig(private val previewDevice: String) :
RoborazziComposeSetupConfig {
data class RoborazziComposePreviewDeviceOption(private val previewDevice: String) :
RoborazziComposeSetupOption {
override fun configure() {
if (previewDevice.isNotBlank()) {
// Requires `io.github.sergio-sastre.ComposablePreviewScanner:android:0.4.0` or later
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ fun captureRoboImage(
captureRoboImage(
file = file,
roborazziOptions = roborazziOptions,
configBuilder = RoborazziComposeConfigBuilder(),
roborazziComposeOptions = RoborazziComposeOptions(),
content = content
)
}
Expand All @@ -39,13 +39,13 @@ fun captureRoboImage(
fun captureRoboImage(
filePath: String = DefaultFileNameGenerator.generateFilePath(),
roborazziOptions: RoborazziOptions = provideRoborazziContext().options,
configBuilder: RoborazziComposeConfigBuilder = RoborazziComposeConfigBuilder(),
roborazziComposeOptions: RoborazziComposeOptions = RoborazziComposeOptions(),
content: @Composable () -> Unit,
) {
captureRoboImage(
file = fileWithRecordFilePathStrategy(filePath),
roborazziOptions = roborazziOptions,
configBuilder = configBuilder,
roborazziComposeOptions = roborazziComposeOptions,
content = content
)
}
Expand All @@ -54,13 +54,13 @@ fun captureRoboImage(
fun captureRoboImage(
file: File,
roborazziOptions: RoborazziOptions = provideRoborazziContext().options,
configBuilder: RoborazziComposeConfigBuilder = RoborazziComposeConfigBuilder(),
roborazziComposeOptions: RoborazziComposeOptions = RoborazziComposeOptions(),
content: @Composable () -> Unit,
) {
if (!roborazziOptions.taskType.isEnabled()) return
launchRoborazziTransparentActivity { activityScenario ->
val configuredContent = configBuilder
.configure(activityScenario) {
val configuredContent = roborazziComposeOptions
.configured(activityScenario) {
content()
}
activityScenario.captureRoboImage(file, roborazziOptions) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,72 +18,101 @@ import org.robolectric.shadows.ShadowDisplay.getDefaultDisplay
import kotlin.math.roundToInt

@ExperimentalRoborazziApi
interface RoborazziComposeConfig
interface RoborazziComposeOption

@ExperimentalRoborazziApi
interface RoborazziComposeSetupConfig : RoborazziComposeConfig {
interface RoborazziComposeSetupOption : RoborazziComposeOption {
fun configure()
}

@ExperimentalRoborazziApi
interface RoborazziComposeActivityScenarioConfig : RoborazziComposeConfig {
interface RoborazziComposeActivityScenarioOption : RoborazziComposeOption {
fun configureWithActivityScenario(scenario: ActivityScenario<out Activity>)
}

@ExperimentalRoborazziApi
interface RoborazziComposeComposableConfig : RoborazziComposeConfig {
interface RoborazziComposeComposableOption : RoborazziComposeOption {
fun configureWithComposable(content: @Composable () -> Unit): @Composable () -> Unit
}

@ExperimentalRoborazziApi
class RoborazziComposeConfigBuilder {
private val activityScenarioConfigs =
mutableListOf<RoborazziComposeActivityScenarioConfig>()
private val composableConfigs = mutableListOf<RoborazziComposeComposableConfig>()
private val setupConfigs = mutableListOf<RoborazziComposeSetupConfig>()

fun with(config: RoborazziComposeConfig): RoborazziComposeConfigBuilder {
if (config is RoborazziComposeActivityScenarioConfig) {
activityScenarioConfigs.add(config)
}
if (config is RoborazziComposeComposableConfig) {
composableConfigs.add(config)
class RoborazziComposeOptions private constructor(
private val activityScenarioOptions: List<RoborazziComposeActivityScenarioOption>,
private val composableOptions: List<RoborazziComposeComposableOption>,
private val setupOptions: List<RoborazziComposeSetupOption>
) {
class Builder {
private val activityScenarioOptions =
mutableListOf<RoborazziComposeActivityScenarioOption>()
private val composableOptions = mutableListOf<RoborazziComposeComposableOption>()
private val setupOptions = mutableListOf<RoborazziComposeSetupOption>()

fun addOption(option: RoborazziComposeOption): Builder {
if (option is RoborazziComposeActivityScenarioOption) {
activityScenarioOptions.add(option)
}
if (option is RoborazziComposeComposableOption) {
composableOptions.add(option)
}
if (option is RoborazziComposeSetupOption) {
setupOptions.add(option)
}
return this
}
if (config is RoborazziComposeSetupConfig) {
setupConfigs.add(config)

fun build(): RoborazziComposeOptions {
return RoborazziComposeOptions(
activityScenarioOptions = activityScenarioOptions,
composableOptions = composableOptions,
setupOptions = setupOptions
)
}
return this
}

fun builder(): Builder {
return Builder()
.apply {
activityScenarioOptions.forEach { addOption(it) }
composableOptions.forEach { addOption(it) }
setupOptions.forEach { addOption(it) }
}
}

@ExperimentalRoborazziApi
fun configure(
scenario: ActivityScenario<out Activity>,
fun configured(
activityScenario: ActivityScenario<out Activity>,
content: @Composable () -> Unit
): @Composable () -> Unit {
setupConfigs.forEach { it.configure() }
activityScenarioConfigs.forEach { it.configureWithActivityScenario(scenario) }
setupOptions.forEach { it.configure() }
activityScenarioOptions.forEach { it.configureWithActivityScenario(activityScenario) }
var appliedContent = content
composableConfigs.forEach { config ->
composableOptions.forEach { config ->
appliedContent = config.configureWithComposable(appliedContent)
}
return {
appliedContent()
}
}

companion object {
operator fun invoke(block: Builder.() -> Unit = {}): RoborazziComposeOptions {
return Builder().apply(block).build()
}
}
}

@ExperimentalRoborazziApi
fun RoborazziComposeConfigBuilder.size(
fun RoborazziComposeOptions.Builder.size(
widthDp: Int = 0,
heightDp: Int = 0
): RoborazziComposeConfigBuilder {
return with(RoborazziComposeSizeConfig(widthDp, heightDp))
): RoborazziComposeOptions.Builder {
return addOption(RoborazziComposeSizeOption(widthDp, heightDp))
}

@ExperimentalRoborazziApi
data class RoborazziComposeSizeConfig(val widthDp: Int, val heightDp: Int) :
RoborazziComposeActivityScenarioConfig,
RoborazziComposeComposableConfig {
data class RoborazziComposeSizeOption(val widthDp: Int, val heightDp: Int) :
RoborazziComposeActivityScenarioOption,
RoborazziComposeComposableOption {
override fun configureWithActivityScenario(scenario: ActivityScenario<out Activity>) {
scenario.onActivity { activity ->
activity.setDisplaySize(widthDp = widthDp, heightDp = heightDp)
Expand Down Expand Up @@ -130,18 +159,18 @@ data class RoborazziComposeSizeConfig(val widthDp: Int, val heightDp: Int) :
}

@ExperimentalRoborazziApi
fun RoborazziComposeConfigBuilder.background(
fun RoborazziComposeOptions.Builder.background(
showBackground: Boolean,
backgroundColor: Long = 0L
): RoborazziComposeConfigBuilder {
return with(RoborazziComposeBackgroundConfig(showBackground, backgroundColor))
): RoborazziComposeOptions.Builder {
return addOption(RoborazziComposeBackgroundOption(showBackground, backgroundColor))
}

@ExperimentalRoborazziApi
data class RoborazziComposeBackgroundConfig(
data class RoborazziComposeBackgroundOption(
private val showBackground: Boolean,
private val backgroundColor: Long
) : RoborazziComposeActivityScenarioConfig {
) : RoborazziComposeActivityScenarioOption {
override fun configureWithActivityScenario(scenario: ActivityScenario<out Activity>) {
when (showBackground) {
false -> {
Expand All @@ -164,13 +193,13 @@ data class RoborazziComposeBackgroundConfig(
}

@ExperimentalRoborazziApi
fun RoborazziComposeConfigBuilder.uiMode(uiMode: Int): RoborazziComposeConfigBuilder {
return with(RoborazziComposeUiModeConfig(uiMode))
fun RoborazziComposeOptions.Builder.uiMode(uiMode: Int): RoborazziComposeOptions.Builder {
return addOption(RoborazziComposeUiModeOption(uiMode))
}

@ExperimentalRoborazziApi
data class RoborazziComposeUiModeConfig(private val uiMode: Int) :
RoborazziComposeSetupConfig {
data class RoborazziComposeUiModeOption(private val uiMode: Int) :
RoborazziComposeSetupOption {
override fun configure() {
val nightMode =
when (uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES) {
Expand All @@ -182,30 +211,31 @@ data class RoborazziComposeUiModeConfig(private val uiMode: Int) :
}

@ExperimentalRoborazziApi
fun RoborazziComposeConfigBuilder.locale(locale: String): RoborazziComposeConfigBuilder {
return with(RoborazziComposeLocaleConfig(locale))
fun RoborazziComposeOptions.Builder.locale(locale: String): RoborazziComposeOptions.Builder {
return addOption(RoborazziComposeLocaleOption(locale))
}

@ExperimentalRoborazziApi
data class RoborazziComposeLocaleConfig(private val locale: String) :
RoborazziComposeSetupConfig {
data class RoborazziComposeLocaleOption(private val locale: String) :
RoborazziComposeSetupOption {
override fun configure() {
val localeWithFallback = locale.ifBlank { "en" }
setQualifiers("+$localeWithFallback")
}
}

@ExperimentalRoborazziApi
fun RoborazziComposeConfigBuilder.fontScale(fontScale: Float): RoborazziComposeConfigBuilder {
return with(RoborazziComposeFontScaleConfig(fontScale))
fun RoborazziComposeOptions.Builder.fontScale(fontScale: Float): RoborazziComposeOptions.Builder {
return addOption(RoborazziComposeFontScaleOption(fontScale))
}

@ExperimentalRoborazziApi
data class RoborazziComposeFontScaleConfig(private val fontScale: Float) :
RoborazziComposeSetupConfig {
data class RoborazziComposeFontScaleOption(private val fontScale: Float) :
RoborazziComposeSetupOption {
init {
require(fontScale > 0) { "fontScale must be greater than 0" }
}

override fun configure() {
setFontScale(fontScale)
}
Expand Down
Loading
Loading