From 5240431348687dfdc3cc766cd081b05f55a9841b Mon Sep 17 00:00:00 2001 From: Oscar Ronberg Date: Tue, 25 Feb 2025 04:56:19 +1100 Subject: [PATCH] Add style to BinaryClock for 24-hour time (#1190) * Add style to BinaryClock for 24-hour time * Use global time format preference for all clock widget styles * Add support for time format preference to remaining clock styles --------- Co-authored-by: MM20 <15646950+MM2-0@users.noreply.github.com> --- .../ui/launcher/widgets/clock/ClockWidget.kt | 121 ++++++++++++++---- .../widgets/clock/clocks/BinaryClock.kt | 23 ++-- .../widgets/clock/clocks/DigitalClock1.kt | 5 +- .../widgets/clock/clocks/DigitalClock2.kt | 5 +- .../widgets/clock/clocks/OrbitClock.kt | 3 +- .../widgets/clock/clocks/SegmentClock.kt | 3 +- .../ClockWidgetSettingsScreenVM.kt | 10 +- .../de/mm20/launcher2/ui/utils/TimeFormat.kt | 10 ++ core/i18n/src/main/res/values/strings.xml | 4 + .../preferences/LauncherSettingsData.kt | 8 ++ .../preferences/ui/ClockWidgetSettings.kt | 10 ++ 11 files changed, 158 insertions(+), 44 deletions(-) create mode 100644 app/ui/src/main/java/de/mm20/launcher2/ui/utils/TimeFormat.kt diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/ClockWidget.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/ClockWidget.kt index c05688b8..2416990c 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/ClockWidget.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/ClockWidget.kt @@ -31,6 +31,7 @@ import androidx.compose.material.icons.rounded.Height import androidx.compose.material.icons.rounded.HorizontalSplit import androidx.compose.material.icons.rounded.LightMode import androidx.compose.material.icons.rounded.MusicNote +import androidx.compose.material.icons.rounded.Timer import androidx.compose.material.icons.rounded.Today import androidx.compose.material.icons.rounded.Tune import androidx.compose.material.icons.rounded.VerticalSplit @@ -67,6 +68,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel import de.mm20.launcher2.preferences.ClockWidgetAlignment import de.mm20.launcher2.preferences.ClockWidgetColors import de.mm20.launcher2.preferences.ClockWidgetStyle +import de.mm20.launcher2.preferences.TimeFormat import de.mm20.launcher2.preferences.ui.ClockWidgetSettings import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.base.LocalTime @@ -83,6 +85,7 @@ import de.mm20.launcher2.ui.launcher.widgets.clock.clocks.SegmentClock import de.mm20.launcher2.ui.launcher.widgets.clock.parts.PartProvider import de.mm20.launcher2.ui.locals.LocalPreferDarkContentOverWallpaper import de.mm20.launcher2.ui.settings.clockwidget.ClockWidgetSettingsScreenVM +import de.mm20.launcher2.ui.utils.isTwentyFourHours import org.koin.androidx.compose.inject @Composable @@ -164,7 +167,8 @@ fun ClockWidget( Box( modifier = Modifier .then(if (fillScreenHeight) Modifier.weight(1f) else Modifier) - .fillMaxWidth().padding(horizontal = if (compact == true) 0.dp else 24.dp), + .fillMaxWidth() + .padding(horizontal = if (compact == true) 0.dp else 24.dp), contentAlignment = when (alignment) { ClockWidgetAlignment.Center -> Alignment.Center ClockWidgetAlignment.Top -> Alignment.TopCenter @@ -265,34 +269,42 @@ fun Clock( darkColors: Boolean = false ) { val time = LocalTime.current + val context = LocalContext.current val clockSettings: ClockWidgetSettings by inject() val showSeconds by clockSettings.showSeconds.collectAsState(initial = false) val useThemeColor by clockSettings.useThemeColor.collectAsState(initial = false) + val timeFormat by clockSettings.timeFormat.collectAsState(null) + + if (timeFormat == null) return + + val isTwentyFourHours = timeFormat!!.isTwentyFourHours(context) when (style) { is ClockWidgetStyle.Digital1 -> DigitalClock1( - time, - style, - compact, - showSeconds, - useThemeColor, - darkColors + time = time, + compact = compact, + showSeconds = showSeconds, + twentyFourHours = isTwentyFourHours, + useThemeColor = useThemeColor, + darkColors = darkColors, ) is ClockWidgetStyle.Digital2 -> DigitalClock2( - time, - compact, - showSeconds, - useThemeColor, - darkColors + time = time, + compact = compact, + showSeconds = showSeconds, + twentyFourHours = isTwentyFourHours, + useThemeColor = useThemeColor, + darkColors = darkColors, ) is ClockWidgetStyle.Binary -> BinaryClock( - time, - compact, - showSeconds, - useThemeColor, - darkColors + time = time, + compact = compact, + showSeconds = showSeconds, + twentyFourHours = isTwentyFourHours, + useThemeColor = useThemeColor, + darkColors = darkColors, ) is ClockWidgetStyle.Analog -> AnalogClock( @@ -304,19 +316,21 @@ fun Clock( ) is ClockWidgetStyle.Orbit -> OrbitClock( - time, - compact, - showSeconds, - useThemeColor, - darkColors + time = time, + compact = compact, + showSeconds = showSeconds, + twentyFourHours = isTwentyFourHours, + useThemeColor = useThemeColor, + darkColors = darkColors, ) is ClockWidgetStyle.Segment -> SegmentClock( - time, - compact, - showSeconds, - useThemeColor, - darkColors + time = time, + compact = compact, + showSeconds = showSeconds, + twentyFourHours = isTwentyFourHours, + useThemeColor = useThemeColor, + darkColors = darkColors, ) is ClockWidgetStyle.Custom -> CustomClock(style, compact, useThemeColor, darkColors) @@ -349,6 +363,7 @@ fun ConfigureClockWidgetSheet( val fillHeight by viewModel.fillHeight.collectAsState() val alignment by viewModel.alignment.collectAsState() val showSeconds by viewModel.showSeconds.collectAsState() + val timeFormat by viewModel.timeFormat.collectAsState() val useAccentColor by viewModel.useThemeColor.collectAsState() val parts by viewModel.parts.collectAsState() @@ -488,13 +503,63 @@ fun ConfigureClockWidgetSheet( AnimatedVisibility(compact == false && style !is ClockWidgetStyle.Custom) { SwitchPreference( title = stringResource(R.string.preference_clock_widget_show_seconds), - icon = Icons.Rounded.AccessTime, + icon = Icons.Rounded.Timer, value = showSeconds, onValueChanged = { viewModel.setShowSeconds(it) } ) } + AnimatedVisibility( + style !is ClockWidgetStyle.Analog && + style !is ClockWidgetStyle.Custom && + style !is ClockWidgetStyle.Empty + ) { + var showDropdown by remember { mutableStateOf(false) } + Preference( + title = stringResource(R.string.preference_clock_widget_time_format), + summary = when (timeFormat) { + TimeFormat.TwelveHour -> stringResource(R.string.preference_clock_widget_time_format_12h) + TimeFormat.TwentyFourHour -> stringResource(R.string.preference_clock_widget_time_format_24h) + TimeFormat.System -> stringResource(R.string.preference_clock_widget_time_format_system) + }, + icon = Icons.Rounded.AccessTime, + onClick = { + showDropdown = true + } + ) + DropdownMenu( + expanded = showDropdown, + onDismissRequest = { showDropdown = false }) { + DropdownMenuItem( + text = { + Text(stringResource(R.string.preference_clock_widget_time_format_system)) + }, + onClick = { + viewModel.setTimeFormat(TimeFormat.System) + showDropdown = false + } + ) + DropdownMenuItem( + text = { + Text(stringResource(R.string.preference_clock_widget_time_format_24h)) + }, + onClick = { + viewModel.setTimeFormat(TimeFormat.TwentyFourHour) + showDropdown = false + } + ) + DropdownMenuItem( + text = { + Text(stringResource(R.string.preference_clock_widget_time_format_12h)) + }, + onClick = { + viewModel.setTimeFormat(TimeFormat.TwelveHour) + showDropdown = false + } + ) + } + } } } OutlinedCard( diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/BinaryClock.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/BinaryClock.kt index 7eeb70f3..72b39ad9 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/BinaryClock.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/BinaryClock.kt @@ -13,14 +13,19 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp +import de.mm20.launcher2.preferences.ClockWidgetStyle +import de.mm20.launcher2.preferences.TimeFormat import de.mm20.launcher2.ui.locals.LocalDarkTheme +import de.mm20.launcher2.ui.utils.isTwentyFourHours import java.util.Calendar @Composable fun BinaryClock( time: Long, compact: Boolean, + twentyFourHours: Boolean, showSeconds: Boolean, useThemeColor: Boolean, darkColors: Boolean, @@ -30,8 +35,8 @@ fun BinaryClock( date.timeInMillis = time val second = date[Calendar.SECOND] val minute = date[Calendar.MINUTE] - var hour = date[Calendar.HOUR] - if (hour == 0) hour = 12 + var hour = date[if(!twentyFourHours) Calendar.HOUR else Calendar.HOUR_OF_DAY] + if (!twentyFourHours && hour == 0) hour = 12 val color = if (useThemeColor) { if (!darkColors) { @@ -56,11 +61,11 @@ fun BinaryClock( Row( modifier = Modifier.padding(start = 0.dp, top = 24.dp, end = 0.dp, bottom = 6.dp) ) { - for (i in 0 until 10) { - val active = if (i < 4) { - hour and (1 shl (3 - i)) != 0 + for (i in 0 until if (twentyFourHours) 11 else 10) { + val active = if (i < if (twentyFourHours) 5 else 4) { + hour and (1 shl ((if (twentyFourHours) 4 else 3) - i)) != 0 } else { - minute and (1 shl (9 - i)) != 0 + minute and (1 shl ((if (twentyFourHours) 10 else 9) - i)) != 0 } Box( modifier = Modifier @@ -70,7 +75,7 @@ fun BinaryClock( if (active) color else disabledColor ) ) - if (i == 3) { + if (i == if (twentyFourHours) 4 else 3) { Box(Modifier.size(8.dp)) } } @@ -98,8 +103,8 @@ fun BinaryClock( horizontalAlignment = Alignment.End ) { Row { - for (i in 0 until 4) { - val active = hour and (1 shl (3 - i)) != 0 + for (i in 0 until if (twentyFourHours) 5 else 4) { + val active = hour and (1 shl ((if (twentyFourHours) 4 else 3) - i)) != 0 Box( modifier = Modifier .padding( 4.dp) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/DigitalClock1.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/DigitalClock1.kt index 04cb2eee..b6b97790 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/DigitalClock1.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/DigitalClock1.kt @@ -33,6 +33,7 @@ fun DigitalClock1( time: Long, style: ClockWidgetStyle.Digital1 = ClockWidgetStyle.Digital1(), compact: Boolean, + twentyFourHours: Boolean, showSeconds: Boolean, useThemeColor: Boolean, darkColors: Boolean, @@ -40,10 +41,10 @@ fun DigitalClock1( val verticalLayout = !compact val format = SimpleDateFormat( when { - DateFormat.is24HourFormat(LocalContext.current) && verticalLayout -> { + twentyFourHours && verticalLayout -> { "HH\nmm" } - DateFormat.is24HourFormat(LocalContext.current) -> { + twentyFourHours -> { "HH mm" } verticalLayout -> { diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/DigitalClock2.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/DigitalClock2.kt index 63746452..5050e710 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/DigitalClock2.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/DigitalClock2.kt @@ -21,6 +21,7 @@ fun DigitalClock2( time: Long, compact: Boolean, showSeconds: Boolean, + twentyFourHours: Boolean, useThemeColor: Boolean, darkColors: Boolean, ) { @@ -40,14 +41,14 @@ fun DigitalClock2( } val formatString = if (verticalLayout && showSeconds) { - if (DateFormat.is24HourFormat(LocalContext.current)) { + if (twentyFourHours) { "HH:mm:ss" } else { "hh:mm:ss" } } else { - if (DateFormat.is24HourFormat(LocalContext.current)) { + if (twentyFourHours) { "HH:mm" } else { diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/OrbitClock.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/OrbitClock.kt index cea5ddde..1305f906 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/OrbitClock.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/OrbitClock.kt @@ -48,6 +48,7 @@ fun OrbitClock( time: Long, compact: Boolean, showSeconds: Boolean, + twentyFourHours: Boolean, useThemeColor: Boolean, darkColors: Boolean, ) { @@ -59,7 +60,7 @@ fun OrbitClock( val minute = parsed.minute val hour = parsed.hour val formattedHour = ( - if (DateFormat.is24HourFormat(LocalContext.current)) + if (twentyFourHours) hour else { ((hour + 11) % 12) + 1 diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/SegmentClock.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/SegmentClock.kt index 5120ca28..524a8934 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/SegmentClock.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/SegmentClock.kt @@ -52,11 +52,12 @@ fun SegmentClock( time: Long, compact: Boolean, showSeconds: Boolean, + twentyFourHours: Boolean, useThemeColor: Boolean, darkColors: Boolean, ) { val parsed = Instant.ofEpochMilli(time).atZone(ZoneId.systemDefault()) - val hour = if (DateFormat.is24HourFormat(LocalContext.current)) parsed.hour else (((parsed.hour + 11) % 12) + 1) + val hour = if (twentyFourHours) parsed.hour else (((parsed.hour + 11) % 12) + 1) val minute = parsed.minute val second = parsed.second diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/clockwidget/ClockWidgetSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/clockwidget/ClockWidgetSettingsScreenVM.kt index 6781420e..e123b7f4 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/clockwidget/ClockWidgetSettingsScreenVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/clockwidget/ClockWidgetSettingsScreenVM.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.viewModelScope import de.mm20.launcher2.preferences.ClockWidgetAlignment import de.mm20.launcher2.preferences.ClockWidgetColors import de.mm20.launcher2.preferences.ClockWidgetStyle +import de.mm20.launcher2.preferences.TimeFormat import de.mm20.launcher2.preferences.ui.ClockWidgetSettings import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine @@ -21,7 +22,7 @@ class ClockWidgetSettingsScreenVM : ViewModel(), KoinComponent { settings.setCompact(compact) } - val availableClockStyles = combine(settings.digital1, settings.custom) {digital1, custom -> + val availableClockStyles = combine(settings.digital1, settings.custom) { digital1, custom -> listOf( digital1, ClockWidgetStyle.Digital2, @@ -54,6 +55,13 @@ class ClockWidgetSettingsScreenVM : ViewModel(), KoinComponent { settings.setShowSeconds(showSeconds) } + val timeFormat = settings.timeFormat + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), TimeFormat.System) + + fun setTimeFormat(timeFormat: TimeFormat) { + settings.setTimeFormat(timeFormat) + } + val useThemeColor = settings.useThemeColor .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/utils/TimeFormat.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/utils/TimeFormat.kt new file mode 100644 index 00000000..b9ec7416 --- /dev/null +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/utils/TimeFormat.kt @@ -0,0 +1,10 @@ +package de.mm20.launcher2.ui.utils + +import android.content.Context +import android.text.format.DateFormat +import de.mm20.launcher2.preferences.TimeFormat +import de.mm20.launcher2.preferences.TimeFormat.TwentyFourHour + +fun TimeFormat.isTwentyFourHours(context: Context): Boolean { + return this == TimeFormat.TwentyFourHour || this == TimeFormat.System && DateFormat.is24HourFormat(context) +} \ No newline at end of file diff --git a/core/i18n/src/main/res/values/strings.xml b/core/i18n/src/main/res/values/strings.xml index e234596c..8def4023 100644 --- a/core/i18n/src/main/res/values/strings.xml +++ b/core/i18n/src/main/res/values/strings.xml @@ -567,6 +567,10 @@ Default Compact Show seconds + Time format + 24-hour + 12-hour + System default Use theme color Fill screen height Alignment diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherSettingsData.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherSettingsData.kt index 8456e566..ede1fec0 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherSettingsData.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherSettingsData.kt @@ -34,6 +34,7 @@ data class LauncherSettingsData internal constructor( val clockWidgetCustom: ClockWidgetStyle.Custom = ClockWidgetStyle.Custom(), val clockWidgetColors: ClockWidgetColors = ClockWidgetColors.Auto, val clockWidgetShowSeconds: Boolean = false, + val clockWidgetTimeFormat: TimeFormat = TimeFormat.System, val clockWidgetUseThemeColor: Boolean = false, val clockWidgetAlarmPart: Boolean = true, val clockWidgetBatteryPart: Boolean = true, @@ -408,4 +409,11 @@ enum class KeyboardFilterBarItem { @SerialName("events") Events, @SerialName("tools") Tools, @SerialName("hidden") HiddenResults, +} + +@Serializable +enum class TimeFormat { + @SerialName("system") System, + @SerialName("12h") TwelveHour, + @SerialName("24h") TwentyFourHour } \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/ClockWidgetSettings.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/ClockWidgetSettings.kt index 3d473c93..d12094e6 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/ClockWidgetSettings.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/ClockWidgetSettings.kt @@ -5,6 +5,7 @@ import de.mm20.launcher2.preferences.ClockWidgetColors import de.mm20.launcher2.preferences.ClockWidgetStyle import de.mm20.launcher2.preferences.ClockWidgetStyleEnum import de.mm20.launcher2.preferences.LauncherDataStore +import de.mm20.launcher2.preferences.TimeFormat import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map @@ -137,6 +138,15 @@ class ClockWidgetSettings internal constructor( } } + val timeFormat + get() = launcherDataStore.data.map { it.clockWidgetTimeFormat } + + fun setTimeFormat(timeFormat: TimeFormat) { + launcherDataStore.update { + it.copy(clockWidgetTimeFormat = timeFormat) + } + } + val useThemeColor get() = launcherDataStore.data.map { it.clockWidgetUseThemeColor }