feat(screenshots): Set up localized Play Store screenshot pipeline

- Add Compose screenshot test infrastructure (build config, screenshotTest source set)

- Add 7 localized screenshots covering all key app screens

- Device spec set to Pixel 8 (1080x2400, 428dpi); batch size 4 for 1080p memory budget

- Add screen title heading and fix appearance card header layout in WidgetConfigurationScreen

- Balance padding on requires-Pro notice in widget config bottom bar
This commit is contained in:
darken
2026-02-25 22:57:34 +01:00
committed by Matthias Urhahn
parent c4e58917da
commit fc9bcafae9
21 changed files with 973 additions and 60 deletions
@@ -0,0 +1,355 @@
package eu.darken.capod.screenshots
import android.content.res.Configuration
import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import eu.darken.capod.R
import eu.darken.capod.common.compose.PreviewWrapper
import eu.darken.capod.main.ui.widget.WidgetConfigurationScreen
import eu.darken.capod.main.ui.widget.WidgetConfigurationViewModel
import eu.darken.capod.main.ui.widget.WidgetTheme
import eu.darken.capod.common.compose.preview.MOCK_NOW
import eu.darken.capod.common.compose.preview.MockPodDataProvider
import eu.darken.capod.common.theming.CapodTheme
import eu.darken.capod.main.ui.overview.OverviewScreen
import eu.darken.capod.main.ui.overview.OverviewViewModel
import eu.darken.capod.main.ui.settings.SettingsScreen
import eu.darken.capod.main.ui.settings.SettingsViewModel
import eu.darken.capod.pods.core.PodDevice
import eu.darken.capod.profiles.ui.DeviceManagerScreen
import eu.darken.capod.profiles.ui.DeviceManagerViewModel
import eu.darken.capod.profiles.ui.creation.DeviceProfileCreationScreen
import eu.darken.capod.profiles.ui.creation.DeviceProfileCreationViewModel
import eu.darken.capod.reaction.core.autoconnect.AutoConnectCondition
import eu.darken.capod.reaction.ui.ReactionSettingsScreen
import eu.darken.capod.reaction.ui.ReactionSettingsViewModel
internal const val DS = "spec:width=1080px,height=2400px,dpi=428"
@Composable
internal fun DashboardContent() = PreviewWrapper {
OverviewScreen(
state = OverviewViewModel.State(
now = MOCK_NOW,
permissions = emptySet(),
devices = listOf(
MockPodDataProvider.airPodsProMixed(),
MockPodDataProvider.airPodsMax(),
MockPodDataProvider.unknownDevice(),
),
isDebugMode = false,
isBluetoothEnabled = true,
profiles = listOf(
MockPodDataProvider.profile("My AirPods Pro", PodDevice.Model.AIRPODS_PRO2),
MockPodDataProvider.profile("AirPods Max", PodDevice.Model.AIRPODS_MAX),
),
upgradeInfo = MockPodDataProvider.gplayInfo(isPro = true),
showUnmatchedDevices = false,
),
onRequestPermission = {},
onManageDevices = {},
onSettings = {},
onUpgrade = {},
onToggleUnmatched = {},
)
}
@Composable
internal fun DeviceProfilesContent() = PreviewWrapper {
DeviceManagerScreen(
state = DeviceManagerViewModel.State(
profiles = listOf(
MockPodDataProvider.profile("Work AirPods", PodDevice.Model.AIRPODS_PRO2),
MockPodDataProvider.profile("AirPods Max", PodDevice.Model.AIRPODS_MAX),
MockPodDataProvider.profile("Gym Beats", PodDevice.Model.POWERBEATS_PRO),
),
),
onBack = {},
onAddDevice = {},
onEditProfile = {},
)
}
@Composable
internal fun AddProfileContent() = PreviewWrapper {
DeviceProfileCreationScreen(
state = DeviceProfileCreationViewModel.State(
isEditMode = false,
name = "",
nameError = null,
selectedModel = null,
availableModels = PodDevice.Model.entries.filter { it != PodDevice.Model.UNKNOWN },
identityKey = null,
encryptionKey = null,
selectedDevice = null,
bondedDevices = emptyList(),
minimumSignalQuality = 0.15f,
canSave = false,
),
onBack = {},
onSave = {},
onDelete = {},
onNameChange = {},
onModelChange = {},
onDeviceChange = {},
onIdentityKeyChange = {},
onEncryptionKeyChange = {},
onSignalQualityChange = {},
onKeyGuide = {},
)
}
@Composable
internal fun SettingsIndexContent() = PreviewWrapper {
SettingsScreen(
state = SettingsViewModel.State(sponsorUrl = "https://example.com"),
onNavigateUp = {},
onGeneralSettings = {},
onDeviceManager = {},
onReactions = {},
onSupport = {},
onChangelog = {},
onHelpTranslate = {},
onAcknowledgements = {},
onPrivacyPolicy = {},
onSponsor = {},
)
}
@Composable
internal fun ReactionSettingsContent() = PreviewWrapper {
ReactionSettingsScreen(
state = ReactionSettingsViewModel.State(
isPro = true,
onePodMode = false,
autoPlay = true,
autoPause = true,
autoConnect = false,
autoConnectCondition = AutoConnectCondition.WHEN_SEEN,
showPopUpOnCaseOpen = true,
showPopUpOnConnection = false,
),
onNavigateUp = {},
onOnePodModeChanged = {},
onAutoPlayChanged = {},
onAutoPauseChanged = {},
onAutoConnectChanged = {},
onAutoConnectConditionSelected = {},
onShowPopUpOnCaseOpenChanged = {},
onShowPopUpOnConnectionChanged = {},
)
}
@Composable
internal fun WidgetConfigurationContent() = PreviewWrapper {
val profiles = listOf(
MockPodDataProvider.profile("My AirPods Pro", PodDevice.Model.AIRPODS_PRO2),
MockPodDataProvider.profile("AirPods Max", PodDevice.Model.AIRPODS_MAX),
)
WidgetConfigurationScreen(
state = WidgetConfigurationViewModel.State(
profiles = profiles,
selectedProfile = profiles.first().id,
isPro = true,
theme = WidgetTheme.DEFAULT,
activePreset = WidgetTheme.Preset.MATERIAL_YOU,
isCustomMode = false,
),
onSelectProfile = {},
onSelectPreset = {},
onEnterCustomMode = { _, _ -> },
onSetBackgroundColor = {},
onSetForegroundColor = {},
onSetBackgroundAlpha = {},
onSetShowDeviceLabel = {},
onReset = {},
onConfirm = {},
onCancel = {},
)
}
@Composable
internal fun HomescreenWidgetContent() {
CapodTheme {
Box(
modifier = Modifier
.fillMaxSize()
.background(
Brush.verticalGradient(
colors = listOf(
Color(0xFF1A237E),
Color(0xFF0D47A1),
Color(0xFF006064),
)
)
),
contentAlignment = Alignment.Center,
) {
WidgetDualCompact()
}
}
}
@Composable
private fun WidgetDualCompact() {
Surface(
shape = RoundedCornerShape(16.dp),
color = MaterialTheme.colorScheme.surface,
tonalElevation = 2.dp,
) {
Column(
modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
WidgetPodRow(
icon = R.drawable.device_airpods_pro2_left,
percent = 85,
charging = false,
inEar = true,
)
WidgetPodRow(
icon = R.drawable.device_airpods_pro2_right,
percent = 92,
charging = true,
inEar = false,
)
WidgetCaseRow(
icon = R.drawable.device_airpods_pro2_case,
percent = 100,
charging = false,
)
Text(
text = "My AirPods Pro",
fontSize = 12.sp,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.onSurface,
)
}
}
}
@Composable
private fun WidgetPodRow(
@DrawableRes icon: Int,
percent: Int,
charging: Boolean,
inEar: Boolean,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
modifier = Modifier.padding(vertical = 2.dp),
) {
Image(
painter = painterResource(icon),
contentDescription = null,
modifier = Modifier.size(20.dp),
)
Text(
text = "$percent%",
fontSize = 12.sp,
color = MaterialTheme.colorScheme.onSurface,
modifier = Modifier.padding(horizontal = 4.dp),
)
if (charging) {
Image(
painter = painterResource(R.drawable.ic_baseline_power_24),
contentDescription = null,
modifier = Modifier.size(20.dp),
)
}
if (inEar) {
Image(
painter = painterResource(R.drawable.ic_baseline_hearing_24),
contentDescription = null,
modifier = Modifier.size(20.dp),
)
}
}
}
@Composable
private fun WidgetCaseRow(
@DrawableRes icon: Int,
percent: Int,
charging: Boolean,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
modifier = Modifier.padding(vertical = 2.dp),
) {
Image(
painter = painterResource(icon),
contentDescription = null,
modifier = Modifier.size(20.dp),
)
Text(
text = "$percent%",
fontSize = 12.sp,
color = MaterialTheme.colorScheme.onSurface,
modifier = Modifier.padding(horizontal = 4.dp),
)
if (charging) {
Image(
painter = painterResource(R.drawable.ic_baseline_power_24),
contentDescription = null,
modifier = Modifier.size(20.dp),
)
}
}
}
@Preview(name = "1 - Dashboard Light", locale = "en", device = DS, showSystemUi = true)
@Composable
private fun PreviewDashboardLight() = DashboardContent()
@Preview(name = "2 - Dashboard Dark", locale = "en", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES, showSystemUi = true)
@Composable
private fun PreviewDashboardDark() = DashboardContent()
@Preview(name = "3 - Device Profiles", locale = "en", device = DS, showSystemUi = true)
@Composable
private fun PreviewDeviceProfiles() = DeviceProfilesContent()
@Preview(name = "4 - Add Profile", locale = "en", device = DS, showSystemUi = true)
@Composable
private fun PreviewAddProfile() = AddProfileContent()
@Preview(name = "5 - Settings", locale = "en", device = DS, showSystemUi = true)
@Composable
private fun PreviewSettingsIndex() = SettingsIndexContent()
@Preview(name = "6 - Reaction Settings", locale = "en", device = DS, showSystemUi = true)
@Composable
private fun PreviewReactionSettings() = ReactionSettingsContent()
@Preview(name = "7 - Homescreen Widget", device = DS, showSystemUi = true)
@Composable
private fun PreviewHomescreenWidget() = HomescreenWidgetContent()
@Preview(name = "7 - Widget Configuration", locale = "en", device = DS, showSystemUi = true)
@Composable
private fun PreviewWidgetConfiguration() = WidgetConfigurationContent()
@@ -18,10 +18,13 @@ import eu.darken.capod.profiles.core.AppleDeviceProfile
import eu.darken.capod.profiles.core.DeviceProfile
import java.time.Instant
/** Fixed timestamp for deterministic preview/screenshot rendering. */
val MOCK_NOW: Instant = Instant.parse("2025-06-15T10:30:00Z")
object MockPodDataProvider {
fun dummyScanResult(rssi: Int = -50): BleScanResult = BleScanResult(
receivedAt = Instant.now(),
receivedAt = MOCK_NOW,
address = "AA:BB:CC:DD:EE:FF",
rssi = rssi,
generatedAtNanos = 0L,
@@ -177,8 +180,8 @@ private class MockDualPodDevice(
) : DualPodDevice, HasCase, HasChargeDetectionDual, HasEarDetectionDual, HasDualMicrophone {
override val identifier: PodDevice.Id = PodDevice.Id()
override val model: PodDevice.Model = _model
override val seenLastAt: Instant = Instant.now()
override val seenFirstAt: Instant = Instant.now()
override val seenLastAt: Instant = MOCK_NOW
override val seenFirstAt: Instant = MOCK_NOW
override val seenCounter: Int = 5
override val scanResult: BleScanResult = MockPodDataProvider.dummyScanResult(rssi)
override val reliability: Float = 1.0f
@@ -219,8 +222,8 @@ private class MockDualPodDeviceNoCase(
) : DualPodDevice, HasChargeDetectionDual, HasEarDetectionDual {
override val identifier: PodDevice.Id = PodDevice.Id()
override val model: PodDevice.Model = _model
override val seenLastAt: Instant = Instant.now()
override val seenFirstAt: Instant = Instant.now()
override val seenLastAt: Instant = MOCK_NOW
override val seenFirstAt: Instant = MOCK_NOW
override val seenCounter: Int = 5
override val scanResult: BleScanResult = MockPodDataProvider.dummyScanResult(rssi)
override val reliability: Float = 1.0f
@@ -251,8 +254,8 @@ private class MockSinglePodDevice(
) : SinglePodDevice, HasChargeDetection, HasEarDetection {
override val identifier: PodDevice.Id = PodDevice.Id()
override val model: PodDevice.Model = _model
override val seenLastAt: Instant = Instant.now()
override val seenFirstAt: Instant = Instant.now()
override val seenLastAt: Instant = MOCK_NOW
override val seenFirstAt: Instant = MOCK_NOW
override val seenCounter: Int = 5
override val scanResult: BleScanResult = MockPodDataProvider.dummyScanResult(rssi)
override val reliability: Float = 1.0f
@@ -121,6 +121,14 @@ fun WidgetConfigurationScreen(
.verticalScroll(rememberScrollState())
.padding(top = 24.dp, bottom = 16.dp),
) {
Text(
text = stringResource(R.string.widget_config_screen_title),
style = MaterialTheme.typography.headlineSmall,
modifier = Modifier
.fillMaxWidth()
.padding(start = screenHPad, end = screenHPad, bottom = 16.dp),
)
// Card A — Select Device
Card(
modifier = Modifier
@@ -170,16 +178,15 @@ fun WidgetConfigurationScreen(
) {
Column(modifier = Modifier.padding(cardPad)) {
// Appearance header + reset
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
Column(modifier = Modifier.fillMaxWidth()) {
Text(
text = stringResource(R.string.widget_config_appearance_label),
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.weight(1f),
)
TextButton(onClick = onReset) {
TextButton(
onClick = onReset,
modifier = Modifier.align(Alignment.End),
) {
Text(text = stringResource(R.string.widget_config_reset_label))
}
}
@@ -327,7 +334,7 @@ fun WidgetConfigurationScreen(
color = MaterialTheme.colorScheme.error,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 24.dp, vertical = 12.dp),
.padding(start = 24.dp, end = 24.dp, top = 16.dp, bottom = 4.dp),
)
}
@@ -496,11 +503,13 @@ private fun WidgetPreview(
// Inner preview content
LayoutInflater.from(ctx).inflate(R.layout.widget_config_preview, clipWrapper, true)
container.addView(clipWrapper, android.widget.FrameLayout.LayoutParams(
android.widget.FrameLayout.LayoutParams.WRAP_CONTENT,
android.widget.FrameLayout.LayoutParams.WRAP_CONTENT,
android.view.Gravity.CENTER,
))
container.addView(
clipWrapper, android.widget.FrameLayout.LayoutParams(
android.widget.FrameLayout.LayoutParams.WRAP_CONTENT,
android.widget.FrameLayout.LayoutParams.WRAP_CONTENT,
android.view.Gravity.CENTER,
)
)
container
},
@@ -744,7 +753,14 @@ private fun HexColorInput(
) {
val hexString = color?.let { String.format("%06X", 0xFFFFFF and it) } ?: ""
var textFieldValue by remember { mutableStateOf(TextFieldValue(text = hexString, selection = TextRange(hexString.length))) }
var textFieldValue by remember {
mutableStateOf(
TextFieldValue(
text = hexString,
selection = TextRange(hexString.length)
)
)
}
// Sync from external color changes (e.g. swatch clicks) only when content genuinely differs
LaunchedEffect(hexString) {
+1
View File
@@ -117,6 +117,7 @@
<string name="translators_thanks_description">darken</string>
<string name="widget_description">A widget showing the last known device status.</string>
<string name="widget_config_screen_title">Widget Configuration</string>
<string name="widget_configuration_title">Select Device</string>
<string name="widget_configuration_description">Choose which device profile this widget should display.</string>
<string name="common_feature_requires_pro_msg">This feature requires CAPod Pro.</string>
@@ -0,0 +1,162 @@
package eu.darken.capod.screenshots
import android.content.res.Configuration
import androidx.compose.ui.tooling.preview.Preview
/**
* Multi-preview annotation generating one preview per Play Store-supported locale (light mode).
* Each [name] is the fastlane metadata directory name for direct use in the copy script.
*/
@Preview(locale = "en", name = "en-US", device = DS)
@Preview(locale = "af", name = "af", device = DS)
@Preview(locale = "am", name = "am", device = DS)
@Preview(locale = "ar", name = "ar", device = DS)
@Preview(locale = "az", name = "az-AZ", device = DS)
@Preview(locale = "be", name = "be", device = DS)
@Preview(locale = "bg", name = "bg", device = DS)
@Preview(locale = "bn-BD", name = "bn-BD", device = DS)
@Preview(locale = "ca", name = "ca", device = DS)
@Preview(locale = "cs", name = "cs-CZ", device = DS)
@Preview(locale = "da", name = "da-DK", device = DS)
@Preview(locale = "de", name = "de-DE", device = DS)
@Preview(locale = "el", name = "el-GR", device = DS)
@Preview(locale = "es", name = "es-ES", device = DS)
@Preview(locale = "es-MX", name = "es-419", device = DS)
@Preview(locale = "et-EE", name = "et", device = DS)
@Preview(locale = "eu-ES", name = "eu-ES", device = DS)
@Preview(locale = "fa", name = "fa", device = DS)
@Preview(locale = "fi", name = "fi-FI", device = DS)
@Preview(locale = "fil", name = "fil", device = DS)
@Preview(locale = "fr", name = "fr-FR", device = DS)
@Preview(locale = "gl-ES", name = "gl-ES", device = DS)
@Preview(locale = "hi-IN", name = "hi-IN", device = DS)
@Preview(locale = "hr", name = "hr", device = DS)
@Preview(locale = "hu", name = "hu-HU", device = DS)
@Preview(locale = "hy-AM", name = "hy-AM", device = DS)
@Preview(locale = "in", name = "id", device = DS)
@Preview(locale = "is", name = "is-IS", device = DS)
@Preview(locale = "it", name = "it-IT", device = DS)
@Preview(locale = "iw", name = "iw-IL", device = DS)
@Preview(locale = "ja", name = "ja-JP", device = DS)
@Preview(locale = "ka-GE", name = "ka-GE", device = DS)
@Preview(locale = "km-KH", name = "km-KH", device = DS)
@Preview(locale = "kn-IN", name = "kn-IN", device = DS)
@Preview(locale = "ko", name = "ko-KR", device = DS)
@Preview(locale = "ky-KG", name = "ky-KG", device = DS)
@Preview(locale = "lo-LA", name = "lo-LA", device = DS)
@Preview(locale = "lt", name = "lt", device = DS)
@Preview(locale = "lv", name = "lv", device = DS)
@Preview(locale = "mk-MK", name = "mk-MK", device = DS)
@Preview(locale = "ml-IN", name = "ml-IN", device = DS)
@Preview(locale = "mn-MN", name = "mn-MN", device = DS)
@Preview(locale = "mr-IN", name = "mr-IN", device = DS)
@Preview(locale = "ms", name = "ms", device = DS)
@Preview(locale = "my-MM", name = "my-MM", device = DS)
@Preview(locale = "nb", name = "no-NO", device = DS)
@Preview(locale = "ne-NP", name = "ne-NP", device = DS)
@Preview(locale = "nl", name = "nl-NL", device = DS)
@Preview(locale = "pl", name = "pl-PL", device = DS)
@Preview(locale = "pt", name = "pt-PT", device = DS)
@Preview(locale = "pt-BR", name = "pt-BR", device = DS)
@Preview(locale = "rm", name = "rm", device = DS)
@Preview(locale = "ro", name = "ro", device = DS)
@Preview(locale = "ru", name = "ru-RU", device = DS)
@Preview(locale = "sk", name = "sk", device = DS)
@Preview(locale = "sl", name = "sl", device = DS)
@Preview(locale = "sr", name = "sr", device = DS)
@Preview(locale = "sv", name = "sv-SE", device = DS)
@Preview(locale = "sw", name = "sw", device = DS)
@Preview(locale = "ta-IN", name = "ta-IN", device = DS)
@Preview(locale = "te-IN", name = "te-IN", device = DS)
@Preview(locale = "th", name = "th", device = DS)
@Preview(locale = "tr", name = "tr-TR", device = DS)
@Preview(locale = "uk", name = "uk", device = DS)
@Preview(locale = "vi", name = "vi", device = DS)
@Preview(locale = "zh-CN", name = "zh-CN", device = DS)
@Preview(locale = "zh-HK", name = "zh-HK", device = DS)
@Preview(locale = "zh-TW", name = "zh-TW", device = DS)
annotation class PlayStoreLocales
/**
* Same locales but with night mode enabled for dark theme screenshots.
*/
@Preview(locale = "en", name = "en-US", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "af", name = "af", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "am", name = "am", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "ar", name = "ar", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "az", name = "az-AZ", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "be", name = "be", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "bg", name = "bg", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "bn-BD", name = "bn-BD", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "ca", name = "ca", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "cs", name = "cs-CZ", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "da", name = "da-DK", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "de", name = "de-DE", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "el", name = "el-GR", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "es", name = "es-ES", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "es-MX", name = "es-419", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "et-EE", name = "et", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "eu-ES", name = "eu-ES", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "fa", name = "fa", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "fi", name = "fi-FI", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "fil", name = "fil", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "fr", name = "fr-FR", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "gl-ES", name = "gl-ES", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "hi-IN", name = "hi-IN", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "hr", name = "hr", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "hu", name = "hu-HU", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "hy-AM", name = "hy-AM", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "in", name = "id", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "is", name = "is-IS", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "it", name = "it-IT", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "iw", name = "iw-IL", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "ja", name = "ja-JP", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "ka-GE", name = "ka-GE", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "km-KH", name = "km-KH", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "kn-IN", name = "kn-IN", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "ko", name = "ko-KR", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "ky-KG", name = "ky-KG", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "lo-LA", name = "lo-LA", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "lt", name = "lt", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "lv", name = "lv", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "mk-MK", name = "mk-MK", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "ml-IN", name = "ml-IN", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "mn-MN", name = "mn-MN", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "mr-IN", name = "mr-IN", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "ms", name = "ms", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "my-MM", name = "my-MM", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "nb", name = "no-NO", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "ne-NP", name = "ne-NP", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "nl", name = "nl-NL", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "pl", name = "pl-PL", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "pt", name = "pt-PT", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "pt-BR", name = "pt-BR", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "rm", name = "rm", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "ro", name = "ro", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "ru", name = "ru-RU", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "sk", name = "sk", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "sl", name = "sl", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "sr", name = "sr", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "sv", name = "sv-SE", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "sw", name = "sw", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "ta-IN", name = "ta-IN", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "te-IN", name = "te-IN", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "th", name = "th", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "tr", name = "tr-TR", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "uk", name = "uk", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "vi", name = "vi", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "zh-CN", name = "zh-CN", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "zh-HK", name = "zh-HK", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(locale = "zh-TW", name = "zh-TW", device = DS, uiMode = Configuration.UI_MODE_NIGHT_YES)
annotation class PlayStoreLocalesDark
/**
* Smoke test subset for fast iteration (6 locales covering LTR, RTL, CJK).
*/
@Preview(locale = "en", name = "en-US", device = DS)
@Preview(locale = "de", name = "de-DE", device = DS)
@Preview(locale = "ja", name = "ja-JP", device = DS)
@Preview(locale = "ar", name = "ar", device = DS)
@Preview(locale = "zh-CN", name = "zh-CN", device = DS)
@Preview(locale = "pt-BR", name = "pt-BR", device = DS)
annotation class PlayStoreLocalesSmoke
@@ -0,0 +1,40 @@
// For IDE design preview, open ScreenshotPreviews.kt instead.
package eu.darken.capod.screenshots
import androidx.compose.runtime.Composable
import com.android.tools.screenshot.PreviewTest
@PreviewTest
@PlayStoreLocales
@Composable
fun DashboardLight() = DashboardContent()
@PreviewTest
@PlayStoreLocalesDark
@Composable
fun DashboardDark() = DashboardContent()
@PreviewTest
@PlayStoreLocales
@Composable
fun DeviceProfiles() = DeviceProfilesContent()
@PreviewTest
@PlayStoreLocales
@Composable
fun AddProfile() = AddProfileContent()
@PreviewTest
@PlayStoreLocales
@Composable
fun SettingsIndex() = SettingsIndexContent()
@PreviewTest
@PlayStoreLocales
@Composable
fun ReactionSettings() = ReactionSettingsContent()
@PreviewTest
@PlayStoreLocales
@Composable
fun WidgetConfiguration() = WidgetConfigurationContent()