mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-16 11:16:12 -04:00
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:
@@ -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) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user