From 2ca8f686c8422a7ad1369bd91ad2d860e5d6e075 Mon Sep 17 00:00:00 2001 From: darken Date: Tue, 24 Feb 2026 16:32:10 +0100 Subject: [PATCH] feat(ui): Add Compose previews for all UI components and screens Add @Preview2 multi-preview annotation (light/dark), mock data provider with realistic device scenarios, and ~45 preview functions across cards, screens, popup, and settings components. --- .../darken/capod/common/compose/Preview2.kt | 10 + .../capod/common/compose/PreviewWrapper.kt | 8 +- .../compose/preview/MockPodDataProvider.kt | 272 ++++++++++++++++++ .../capod/common/settings/SettingsBaseItem.kt | 15 + .../common/settings/SettingsCategoryHeader.kt | 8 + .../capod/common/settings/SettingsDivider.kt | 8 + .../settings/SettingsListPreferenceItem.kt | 17 ++ .../common/settings/SettingsSliderItem.kt | 17 ++ .../common/settings/SettingsSwitchItem.kt | 28 ++ .../main/ui/onboarding/OnboardingScreen.kt | 8 + .../capod/main/ui/overview/OverviewScreen.kt | 98 +++++++ .../overview/cards/BluetoothDisabledCard.kt | 8 + .../main/ui/overview/cards/DualPodsCard.kt | 39 +++ .../ui/overview/cards/MonitoringActiveCard.kt | 8 + .../main/ui/overview/cards/NoProfilesCard.kt | 8 + .../main/ui/overview/cards/PermissionCard.kt | 8 + .../main/ui/overview/cards/SinglePodsCard.kt | 21 ++ .../ui/overview/cards/UnknownPodDeviceCard.kt | 15 + .../ui/overview/cards/UnmatchedDevicesCard.kt | 14 + .../capod/main/ui/settings/SettingsScreen.kt | 20 ++ .../settings/acks/AcknowledgementsScreen.kt | 8 + .../settings/general/GeneralSettingsScreen.kt | 27 ++ .../general/debug/DebugSettingsScreen.kt | 18 ++ .../main/ui/settings/support/SupportScreen.kt | 15 + .../ui/widget/WidgetConfigurationScreen.kt | 65 +++++ .../capod/profiles/ui/DeviceManagerScreen.kt | 32 +++ .../creation/DeviceProfileCreationScreen.kt | 62 ++++ .../reaction/ui/ReactionSettingsScreen.kt | 27 ++ .../capod/reaction/ui/popup/PopUpContent.kt | 21 ++ .../troubleshooter/ui/TroubleShooterScreen.kt | 15 + 30 files changed, 918 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/eu/darken/capod/common/compose/Preview2.kt create mode 100644 app/src/main/java/eu/darken/capod/common/compose/preview/MockPodDataProvider.kt diff --git a/app/src/main/java/eu/darken/capod/common/compose/Preview2.kt b/app/src/main/java/eu/darken/capod/common/compose/Preview2.kt new file mode 100644 index 00000000..40457777 --- /dev/null +++ b/app/src/main/java/eu/darken/capod/common/compose/Preview2.kt @@ -0,0 +1,10 @@ +package eu.darken.capod.common.compose + +import android.content.res.Configuration +import androidx.compose.ui.tooling.preview.Preview + +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FUNCTION) +@Preview(showBackground = true, name = "Light", uiMode = Configuration.UI_MODE_NIGHT_NO) +@Preview(showBackground = true, name = "Dark", uiMode = Configuration.UI_MODE_NIGHT_YES) +annotation class Preview2 diff --git a/app/src/main/java/eu/darken/capod/common/compose/PreviewWrapper.kt b/app/src/main/java/eu/darken/capod/common/compose/PreviewWrapper.kt index c8edd0eb..7e37df0f 100644 --- a/app/src/main/java/eu/darken/capod/common/compose/PreviewWrapper.kt +++ b/app/src/main/java/eu/darken/capod/common/compose/PreviewWrapper.kt @@ -1,11 +1,15 @@ package eu.darken.capod.common.compose +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import eu.darken.capod.common.theming.CapodTheme @Composable fun PreviewWrapper(content: @Composable () -> Unit) { - CapodTheme { - content() + CapodTheme(dynamicColor = false) { + Surface(color = MaterialTheme.colorScheme.background) { + content() + } } } diff --git a/app/src/main/java/eu/darken/capod/common/compose/preview/MockPodDataProvider.kt b/app/src/main/java/eu/darken/capod/common/compose/preview/MockPodDataProvider.kt new file mode 100644 index 00000000..f7f121b4 --- /dev/null +++ b/app/src/main/java/eu/darken/capod/common/compose/preview/MockPodDataProvider.kt @@ -0,0 +1,272 @@ +package eu.darken.capod.common.compose.preview + +import android.content.Context +import eu.darken.capod.R +import eu.darken.capod.common.bluetooth.BleScanResult +import eu.darken.capod.common.upgrade.UpgradeRepo +import eu.darken.capod.pods.core.DualPodDevice +import eu.darken.capod.pods.core.HasCase +import eu.darken.capod.pods.core.HasChargeDetection +import eu.darken.capod.pods.core.HasChargeDetectionDual +import eu.darken.capod.pods.core.HasDualMicrophone +import eu.darken.capod.pods.core.HasEarDetection +import eu.darken.capod.pods.core.HasEarDetectionDual +import eu.darken.capod.pods.core.PodDevice +import eu.darken.capod.pods.core.SinglePodDevice +import eu.darken.capod.pods.core.unknown.UnknownDevice +import eu.darken.capod.profiles.core.AppleDeviceProfile +import eu.darken.capod.profiles.core.DeviceProfile +import java.time.Instant + +object MockPodDataProvider { + + fun dummyScanResult(rssi: Int = -50): BleScanResult = BleScanResult( + receivedAt = Instant.now(), + address = "AA:BB:CC:DD:EE:FF", + rssi = rssi, + generatedAtNanos = 0L, + manufacturerSpecificData = mapOf( + 76 to byteArrayOf(0x07, 0x19, 0x01, 0x02, 0x20, 0x75, 0xAA.toByte(), 0x30, 0x01, 0x00, 0x00, 0x2D) + ), + ) + + // --- Dual pod scenarios --- + + fun airPodsProFullCharge(): DualPodDevice = MockDualPodDevice( + _model = PodDevice.Model.AIRPODS_PRO2, + _label = "Work AirPods", + batteryLeftPodPercent = 1.0f, + batteryRightPodPercent = 1.0f, + _batteryCasePercent = 1.0f, + leftPodIcon = R.drawable.device_airpods_pro2_left, + rightPodIcon = R.drawable.device_airpods_pro2_right, + _caseIcon = R.drawable.device_airpods_pro2_case, + ) + + fun airPodsProMixed(): DualPodDevice = MockDualPodDevice( + _model = PodDevice.Model.AIRPODS_PRO2, + _label = "My AirPods Pro", + batteryLeftPodPercent = 0.80f, + batteryRightPodPercent = 0.45f, + _batteryCasePercent = 0.60f, + _isLeftPodCharging = true, + leftPodIcon = R.drawable.device_airpods_pro2_left, + rightPodIcon = R.drawable.device_airpods_pro2_right, + _caseIcon = R.drawable.device_airpods_pro2_case, + ) + + fun airPodsProLowBattery(): DualPodDevice = MockDualPodDevice( + _model = PodDevice.Model.AIRPODS_PRO2, + _label = "My AirPods Pro", + batteryLeftPodPercent = 0.10f, + batteryRightPodPercent = 0.05f, + _batteryCasePercent = null, + _isLeftPodInEar = true, + _isRightPodInEar = true, + leftPodIcon = R.drawable.device_airpods_pro2_left, + rightPodIcon = R.drawable.device_airpods_pro2_right, + _caseIcon = R.drawable.device_airpods_pro2_case, + ) + + fun airPodsProInCase(): DualPodDevice = MockDualPodDevice( + _model = PodDevice.Model.AIRPODS_PRO2, + _label = "My AirPods Pro", + batteryLeftPodPercent = null, + batteryRightPodPercent = null, + _batteryCasePercent = 0.90f, + leftPodIcon = R.drawable.device_airpods_pro2_left, + rightPodIcon = R.drawable.device_airpods_pro2_right, + _caseIcon = R.drawable.device_airpods_pro2_case, + ) + + fun airPodsGen1Wearing(): DualPodDevice = MockDualPodDevice( + _model = PodDevice.Model.AIRPODS_GEN1, + _label = "Old AirPods", + batteryLeftPodPercent = 0.70f, + batteryRightPodPercent = 0.65f, + _batteryCasePercent = 0.50f, + _isLeftPodInEar = true, + _isRightPodInEar = true, + _isLeftPodMicrophone = true, + leftPodIcon = R.drawable.device_airpods_gen1_left, + rightPodIcon = R.drawable.device_airpods_gen1_right, + _caseIcon = R.drawable.device_airpods_gen1_case, + ) + + fun powerBeatsPro(): DualPodDevice = MockDualPodDeviceNoCase( + _model = PodDevice.Model.POWERBEATS_PRO, + _label = "PowerBeats Pro", + batteryLeftPodPercent = 0.55f, + batteryRightPodPercent = 0.60f, + leftPodIcon = R.drawable.device_powerbeats_pro_left, + rightPodIcon = R.drawable.device_powerbeats_pro_right, + ) + + // --- Single pod scenarios --- + + fun airPodsMax(): SinglePodDevice = MockSinglePodDevice( + _model = PodDevice.Model.AIRPODS_MAX, + _label = "AirPods Max", + batteryHeadsetPercent = 0.85f, + _isBeingWorn = true, + ) + + fun airPodsMaxCharging(): SinglePodDevice = MockSinglePodDevice( + _model = PodDevice.Model.AIRPODS_MAX, + _label = "AirPods Max", + batteryHeadsetPercent = 0.40f, + _isHeadsetBeingCharged = true, + ) + + fun beatsSolo3(): SinglePodDevice = MockSinglePodDevice( + _model = PodDevice.Model.BEATS_SOLO_3, + _label = "Beats Solo 3", + batteryHeadsetPercent = 0.70f, + ) + + // --- Unknown device --- + + fun unknownDevice(): PodDevice = UnknownDevice( + scanResult = dummyScanResult(rssi = -70), + ) + + // --- Profile helpers --- + + fun profile(label: String, model: PodDevice.Model): AppleDeviceProfile = AppleDeviceProfile( + label = label, + model = model, + ) + + // --- UpgradeInfo --- + + fun fossInfo(isPro: Boolean = false): UpgradeRepo.Info = MockUpgradeInfo( + type = UpgradeRepo.Type.FOSS, + isPro = isPro, + ) + + fun gplayInfo(isPro: Boolean = false): UpgradeRepo.Info = MockUpgradeInfo( + type = UpgradeRepo.Type.GPLAY, + isPro = isPro, + ) +} + +private data class MockUpgradeInfo( + override val type: UpgradeRepo.Type, + override val isPro: Boolean, + override val upgradedAt: Instant? = null, + override val error: Throwable? = null, +) : UpgradeRepo.Info + +private class MockDualPodDevice( + private val _model: PodDevice.Model, + private val _label: String, + override val batteryLeftPodPercent: Float?, + override val batteryRightPodPercent: Float?, + private val _batteryCasePercent: Float?, + private val _isCaseCharging: Boolean = false, + private val _isLeftPodCharging: Boolean = false, + private val _isRightPodCharging: Boolean = false, + private val _isLeftPodInEar: Boolean = false, + private val _isRightPodInEar: Boolean = false, + private val _isLeftPodMicrophone: Boolean = false, + private val _isRightPodMicrophone: Boolean = false, + override val leftPodIcon: Int = R.drawable.device_airpods_gen1_left, + override val rightPodIcon: Int = R.drawable.device_airpods_gen1_right, + private val _caseIcon: Int = R.drawable.device_airpods_gen1_case, + rssi: Int = -50, +) : 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 seenCounter: Int = 5 + override val scanResult: BleScanResult = MockPodDataProvider.dummyScanResult(rssi) + override val reliability: Float = 1.0f + override val signalQuality: Float = 0.75f + override val iconRes: Int = _model.iconRes + override val meta: PodDevice.Meta = object : PodDevice.Meta { + override val profile: DeviceProfile = AppleDeviceProfile(label = _label, model = _model) + } + + override fun getLabel(context: Context): String = _model.label + + // HasCase + override val batteryCasePercent: Float? = _batteryCasePercent + override val isCaseCharging: Boolean = _isCaseCharging + override val caseIcon: Int = _caseIcon + + // HasChargeDetectionDual + override val isLeftPodCharging: Boolean = _isLeftPodCharging + override val isRightPodCharging: Boolean = _isRightPodCharging + + // HasEarDetectionDual + override val isLeftPodInEar: Boolean = _isLeftPodInEar + override val isRightPodInEar: Boolean = _isRightPodInEar + + // HasDualMicrophone + override val isLeftPodMicrophone: Boolean = _isLeftPodMicrophone + override val isRightPodMicrophone: Boolean = _isRightPodMicrophone +} + +private class MockDualPodDeviceNoCase( + private val _model: PodDevice.Model, + private val _label: String, + override val batteryLeftPodPercent: Float?, + override val batteryRightPodPercent: Float?, + override val leftPodIcon: Int = R.drawable.device_airpods_gen1_left, + override val rightPodIcon: Int = R.drawable.device_airpods_gen1_right, + rssi: Int = -50, +) : 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 seenCounter: Int = 5 + override val scanResult: BleScanResult = MockPodDataProvider.dummyScanResult(rssi) + override val reliability: Float = 1.0f + override val signalQuality: Float = 0.70f + override val iconRes: Int = _model.iconRes + override val meta: PodDevice.Meta = object : PodDevice.Meta { + override val profile: DeviceProfile = AppleDeviceProfile(label = _label, model = _model) + } + + override fun getLabel(context: Context): String = _model.label + + // HasChargeDetectionDual + override val isLeftPodCharging: Boolean = false + override val isRightPodCharging: Boolean = false + + // HasEarDetectionDual + override val isLeftPodInEar: Boolean = false + override val isRightPodInEar: Boolean = false +} + +private class MockSinglePodDevice( + private val _model: PodDevice.Model, + private val _label: String, + override val batteryHeadsetPercent: Float?, + private val _isHeadsetBeingCharged: Boolean = false, + private val _isBeingWorn: Boolean = false, + rssi: Int = -50, +) : 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 seenCounter: Int = 5 + override val scanResult: BleScanResult = MockPodDataProvider.dummyScanResult(rssi) + override val reliability: Float = 1.0f + override val signalQuality: Float = 0.80f + override val iconRes: Int = _model.iconRes + override val meta: PodDevice.Meta = object : PodDevice.Meta { + override val profile: DeviceProfile = AppleDeviceProfile(label = _label, model = _model) + } + + override fun getLabel(context: Context): String = _model.label + + // HasChargeDetection + override val isHeadsetBeingCharged: Boolean = _isHeadsetBeingCharged + + // HasEarDetection + override val isBeingWorn: Boolean = _isBeingWorn +} diff --git a/app/src/main/java/eu/darken/capod/common/settings/SettingsBaseItem.kt b/app/src/main/java/eu/darken/capod/common/settings/SettingsBaseItem.kt index 0f3c6b2e..06bfd034 100644 --- a/app/src/main/java/eu/darken/capod/common/settings/SettingsBaseItem.kt +++ b/app/src/main/java/eu/darken/capod/common/settings/SettingsBaseItem.kt @@ -13,12 +13,16 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.twotone.Settings import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import eu.darken.capod.common.compose.Preview2 +import eu.darken.capod.common.compose.PreviewWrapper @OptIn(ExperimentalFoundationApi::class) @Composable @@ -94,3 +98,14 @@ fun SettingsBaseItem( trailingContent?.invoke() } } + +@Preview2 +@Composable +private fun SettingsBaseItemPreview() = PreviewWrapper { + SettingsBaseItem( + title = "General Settings", + subtitle = "Monitor mode, scanner, notifications", + icon = Icons.TwoTone.Settings, + onClick = {}, + ) +} diff --git a/app/src/main/java/eu/darken/capod/common/settings/SettingsCategoryHeader.kt b/app/src/main/java/eu/darken/capod/common/settings/SettingsCategoryHeader.kt index 42d6b84b..9e6991ac 100644 --- a/app/src/main/java/eu/darken/capod/common/settings/SettingsCategoryHeader.kt +++ b/app/src/main/java/eu/darken/capod/common/settings/SettingsCategoryHeader.kt @@ -7,6 +7,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import eu.darken.capod.common.compose.Preview2 +import eu.darken.capod.common.compose.PreviewWrapper @Composable fun SettingsCategoryHeader( @@ -21,3 +23,9 @@ fun SettingsCategoryHeader( modifier = modifier.padding(horizontal = 16.dp, vertical = 12.dp) ) } + +@Preview2 +@Composable +private fun SettingsCategoryHeaderPreview() = PreviewWrapper { + SettingsCategoryHeader(text = "Other") +} diff --git a/app/src/main/java/eu/darken/capod/common/settings/SettingsDivider.kt b/app/src/main/java/eu/darken/capod/common/settings/SettingsDivider.kt index 81f0217d..5c574560 100644 --- a/app/src/main/java/eu/darken/capod/common/settings/SettingsDivider.kt +++ b/app/src/main/java/eu/darken/capod/common/settings/SettingsDivider.kt @@ -6,6 +6,8 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import eu.darken.capod.common.compose.Preview2 +import eu.darken.capod.common.compose.PreviewWrapper @Composable fun SettingsDivider( @@ -16,3 +18,9 @@ fun SettingsDivider( color = MaterialTheme.colorScheme.outline.copy(alpha = 0.12f) ) } + +@Preview2 +@Composable +private fun SettingsDividerPreview() = PreviewWrapper { + SettingsDivider() +} diff --git a/app/src/main/java/eu/darken/capod/common/settings/SettingsListPreferenceItem.kt b/app/src/main/java/eu/darken/capod/common/settings/SettingsListPreferenceItem.kt index 5880f623..f3359ecb 100644 --- a/app/src/main/java/eu/darken/capod/common/settings/SettingsListPreferenceItem.kt +++ b/app/src/main/java/eu/darken/capod/common/settings/SettingsListPreferenceItem.kt @@ -11,6 +11,8 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.RadioButton import androidx.compose.material3.Text import androidx.compose.material3.TextButton +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.twotone.Tune import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -22,6 +24,8 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.unit.dp +import eu.darken.capod.common.compose.Preview2 +import eu.darken.capod.common.compose.PreviewWrapper @Composable fun SettingsListPreferenceItem( @@ -89,3 +93,16 @@ fun SettingsListPreferenceItem( ) } } + +@Preview2 +@Composable +private fun SettingsListPreferenceItemPreview() = PreviewWrapper { + SettingsListPreferenceItem( + icon = Icons.TwoTone.Tune, + title = "Mode", + entries = listOf("Manual", "Automatic", "Always"), + selectedEntry = "Automatic", + onEntrySelected = {}, + entryLabel = { it }, + ) +} diff --git a/app/src/main/java/eu/darken/capod/common/settings/SettingsSliderItem.kt b/app/src/main/java/eu/darken/capod/common/settings/SettingsSliderItem.kt index 2df1bdac..efed7c66 100644 --- a/app/src/main/java/eu/darken/capod/common/settings/SettingsSliderItem.kt +++ b/app/src/main/java/eu/darken/capod/common/settings/SettingsSliderItem.kt @@ -6,10 +6,14 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Slider import androidx.compose.material3.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.twotone.VolumeUp import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.unit.dp +import eu.darken.capod.common.compose.Preview2 +import eu.darken.capod.common.compose.PreviewWrapper @Composable fun SettingsSliderItem( @@ -55,3 +59,16 @@ fun SettingsSliderItem( ) } } + +@Preview2 +@Composable +private fun SettingsSliderItemPreview() = PreviewWrapper { + SettingsSliderItem( + icon = Icons.AutoMirrored.TwoTone.VolumeUp, + title = "Volume", + subtitle = "Adjust volume level", + value = 0.6f, + onValueChange = {}, + valueLabel = { "${(it * 100).toInt()}%" }, + ) +} diff --git a/app/src/main/java/eu/darken/capod/common/settings/SettingsSwitchItem.kt b/app/src/main/java/eu/darken/capod/common/settings/SettingsSwitchItem.kt index fdcc0e01..409ea282 100644 --- a/app/src/main/java/eu/darken/capod/common/settings/SettingsSwitchItem.kt +++ b/app/src/main/java/eu/darken/capod/common/settings/SettingsSwitchItem.kt @@ -2,10 +2,14 @@ package eu.darken.capod.common.settings import androidx.compose.foundation.layout.padding import androidx.compose.material3.Switch +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.twotone.Notifications import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.unit.dp +import eu.darken.capod.common.compose.Preview2 +import eu.darken.capod.common.compose.PreviewWrapper @Composable fun SettingsSwitchItem( @@ -34,3 +38,27 @@ fun SettingsSwitchItem( } ) } + +@Preview2 +@Composable +private fun SettingsSwitchItemOnPreview() = PreviewWrapper { + SettingsSwitchItem( + icon = Icons.TwoTone.Notifications, + title = "Show notifications", + subtitle = "Display connection notifications", + checked = true, + onCheckedChange = {}, + ) +} + +@Preview2 +@Composable +private fun SettingsSwitchItemOffPreview() = PreviewWrapper { + SettingsSwitchItem( + icon = Icons.TwoTone.Notifications, + title = "Show notifications", + subtitle = "Display connection notifications", + checked = false, + onCheckedChange = {}, + ) +} diff --git a/app/src/main/java/eu/darken/capod/main/ui/onboarding/OnboardingScreen.kt b/app/src/main/java/eu/darken/capod/main/ui/onboarding/OnboardingScreen.kt index adaadbcd..c4f81603 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/onboarding/OnboardingScreen.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/onboarding/OnboardingScreen.kt @@ -24,6 +24,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import eu.darken.capod.R +import eu.darken.capod.common.compose.Preview2 +import eu.darken.capod.common.compose.PreviewWrapper import eu.darken.capod.common.compose.waitForState import eu.darken.capod.common.error.ErrorEventHandler import eu.darken.capod.common.navigation.NavigationEventHandler @@ -121,3 +123,9 @@ fun OnboardingScreen( } } } + +@Preview2 +@Composable +private fun OnboardingScreenPreview() = PreviewWrapper { + OnboardingScreen(onPrivacyPolicy = {}, onContinue = {}) +} diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewScreen.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewScreen.kt index 545cc4bc..3111571b 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewScreen.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewScreen.kt @@ -41,6 +41,9 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.LifecycleEventEffect import eu.darken.capod.R +import eu.darken.capod.common.compose.Preview2 +import eu.darken.capod.common.compose.PreviewWrapper +import eu.darken.capod.common.compose.preview.MockPodDataProvider import eu.darken.capod.common.compose.waitForState import eu.darken.capod.common.error.ErrorEventHandler import eu.darken.capod.common.navigation.NavigationEventHandler @@ -276,6 +279,101 @@ private fun PodDeviceCard(device: PodDevice, showDebug: Boolean, now: Instant) { } } +@Preview2 +@Composable +private fun OverviewScreenWithDevicesPreview() = PreviewWrapper { + OverviewScreen( + state = OverviewViewModel.State( + now = Instant.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.fossInfo(), + showUnmatchedDevices = false, + ), + onRequestPermission = {}, + onManageDevices = {}, + onSettings = {}, + onUpgrade = {}, + onToggleUnmatched = {}, + ) +} + +@Preview2 +@Composable +private fun OverviewScreenEmptyPreview() = PreviewWrapper { + OverviewScreen( + state = OverviewViewModel.State( + now = Instant.now(), + permissions = emptySet(), + devices = emptyList(), + isDebugMode = false, + isBluetoothEnabled = true, + profiles = listOf(MockPodDataProvider.profile("My AirPods", PodDevice.Model.AIRPODS_PRO2)), + upgradeInfo = MockPodDataProvider.fossInfo(), + showUnmatchedDevices = false, + ), + onRequestPermission = {}, + onManageDevices = {}, + onSettings = {}, + onUpgrade = {}, + onToggleUnmatched = {}, + ) +} + +@Preview2 +@Composable +private fun OverviewScreenNoProfilesPreview() = PreviewWrapper { + OverviewScreen( + state = OverviewViewModel.State( + now = Instant.now(), + permissions = emptySet(), + devices = emptyList(), + isDebugMode = false, + isBluetoothEnabled = true, + profiles = emptyList(), + upgradeInfo = MockPodDataProvider.gplayInfo(), + showUnmatchedDevices = false, + ), + onRequestPermission = {}, + onManageDevices = {}, + onSettings = {}, + onUpgrade = {}, + onToggleUnmatched = {}, + ) +} + +@Preview2 +@Composable +private fun OverviewScreenBluetoothOffPreview() = PreviewWrapper { + OverviewScreen( + state = OverviewViewModel.State( + now = Instant.now(), + permissions = emptySet(), + devices = emptyList(), + isDebugMode = false, + isBluetoothEnabled = false, + profiles = listOf(MockPodDataProvider.profile("My AirPods", PodDevice.Model.AIRPODS_PRO2)), + upgradeInfo = MockPodDataProvider.fossInfo(isPro = true), + showUnmatchedDevices = false, + ), + onRequestPermission = {}, + onManageDevices = {}, + onSettings = {}, + onUpgrade = {}, + onToggleUnmatched = {}, + ) +} + @Composable private fun ToolbarTitle(upgradeInfo: UpgradeRepo.Info) { val appName = stringResource(R.string.app_name) diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/BluetoothDisabledCard.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/BluetoothDisabledCard.kt index 79783ffd..25f39076 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/BluetoothDisabledCard.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/BluetoothDisabledCard.kt @@ -13,6 +13,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import eu.darken.capod.R +import eu.darken.capod.common.compose.Preview2 +import eu.darken.capod.common.compose.PreviewWrapper @Composable fun BluetoothDisabledCard() { @@ -38,3 +40,9 @@ fun BluetoothDisabledCard() { } } } + +@Preview2 +@Composable +private fun BluetoothDisabledCardPreview() = PreviewWrapper { + BluetoothDisabledCard() +} diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/DualPodsCard.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/DualPodsCard.kt index 6ea7c14c..f0b93fc2 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/DualPodsCard.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/DualPodsCard.kt @@ -46,6 +46,9 @@ import eu.darken.capod.pods.core.firstSeenFormatted import eu.darken.capod.pods.core.formatBatteryPercent import eu.darken.capod.pods.core.getSignalQuality import eu.darken.capod.pods.core.lastSeenFormatted +import eu.darken.capod.common.compose.Preview2 +import eu.darken.capod.common.compose.PreviewWrapper +import eu.darken.capod.common.compose.preview.MockPodDataProvider import java.time.Duration import java.time.Instant @@ -246,6 +249,42 @@ fun DualPodsCard( } } +@Preview2 +@Composable +private fun DualPodsCardFullChargePreview() = PreviewWrapper { + DualPodsCard(device = MockPodDataProvider.airPodsProFullCharge(), showDebug = false, now = Instant.now()) +} + +@Preview2 +@Composable +private fun DualPodsCardMixedBatteryPreview() = PreviewWrapper { + DualPodsCard(device = MockPodDataProvider.airPodsProMixed(), showDebug = false, now = Instant.now()) +} + +@Preview2 +@Composable +private fun DualPodsCardLowBatteryPreview() = PreviewWrapper { + DualPodsCard(device = MockPodDataProvider.airPodsProLowBattery(), showDebug = false, now = Instant.now()) +} + +@Preview2 +@Composable +private fun DualPodsCardInCasePreview() = PreviewWrapper { + DualPodsCard(device = MockPodDataProvider.airPodsProInCase(), showDebug = false, now = Instant.now()) +} + +@Preview2 +@Composable +private fun DualPodsCardDebugPreview() = PreviewWrapper { + DualPodsCard(device = MockPodDataProvider.airPodsProMixed(), showDebug = true, now = Instant.now()) +} + +@Preview2 +@Composable +private fun DualPodsCardNoCasePreview() = PreviewWrapper { + DualPodsCard(device = MockPodDataProvider.powerBeatsPro(), showDebug = false, now = Instant.now()) +} + @Composable private fun BatteryRow( label: String, diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/MonitoringActiveCard.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/MonitoringActiveCard.kt index a5425713..c09702cd 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/MonitoringActiveCard.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/MonitoringActiveCard.kt @@ -13,6 +13,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import eu.darken.capod.R +import eu.darken.capod.common.compose.Preview2 +import eu.darken.capod.common.compose.PreviewWrapper @Composable fun MonitoringActiveCard() { @@ -38,3 +40,9 @@ fun MonitoringActiveCard() { } } } + +@Preview2 +@Composable +private fun MonitoringActiveCardPreview() = PreviewWrapper { + MonitoringActiveCard() +} diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/NoProfilesCard.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/NoProfilesCard.kt index a96f1fd0..101018ce 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/NoProfilesCard.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/NoProfilesCard.kt @@ -18,6 +18,8 @@ import androidx.compose.material.icons.twotone.DevicesOther import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import eu.darken.capod.R +import eu.darken.capod.common.compose.Preview2 +import eu.darken.capod.common.compose.PreviewWrapper @Composable fun NoProfilesCard(onManageDevices: () -> Unit) { @@ -57,3 +59,9 @@ fun NoProfilesCard(onManageDevices: () -> Unit) { } } } + +@Preview2 +@Composable +private fun NoProfilesCardPreview() = PreviewWrapper { + NoProfilesCard(onManageDevices = {}) +} diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/PermissionCard.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/PermissionCard.kt index 06f6cbfd..f80e7c58 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/PermissionCard.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/PermissionCard.kt @@ -15,6 +15,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import eu.darken.capod.R +import eu.darken.capod.common.compose.Preview2 +import eu.darken.capod.common.compose.PreviewWrapper import eu.darken.capod.common.permissions.Permission @Composable @@ -53,3 +55,9 @@ fun PermissionCard( } } } + +@Preview2 +@Composable +private fun PermissionCardPreview() = PreviewWrapper { + PermissionCard(permission = Permission.BLUETOOTH_SCAN, onRequest = {}) +} diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/SinglePodsCard.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/SinglePodsCard.kt index 3dd9dbe2..4c2ce25c 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/SinglePodsCard.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/SinglePodsCard.kt @@ -34,6 +34,9 @@ import eu.darken.capod.pods.core.firstSeenFormatted import eu.darken.capod.pods.core.formatBatteryPercent import eu.darken.capod.pods.core.getSignalQuality import eu.darken.capod.pods.core.lastSeenFormatted +import eu.darken.capod.common.compose.Preview2 +import eu.darken.capod.common.compose.PreviewWrapper +import eu.darken.capod.common.compose.preview.MockPodDataProvider import java.time.Duration import java.time.Instant @@ -173,3 +176,21 @@ fun SinglePodsCard( } } } + +@Preview2 +@Composable +private fun SinglePodsCardWearingPreview() = PreviewWrapper { + SinglePodsCard(device = MockPodDataProvider.airPodsMax(), showDebug = false, now = Instant.now()) +} + +@Preview2 +@Composable +private fun SinglePodsCardChargingPreview() = PreviewWrapper { + SinglePodsCard(device = MockPodDataProvider.airPodsMaxCharging(), showDebug = false, now = Instant.now()) +} + +@Preview2 +@Composable +private fun SinglePodsCardDebugPreview() = PreviewWrapper { + SinglePodsCard(device = MockPodDataProvider.beatsSolo3(), showDebug = true, now = Instant.now()) +} diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/UnknownPodDeviceCard.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/UnknownPodDeviceCard.kt index abda4821..86363d8e 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/UnknownPodDeviceCard.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/UnknownPodDeviceCard.kt @@ -25,6 +25,9 @@ import eu.darken.capod.pods.core.PodDevice import eu.darken.capod.pods.core.apple.ApplePods import eu.darken.capod.pods.core.getSignalQuality import eu.darken.capod.pods.core.lastSeenFormatted +import eu.darken.capod.common.compose.Preview2 +import eu.darken.capod.common.compose.PreviewWrapper +import eu.darken.capod.common.compose.preview.MockPodDataProvider import java.time.Instant @Composable @@ -103,3 +106,15 @@ fun UnknownPodDeviceCard( } } } + +@Preview2 +@Composable +private fun UnknownPodDeviceCardPreview() = PreviewWrapper { + UnknownPodDeviceCard(device = MockPodDataProvider.unknownDevice(), showDebug = false, now = Instant.now()) +} + +@Preview2 +@Composable +private fun UnknownPodDeviceCardDebugPreview() = PreviewWrapper { + UnknownPodDeviceCard(device = MockPodDataProvider.unknownDevice(), showDebug = true, now = Instant.now()) +} diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/UnmatchedDevicesCard.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/UnmatchedDevicesCard.kt index 99789722..87c94268 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/UnmatchedDevicesCard.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/UnmatchedDevicesCard.kt @@ -15,6 +15,8 @@ import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import eu.darken.capod.R +import eu.darken.capod.common.compose.Preview2 +import eu.darken.capod.common.compose.PreviewWrapper @Composable fun UnmatchedDevicesCard( @@ -52,3 +54,15 @@ fun UnmatchedDevicesCard( } } } + +@Preview2 +@Composable +private fun UnmatchedDevicesCardCollapsedPreview() = PreviewWrapper { + UnmatchedDevicesCard(count = 3, isExpanded = false, onToggle = {}) +} + +@Preview2 +@Composable +private fun UnmatchedDevicesCardExpandedPreview() = PreviewWrapper { + UnmatchedDevicesCard(count = 3, isExpanded = true, onToggle = {}) +} diff --git a/app/src/main/java/eu/darken/capod/main/ui/settings/SettingsScreen.kt b/app/src/main/java/eu/darken/capod/main/ui/settings/SettingsScreen.kt index bc010af0..0a941c01 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/settings/SettingsScreen.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/settings/SettingsScreen.kt @@ -24,6 +24,8 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel import eu.darken.capod.R +import eu.darken.capod.common.compose.Preview2 +import eu.darken.capod.common.compose.PreviewWrapper import eu.darken.capod.common.BuildConfigWrap import eu.darken.capod.common.PrivacyPolicy import eu.darken.capod.common.compose.waitForState @@ -172,3 +174,21 @@ fun SettingsScreen( } } } + +@Preview2 +@Composable +private fun SettingsScreenPreview() = PreviewWrapper { + SettingsScreen( + state = SettingsViewModel.State(sponsorUrl = "https://example.com"), + onNavigateUp = {}, + onGeneralSettings = {}, + onDeviceManager = {}, + onReactions = {}, + onSupport = {}, + onChangelog = {}, + onHelpTranslate = {}, + onAcknowledgements = {}, + onPrivacyPolicy = {}, + onSponsor = {}, + ) +} diff --git a/app/src/main/java/eu/darken/capod/main/ui/settings/acks/AcknowledgementsScreen.kt b/app/src/main/java/eu/darken/capod/main/ui/settings/acks/AcknowledgementsScreen.kt index e7bbfdc4..92d29645 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/settings/acks/AcknowledgementsScreen.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/settings/acks/AcknowledgementsScreen.kt @@ -15,6 +15,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel import eu.darken.capod.R +import eu.darken.capod.common.compose.Preview2 +import eu.darken.capod.common.compose.PreviewWrapper import eu.darken.capod.common.error.ErrorEventHandler import eu.darken.capod.common.navigation.NavigationEventHandler import eu.darken.capod.common.settings.SettingsBaseItem @@ -162,3 +164,9 @@ fun AcknowledgementsScreen( } } } + +@Preview2 +@Composable +private fun AcknowledgementsScreenPreview() = PreviewWrapper { + AcknowledgementsScreen(onNavigateUp = {}, onOpenUrl = {}) +} diff --git a/app/src/main/java/eu/darken/capod/main/ui/settings/general/GeneralSettingsScreen.kt b/app/src/main/java/eu/darken/capod/main/ui/settings/general/GeneralSettingsScreen.kt index e60bdaae..5d58ad0b 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/settings/general/GeneralSettingsScreen.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/settings/general/GeneralSettingsScreen.kt @@ -40,6 +40,8 @@ import androidx.compose.ui.semantics.Role import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import eu.darken.capod.R +import eu.darken.capod.common.compose.Preview2 +import eu.darken.capod.common.compose.PreviewWrapper import eu.darken.capod.common.bluetooth.ScannerMode import eu.darken.capod.common.compose.waitForState import eu.darken.capod.common.error.ErrorEventHandler @@ -244,6 +246,31 @@ fun GeneralSettingsScreen( } } +@Preview2 +@Composable +private fun GeneralSettingsScreenPreview() = PreviewWrapper { + GeneralSettingsScreen( + state = GeneralSettingsViewModel.State( + monitorMode = MonitorMode.AUTOMATIC, + scannerMode = ScannerMode.BALANCED, + showConnectedNotification = true, + keepNotificationAfterDisconnect = false, + isOffloadedFilteringDisabled = false, + isOffloadedBatchingDisabled = false, + useIndirectScanResultCallback = false, + ), + onNavigateUp = {}, + onMonitorModeSelected = {}, + onScannerModeSelected = {}, + onShowConnectedNotificationChanged = {}, + onKeepNotificationAfterDisconnectChanged = {}, + onDebugSettings = {}, + onOffloadedFilteringDisabledChanged = {}, + onOffloadedBatchingDisabledChanged = {}, + onUseIndirectScanResultCallbackChanged = {}, + ) +} + @Composable private fun ListPreferenceDialog( title: String, diff --git a/app/src/main/java/eu/darken/capod/main/ui/settings/general/debug/DebugSettingsScreen.kt b/app/src/main/java/eu/darken/capod/main/ui/settings/general/debug/DebugSettingsScreen.kt index e53eb9e8..4ed8019f 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/settings/general/debug/DebugSettingsScreen.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/settings/general/debug/DebugSettingsScreen.kt @@ -21,6 +21,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import eu.darken.capod.R +import eu.darken.capod.common.compose.Preview2 +import eu.darken.capod.common.compose.PreviewWrapper import eu.darken.capod.common.compose.waitForState import eu.darken.capod.common.error.ErrorEventHandler import eu.darken.capod.common.navigation.NavigationEventHandler @@ -118,3 +120,19 @@ fun DebugSettingsScreen( } } } + +@Preview2 +@Composable +private fun DebugSettingsScreenPreview() = PreviewWrapper { + DebugSettingsScreen( + state = DebugSettingsViewModel.State( + isDebugModeEnabled = true, + showFakeData = false, + showUnfiltered = false, + ), + onNavigateUp = {}, + onDebugModeChanged = {}, + onShowFakeDataChanged = {}, + onShowUnfilteredChanged = {}, + ) +} diff --git a/app/src/main/java/eu/darken/capod/main/ui/settings/support/SupportScreen.kt b/app/src/main/java/eu/darken/capod/main/ui/settings/support/SupportScreen.kt index ffbf79e6..92f03971 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/settings/support/SupportScreen.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/settings/support/SupportScreen.kt @@ -20,6 +20,8 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel import eu.darken.capod.R +import eu.darken.capod.common.compose.Preview2 +import eu.darken.capod.common.compose.PreviewWrapper import eu.darken.capod.common.compose.waitForState import eu.darken.capod.common.error.ErrorEventHandler import eu.darken.capod.common.navigation.NavigationEventHandler @@ -125,3 +127,16 @@ fun SupportScreen( } } } + +@Preview2 +@Composable +private fun SupportScreenPreview() = PreviewWrapper { + SupportScreen( + state = SupportViewModel.State(isRecording = false, currentLogPath = null), + onNavigateUp = {}, + onDiscord = {}, + onIssueTracker = {}, + onTroubleShooter = {}, + onDebugLogToggle = {}, + ) +} diff --git a/app/src/main/java/eu/darken/capod/main/ui/widget/WidgetConfigurationScreen.kt b/app/src/main/java/eu/darken/capod/main/ui/widget/WidgetConfigurationScreen.kt index a9c85c4d..637c8b65 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/widget/WidgetConfigurationScreen.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/widget/WidgetConfigurationScreen.kt @@ -71,6 +71,9 @@ import androidx.core.graphics.createBitmap import androidx.core.graphics.drawable.toDrawable import androidx.core.view.isVisible import eu.darken.capod.R +import eu.darken.capod.common.compose.Preview2 +import eu.darken.capod.common.compose.PreviewWrapper +import eu.darken.capod.common.compose.preview.MockPodDataProvider import eu.darken.capod.pods.core.PodDevice import eu.darken.capod.profiles.core.DeviceProfile @@ -650,6 +653,68 @@ private fun HexColorInput( ) } +@Preview2 +@Composable +private fun WidgetConfigurationScreenPreview() = 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 = {}, + ) +} + +@Preview2 +@Composable +private fun WidgetConfigurationScreenCustomPreview() = PreviewWrapper { + val profiles = listOf( + MockPodDataProvider.profile("My AirPods Pro", PodDevice.Model.AIRPODS_PRO2), + ) + WidgetConfigurationScreen( + state = WidgetConfigurationViewModel.State( + profiles = profiles, + selectedProfile = profiles.first().id, + isPro = true, + theme = WidgetTheme( + backgroundColor = 0xFF1565C0.toInt(), + foregroundColor = 0xFFFFFFFF.toInt(), + backgroundAlpha = 200, + showDeviceLabel = true, + ), + activePreset = null, + isCustomMode = true, + ), + onSelectProfile = {}, + onSelectPreset = {}, + onEnterCustomMode = { _, _ -> }, + onSetBackgroundColor = {}, + onSetForegroundColor = {}, + onSetBackgroundAlpha = {}, + onSetShowDeviceLabel = {}, + onReset = {}, + onConfirm = {}, + onCancel = {}, + ) +} + private val SWATCH_COLORS = intArrayOf( 0xFFF44336.toInt(), // Red 0xFFE91E63.toInt(), // Pink diff --git a/app/src/main/java/eu/darken/capod/profiles/ui/DeviceManagerScreen.kt b/app/src/main/java/eu/darken/capod/profiles/ui/DeviceManagerScreen.kt index 479f7523..b6c8252f 100644 --- a/app/src/main/java/eu/darken/capod/profiles/ui/DeviceManagerScreen.kt +++ b/app/src/main/java/eu/darken/capod/profiles/ui/DeviceManagerScreen.kt @@ -36,9 +36,13 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import eu.darken.capod.R +import eu.darken.capod.common.compose.Preview2 +import eu.darken.capod.common.compose.PreviewWrapper +import eu.darken.capod.common.compose.preview.MockPodDataProvider import eu.darken.capod.common.compose.waitForState import eu.darken.capod.common.error.ErrorEventHandler import eu.darken.capod.common.navigation.NavigationEventHandler +import eu.darken.capod.pods.core.PodDevice import eu.darken.capod.profiles.core.DeviceProfile @Composable @@ -121,6 +125,34 @@ fun DeviceManagerScreen( } } +@Preview2 +@Composable +private fun DeviceManagerScreenWithProfilesPreview() = 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 = {}, + ) +} + +@Preview2 +@Composable +private fun DeviceManagerScreenEmptyPreview() = PreviewWrapper { + DeviceManagerScreen( + state = DeviceManagerViewModel.State(profiles = emptyList()), + onBack = {}, + onAddDevice = {}, + onEditProfile = {}, + ) +} + @Composable private fun EmptyState( modifier: Modifier = Modifier, diff --git a/app/src/main/java/eu/darken/capod/profiles/ui/creation/DeviceProfileCreationScreen.kt b/app/src/main/java/eu/darken/capod/profiles/ui/creation/DeviceProfileCreationScreen.kt index 96f47304..271cc4a3 100644 --- a/app/src/main/java/eu/darken/capod/profiles/ui/creation/DeviceProfileCreationScreen.kt +++ b/app/src/main/java/eu/darken/capod/profiles/ui/creation/DeviceProfileCreationScreen.kt @@ -48,6 +48,8 @@ import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import eu.darken.capod.R +import eu.darken.capod.common.compose.Preview2 +import eu.darken.capod.common.compose.PreviewWrapper import eu.darken.capod.common.bluetooth.BluetoothDevice2 import eu.darken.capod.common.compose.waitForState import eu.darken.capod.common.error.ErrorEventHandler @@ -548,6 +550,66 @@ private fun UnsavedChangesDialog( ) } +@Preview2 +@Composable +private fun DeviceProfileCreationScreenNewPreview() = 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 = {}, + ) +} + +@Preview2 +@Composable +private fun DeviceProfileCreationScreenEditPreview() = PreviewWrapper { + DeviceProfileCreationScreen( + state = DeviceProfileCreationViewModel.State( + isEditMode = true, + name = "My AirPods Pro", + nameError = null, + selectedModel = PodDevice.Model.AIRPODS_PRO2, + availableModels = PodDevice.Model.entries.filter { it != PodDevice.Model.UNKNOWN }, + identityKey = null, + encryptionKey = null, + selectedDevice = null, + bondedDevices = emptyList(), + minimumSignalQuality = 0.25f, + canSave = true, + ), + onBack = {}, + onSave = {}, + onDelete = {}, + onNameChange = {}, + onModelChange = {}, + onDeviceChange = {}, + onIdentityKeyChange = {}, + onEncryptionKeyChange = {}, + onSignalQualityChange = {}, + onKeyGuide = {}, + ) +} + @Composable private fun DeleteConfirmationDialog( onDelete: () -> Unit, diff --git a/app/src/main/java/eu/darken/capod/reaction/ui/ReactionSettingsScreen.kt b/app/src/main/java/eu/darken/capod/reaction/ui/ReactionSettingsScreen.kt index 1693560d..a8797a32 100644 --- a/app/src/main/java/eu/darken/capod/reaction/ui/ReactionSettingsScreen.kt +++ b/app/src/main/java/eu/darken/capod/reaction/ui/ReactionSettingsScreen.kt @@ -41,6 +41,8 @@ import androidx.compose.ui.semantics.Role import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import eu.darken.capod.R +import eu.darken.capod.common.compose.Preview2 +import eu.darken.capod.common.compose.PreviewWrapper import eu.darken.capod.common.compose.waitForState import eu.darken.capod.common.error.ErrorEventHandler import eu.darken.capod.common.navigation.NavigationEventHandler @@ -267,3 +269,28 @@ fun ReactionSettingsScreen( ) } } + +@Preview2 +@Composable +private fun ReactionSettingsScreenPreview() = 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 = {}, + ) +} diff --git a/app/src/main/java/eu/darken/capod/reaction/ui/popup/PopUpContent.kt b/app/src/main/java/eu/darken/capod/reaction/ui/popup/PopUpContent.kt index 1784d355..fe3c25af 100644 --- a/app/src/main/java/eu/darken/capod/reaction/ui/popup/PopUpContent.kt +++ b/app/src/main/java/eu/darken/capod/reaction/ui/popup/PopUpContent.kt @@ -30,6 +30,9 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import eu.darken.capod.R +import eu.darken.capod.common.compose.Preview2 +import eu.darken.capod.common.compose.PreviewWrapper +import eu.darken.capod.common.compose.preview.MockPodDataProvider import eu.darken.capod.pods.core.DualPodDevice import eu.darken.capod.pods.core.HasCase import eu.darken.capod.pods.core.PodDevice @@ -192,3 +195,21 @@ private fun BatteryColumn( } } } + +@Preview2 +@Composable +private fun PopUpContentDualPodPreview() = PreviewWrapper { + PopUpContent(device = MockPodDataProvider.airPodsProMixed(), onClose = {}) +} + +@Preview2 +@Composable +private fun PopUpContentDualPodNoCasePreview() = PreviewWrapper { + PopUpContent(device = MockPodDataProvider.powerBeatsPro(), onClose = {}) +} + +@Preview2 +@Composable +private fun PopUpContentSinglePodPreview() = PreviewWrapper { + PopUpContent(device = MockPodDataProvider.airPodsMax(), onClose = {}) +} diff --git a/app/src/main/java/eu/darken/capod/troubleshooter/ui/TroubleShooterScreen.kt b/app/src/main/java/eu/darken/capod/troubleshooter/ui/TroubleShooterScreen.kt index 6df4d7ea..e58b1e20 100644 --- a/app/src/main/java/eu/darken/capod/troubleshooter/ui/TroubleShooterScreen.kt +++ b/app/src/main/java/eu/darken/capod/troubleshooter/ui/TroubleShooterScreen.kt @@ -30,6 +30,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import eu.darken.capod.R +import eu.darken.capod.common.compose.Preview2 +import eu.darken.capod.common.compose.PreviewWrapper import eu.darken.capod.common.compose.waitForState import eu.darken.capod.common.error.ErrorEventHandler import eu.darken.capod.common.navigation.NavigationEventHandler @@ -214,3 +216,16 @@ private fun ResultContent( style = MaterialTheme.typography.bodySmall, ) } + +@Preview2 +@Composable +private fun TroubleShooterScreenPreview() = PreviewWrapper { + TroubleShooterScreen( + state = TroubleShooterViewModel.State( + bleState = TroubleShooterViewModel.BleState.Intro(), + ), + onStart = {}, + onRetry = {}, + onNavigateUp = {}, + ) +}