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>
This commit is contained in:
Oscar Ronberg 2025-02-25 04:56:19 +11:00 committed by GitHub
parent 4330b2a712
commit 5240431348
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 158 additions and 44 deletions

View File

@ -31,6 +31,7 @@ import androidx.compose.material.icons.rounded.Height
import androidx.compose.material.icons.rounded.HorizontalSplit import androidx.compose.material.icons.rounded.HorizontalSplit
import androidx.compose.material.icons.rounded.LightMode import androidx.compose.material.icons.rounded.LightMode
import androidx.compose.material.icons.rounded.MusicNote 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.Today
import androidx.compose.material.icons.rounded.Tune import androidx.compose.material.icons.rounded.Tune
import androidx.compose.material.icons.rounded.VerticalSplit 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.ClockWidgetAlignment
import de.mm20.launcher2.preferences.ClockWidgetColors import de.mm20.launcher2.preferences.ClockWidgetColors
import de.mm20.launcher2.preferences.ClockWidgetStyle import de.mm20.launcher2.preferences.ClockWidgetStyle
import de.mm20.launcher2.preferences.TimeFormat
import de.mm20.launcher2.preferences.ui.ClockWidgetSettings import de.mm20.launcher2.preferences.ui.ClockWidgetSettings
import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.base.LocalTime 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.launcher.widgets.clock.parts.PartProvider
import de.mm20.launcher2.ui.locals.LocalPreferDarkContentOverWallpaper import de.mm20.launcher2.ui.locals.LocalPreferDarkContentOverWallpaper
import de.mm20.launcher2.ui.settings.clockwidget.ClockWidgetSettingsScreenVM import de.mm20.launcher2.ui.settings.clockwidget.ClockWidgetSettingsScreenVM
import de.mm20.launcher2.ui.utils.isTwentyFourHours
import org.koin.androidx.compose.inject import org.koin.androidx.compose.inject
@Composable @Composable
@ -164,7 +167,8 @@ fun ClockWidget(
Box( Box(
modifier = Modifier modifier = Modifier
.then(if (fillScreenHeight) Modifier.weight(1f) else 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) { contentAlignment = when (alignment) {
ClockWidgetAlignment.Center -> Alignment.Center ClockWidgetAlignment.Center -> Alignment.Center
ClockWidgetAlignment.Top -> Alignment.TopCenter ClockWidgetAlignment.Top -> Alignment.TopCenter
@ -265,34 +269,42 @@ fun Clock(
darkColors: Boolean = false darkColors: Boolean = false
) { ) {
val time = LocalTime.current val time = LocalTime.current
val context = LocalContext.current
val clockSettings: ClockWidgetSettings by inject() val clockSettings: ClockWidgetSettings by inject()
val showSeconds by clockSettings.showSeconds.collectAsState(initial = false) val showSeconds by clockSettings.showSeconds.collectAsState(initial = false)
val useThemeColor by clockSettings.useThemeColor.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) { when (style) {
is ClockWidgetStyle.Digital1 -> DigitalClock1( is ClockWidgetStyle.Digital1 -> DigitalClock1(
time, time = time,
style, compact = compact,
compact, showSeconds = showSeconds,
showSeconds, twentyFourHours = isTwentyFourHours,
useThemeColor, useThemeColor = useThemeColor,
darkColors darkColors = darkColors,
) )
is ClockWidgetStyle.Digital2 -> DigitalClock2( is ClockWidgetStyle.Digital2 -> DigitalClock2(
time, time = time,
compact, compact = compact,
showSeconds, showSeconds = showSeconds,
useThemeColor, twentyFourHours = isTwentyFourHours,
darkColors useThemeColor = useThemeColor,
darkColors = darkColors,
) )
is ClockWidgetStyle.Binary -> BinaryClock( is ClockWidgetStyle.Binary -> BinaryClock(
time, time = time,
compact, compact = compact,
showSeconds, showSeconds = showSeconds,
useThemeColor, twentyFourHours = isTwentyFourHours,
darkColors useThemeColor = useThemeColor,
darkColors = darkColors,
) )
is ClockWidgetStyle.Analog -> AnalogClock( is ClockWidgetStyle.Analog -> AnalogClock(
@ -304,19 +316,21 @@ fun Clock(
) )
is ClockWidgetStyle.Orbit -> OrbitClock( is ClockWidgetStyle.Orbit -> OrbitClock(
time, time = time,
compact, compact = compact,
showSeconds, showSeconds = showSeconds,
useThemeColor, twentyFourHours = isTwentyFourHours,
darkColors useThemeColor = useThemeColor,
darkColors = darkColors,
) )
is ClockWidgetStyle.Segment -> SegmentClock( is ClockWidgetStyle.Segment -> SegmentClock(
time, time = time,
compact, compact = compact,
showSeconds, showSeconds = showSeconds,
useThemeColor, twentyFourHours = isTwentyFourHours,
darkColors useThemeColor = useThemeColor,
darkColors = darkColors,
) )
is ClockWidgetStyle.Custom -> CustomClock(style, compact, useThemeColor, darkColors) is ClockWidgetStyle.Custom -> CustomClock(style, compact, useThemeColor, darkColors)
@ -349,6 +363,7 @@ fun ConfigureClockWidgetSheet(
val fillHeight by viewModel.fillHeight.collectAsState() val fillHeight by viewModel.fillHeight.collectAsState()
val alignment by viewModel.alignment.collectAsState() val alignment by viewModel.alignment.collectAsState()
val showSeconds by viewModel.showSeconds.collectAsState() val showSeconds by viewModel.showSeconds.collectAsState()
val timeFormat by viewModel.timeFormat.collectAsState()
val useAccentColor by viewModel.useThemeColor.collectAsState() val useAccentColor by viewModel.useThemeColor.collectAsState()
val parts by viewModel.parts.collectAsState() val parts by viewModel.parts.collectAsState()
@ -488,13 +503,63 @@ fun ConfigureClockWidgetSheet(
AnimatedVisibility(compact == false && style !is ClockWidgetStyle.Custom) { AnimatedVisibility(compact == false && style !is ClockWidgetStyle.Custom) {
SwitchPreference( SwitchPreference(
title = stringResource(R.string.preference_clock_widget_show_seconds), title = stringResource(R.string.preference_clock_widget_show_seconds),
icon = Icons.Rounded.AccessTime, icon = Icons.Rounded.Timer,
value = showSeconds, value = showSeconds,
onValueChanged = { onValueChanged = {
viewModel.setShowSeconds(it) 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( OutlinedCard(

View File

@ -13,14 +13,19 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp 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.locals.LocalDarkTheme
import de.mm20.launcher2.ui.utils.isTwentyFourHours
import java.util.Calendar import java.util.Calendar
@Composable @Composable
fun BinaryClock( fun BinaryClock(
time: Long, time: Long,
compact: Boolean, compact: Boolean,
twentyFourHours: Boolean,
showSeconds: Boolean, showSeconds: Boolean,
useThemeColor: Boolean, useThemeColor: Boolean,
darkColors: Boolean, darkColors: Boolean,
@ -30,8 +35,8 @@ fun BinaryClock(
date.timeInMillis = time date.timeInMillis = time
val second = date[Calendar.SECOND] val second = date[Calendar.SECOND]
val minute = date[Calendar.MINUTE] val minute = date[Calendar.MINUTE]
var hour = date[Calendar.HOUR] var hour = date[if(!twentyFourHours) Calendar.HOUR else Calendar.HOUR_OF_DAY]
if (hour == 0) hour = 12 if (!twentyFourHours && hour == 0) hour = 12
val color = if (useThemeColor) { val color = if (useThemeColor) {
if (!darkColors) { if (!darkColors) {
@ -56,11 +61,11 @@ fun BinaryClock(
Row( Row(
modifier = Modifier.padding(start = 0.dp, top = 24.dp, end = 0.dp, bottom = 6.dp) modifier = Modifier.padding(start = 0.dp, top = 24.dp, end = 0.dp, bottom = 6.dp)
) { ) {
for (i in 0 until 10) { for (i in 0 until if (twentyFourHours) 11 else 10) {
val active = if (i < 4) { val active = if (i < if (twentyFourHours) 5 else 4) {
hour and (1 shl (3 - i)) != 0 hour and (1 shl ((if (twentyFourHours) 4 else 3) - i)) != 0
} else { } else {
minute and (1 shl (9 - i)) != 0 minute and (1 shl ((if (twentyFourHours) 10 else 9) - i)) != 0
} }
Box( Box(
modifier = Modifier modifier = Modifier
@ -70,7 +75,7 @@ fun BinaryClock(
if (active) color else disabledColor if (active) color else disabledColor
) )
) )
if (i == 3) { if (i == if (twentyFourHours) 4 else 3) {
Box(Modifier.size(8.dp)) Box(Modifier.size(8.dp))
} }
} }
@ -98,8 +103,8 @@ fun BinaryClock(
horizontalAlignment = Alignment.End horizontalAlignment = Alignment.End
) { ) {
Row { Row {
for (i in 0 until 4) { for (i in 0 until if (twentyFourHours) 5 else 4) {
val active = hour and (1 shl (3 - i)) != 0 val active = hour and (1 shl ((if (twentyFourHours) 4 else 3) - i)) != 0
Box( Box(
modifier = Modifier modifier = Modifier
.padding( 4.dp) .padding( 4.dp)

View File

@ -33,6 +33,7 @@ fun DigitalClock1(
time: Long, time: Long,
style: ClockWidgetStyle.Digital1 = ClockWidgetStyle.Digital1(), style: ClockWidgetStyle.Digital1 = ClockWidgetStyle.Digital1(),
compact: Boolean, compact: Boolean,
twentyFourHours: Boolean,
showSeconds: Boolean, showSeconds: Boolean,
useThemeColor: Boolean, useThemeColor: Boolean,
darkColors: Boolean, darkColors: Boolean,
@ -40,10 +41,10 @@ fun DigitalClock1(
val verticalLayout = !compact val verticalLayout = !compact
val format = SimpleDateFormat( val format = SimpleDateFormat(
when { when {
DateFormat.is24HourFormat(LocalContext.current) && verticalLayout -> { twentyFourHours && verticalLayout -> {
"HH\nmm" "HH\nmm"
} }
DateFormat.is24HourFormat(LocalContext.current) -> { twentyFourHours -> {
"HH mm" "HH mm"
} }
verticalLayout -> { verticalLayout -> {

View File

@ -21,6 +21,7 @@ fun DigitalClock2(
time: Long, time: Long,
compact: Boolean, compact: Boolean,
showSeconds: Boolean, showSeconds: Boolean,
twentyFourHours: Boolean,
useThemeColor: Boolean, useThemeColor: Boolean,
darkColors: Boolean, darkColors: Boolean,
) { ) {
@ -40,14 +41,14 @@ fun DigitalClock2(
} }
val formatString = if (verticalLayout && showSeconds) { val formatString = if (verticalLayout && showSeconds) {
if (DateFormat.is24HourFormat(LocalContext.current)) { if (twentyFourHours) {
"HH:mm:ss" "HH:mm:ss"
} }
else { else {
"hh:mm:ss" "hh:mm:ss"
} }
} else { } else {
if (DateFormat.is24HourFormat(LocalContext.current)) { if (twentyFourHours) {
"HH:mm" "HH:mm"
} }
else { else {

View File

@ -48,6 +48,7 @@ fun OrbitClock(
time: Long, time: Long,
compact: Boolean, compact: Boolean,
showSeconds: Boolean, showSeconds: Boolean,
twentyFourHours: Boolean,
useThemeColor: Boolean, useThemeColor: Boolean,
darkColors: Boolean, darkColors: Boolean,
) { ) {
@ -59,7 +60,7 @@ fun OrbitClock(
val minute = parsed.minute val minute = parsed.minute
val hour = parsed.hour val hour = parsed.hour
val formattedHour = ( val formattedHour = (
if (DateFormat.is24HourFormat(LocalContext.current)) if (twentyFourHours)
hour hour
else { else {
((hour + 11) % 12) + 1 ((hour + 11) % 12) + 1

View File

@ -52,11 +52,12 @@ fun SegmentClock(
time: Long, time: Long,
compact: Boolean, compact: Boolean,
showSeconds: Boolean, showSeconds: Boolean,
twentyFourHours: Boolean,
useThemeColor: Boolean, useThemeColor: Boolean,
darkColors: Boolean, darkColors: Boolean,
) { ) {
val parsed = Instant.ofEpochMilli(time).atZone(ZoneId.systemDefault()) 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 minute = parsed.minute
val second = parsed.second val second = parsed.second

View File

@ -5,6 +5,7 @@ import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.preferences.ClockWidgetAlignment import de.mm20.launcher2.preferences.ClockWidgetAlignment
import de.mm20.launcher2.preferences.ClockWidgetColors import de.mm20.launcher2.preferences.ClockWidgetColors
import de.mm20.launcher2.preferences.ClockWidgetStyle import de.mm20.launcher2.preferences.ClockWidgetStyle
import de.mm20.launcher2.preferences.TimeFormat
import de.mm20.launcher2.preferences.ui.ClockWidgetSettings import de.mm20.launcher2.preferences.ui.ClockWidgetSettings
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
@ -21,7 +22,7 @@ class ClockWidgetSettingsScreenVM : ViewModel(), KoinComponent {
settings.setCompact(compact) settings.setCompact(compact)
} }
val availableClockStyles = combine(settings.digital1, settings.custom) {digital1, custom -> val availableClockStyles = combine(settings.digital1, settings.custom) { digital1, custom ->
listOf( listOf(
digital1, digital1,
ClockWidgetStyle.Digital2, ClockWidgetStyle.Digital2,
@ -54,6 +55,13 @@ class ClockWidgetSettingsScreenVM : ViewModel(), KoinComponent {
settings.setShowSeconds(showSeconds) settings.setShowSeconds(showSeconds)
} }
val timeFormat = settings.timeFormat
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), TimeFormat.System)
fun setTimeFormat(timeFormat: TimeFormat) {
settings.setTimeFormat(timeFormat)
}
val useThemeColor = settings.useThemeColor val useThemeColor = settings.useThemeColor
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)

View File

@ -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)
}

View File

@ -567,6 +567,10 @@
<string name="preference_clockwidget_layout_vertical">Default</string> <string name="preference_clockwidget_layout_vertical">Default</string>
<string name="preference_clockwidget_layout_horizontal">Compact</string> <string name="preference_clockwidget_layout_horizontal">Compact</string>
<string name="preference_clock_widget_show_seconds">Show seconds</string> <string name="preference_clock_widget_show_seconds">Show seconds</string>
<string name="preference_clock_widget_time_format">Time format</string>
<string name="preference_clock_widget_time_format_24h">24-hour</string>
<string name="preference_clock_widget_time_format_12h">12-hour</string>
<string name="preference_clock_widget_time_format_system">System default</string>
<string name="widget_use_theme_colors">Use theme color</string> <string name="widget_use_theme_colors">Use theme color</string>
<string name="preference_clock_widget_fill_height">Fill screen height</string> <string name="preference_clock_widget_fill_height">Fill screen height</string>
<string name="preference_clock_widget_alignment">Alignment</string> <string name="preference_clock_widget_alignment">Alignment</string>

View File

@ -34,6 +34,7 @@ data class LauncherSettingsData internal constructor(
val clockWidgetCustom: ClockWidgetStyle.Custom = ClockWidgetStyle.Custom(), val clockWidgetCustom: ClockWidgetStyle.Custom = ClockWidgetStyle.Custom(),
val clockWidgetColors: ClockWidgetColors = ClockWidgetColors.Auto, val clockWidgetColors: ClockWidgetColors = ClockWidgetColors.Auto,
val clockWidgetShowSeconds: Boolean = false, val clockWidgetShowSeconds: Boolean = false,
val clockWidgetTimeFormat: TimeFormat = TimeFormat.System,
val clockWidgetUseThemeColor: Boolean = false, val clockWidgetUseThemeColor: Boolean = false,
val clockWidgetAlarmPart: Boolean = true, val clockWidgetAlarmPart: Boolean = true,
val clockWidgetBatteryPart: Boolean = true, val clockWidgetBatteryPart: Boolean = true,
@ -408,4 +409,11 @@ enum class KeyboardFilterBarItem {
@SerialName("events") Events, @SerialName("events") Events,
@SerialName("tools") Tools, @SerialName("tools") Tools,
@SerialName("hidden") HiddenResults, @SerialName("hidden") HiddenResults,
}
@Serializable
enum class TimeFormat {
@SerialName("system") System,
@SerialName("12h") TwelveHour,
@SerialName("24h") TwentyFourHour
} }

View File

@ -5,6 +5,7 @@ import de.mm20.launcher2.preferences.ClockWidgetColors
import de.mm20.launcher2.preferences.ClockWidgetStyle import de.mm20.launcher2.preferences.ClockWidgetStyle
import de.mm20.launcher2.preferences.ClockWidgetStyleEnum import de.mm20.launcher2.preferences.ClockWidgetStyleEnum
import de.mm20.launcher2.preferences.LauncherDataStore import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.TimeFormat
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map 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 val useThemeColor
get() = launcherDataStore.data.map { it.clockWidgetUseThemeColor } get() = launcherDataStore.data.map { it.clockWidgetUseThemeColor }