use 12h/24h preference in calendar results

This commit is contained in:
MM20
2025-11-30 14:20:25 +01:00
parent 054e1c8d92
commit 7566170e0b
8 changed files with 126 additions and 88 deletions

View File

@@ -1,30 +1,44 @@
package de.mm20.launcher2.ui.base
import android.icu.util.LocaleData
import android.icu.util.ULocale
import android.text.format.DateFormat
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import de.mm20.launcher2.ktx.isAtLeastApiLevel
import de.mm20.launcher2.preferences.IconShape
import de.mm20.launcher2.preferences.MeasurementSystem
import de.mm20.launcher2.preferences.TimeFormat
import de.mm20.launcher2.preferences.ui.CardStyle
import de.mm20.launcher2.preferences.ui.GridSettings
import de.mm20.launcher2.preferences.ui.LocaleSettings
import de.mm20.launcher2.preferences.ui.UiSettings
import de.mm20.launcher2.ui.component.ProvideIconShape
import de.mm20.launcher2.ui.locals.LocalCardStyle
import de.mm20.launcher2.ui.locals.LocalFavoritesEnabled
import de.mm20.launcher2.ui.locals.LocalGridSettings
import de.mm20.launcher2.ui.locals.LocalMeasurementSystem
import de.mm20.launcher2.ui.locals.LocalTimeFormat
import de.mm20.launcher2.widgets.FavoritesWidget
import de.mm20.launcher2.widgets.WidgetRepository
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import org.koin.compose.koinInject
@Composable
fun ProvideSettings(
content: @Composable () -> Unit
) {
val context = LocalContext.current
val settings: UiSettings = koinInject()
val widgetRepository: WidgetRepository = koinInject()
val localeSettings: LocaleSettings = koinInject()
val iconShape by remember {
settings.iconShape.distinctUntilChanged()
@@ -41,9 +55,42 @@ fun ProvideSettings(
settings.gridSettings.distinctUntilChanged()
}.collectAsState(GridSettings())
val timeFormat by remember(context) {
localeSettings.timeFormat
.map {
if (it == TimeFormat.System) {
if (DateFormat.is24HourFormat(context)) TimeFormat.TwentyFourHour else TimeFormat.TwelveHour
} else {
it
}
}.distinctUntilChanged()
}.collectAsState(null)
val measurementSystem by remember {
localeSettings.measurementSystem.map { ms ->
if (ms == MeasurementSystem.System) {
return@map if (isAtLeastApiLevel(28)) {
val systemMs = LocaleData.getMeasurementSystem(ULocale.getDefault())
when (systemMs) {
LocaleData.MeasurementSystem.UK -> MeasurementSystem.UnitedKingdom
LocaleData.MeasurementSystem.US -> MeasurementSystem.UnitedStates
else -> MeasurementSystem.Metric
}
} else {
MeasurementSystem.Metric
}
}
return@map ms
}.distinctUntilChanged()
}.collectAsState(null)
if (timeFormat == null || measurementSystem == null) return
CompositionLocalProvider(
LocalFavoritesEnabled provides favoritesEnabled,
LocalGridSettings provides gridSettings,
LocalTimeFormat provides timeFormat!!,
LocalMeasurementSystem provides measurementSystem!!
) {
ProvideIconShape(iconShape) {
content()

View File

@@ -36,7 +36,7 @@ import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.IntRect
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.roundToIntRect
import de.mm20.launcher2.preferences.TimeFormat
import de.mm20.launcher2.search.CalendarEvent
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.DefaultToolbarAction
@@ -49,6 +49,7 @@ import de.mm20.launcher2.ui.launcher.sheets.LocalBottomSheetManager
import de.mm20.launcher2.ui.locals.LocalDarkTheme
import de.mm20.launcher2.ui.locals.LocalFavoritesEnabled
import de.mm20.launcher2.ui.locals.LocalGridSettings
import de.mm20.launcher2.ui.locals.LocalTimeFormat
import palettes.TonalPalette
@Composable
@@ -61,6 +62,7 @@ fun CalendarItem(
val context = LocalContext.current
val viewModel: SearchableItemVM = listItemViewModel(key = "search-${calendar.key}")
val iconSize = LocalGridSettings.current.iconSize.dp.toPixels()
val timeFormat = LocalTimeFormat.current
LaunchedEffect(calendar) {
viewModel.init(calendar, iconSize.toInt())
@@ -85,11 +87,13 @@ fun CalendarItem(
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
painterResource(when (calendar.isCompleted) {
true -> R.drawable.check_circle_24px_filled
false -> R.drawable.radio_button_unchecked_24px
null -> R.drawable.circle_24px_filled
}),
painterResource(
when (calendar.isCompleted) {
true -> R.drawable.check_circle_24px_filled
false -> R.drawable.radio_button_unchecked_24px
null -> R.drawable.circle_24px_filled
}
),
null,
modifier = Modifier
.padding(horizontal = 14.dp, vertical = 20.dp)
@@ -149,7 +153,7 @@ fun CalendarItem(
rememberSharedContentState("date"),
this@AnimatedContent
),
text = calendar.formatTime(context),
text = calendar.formatTime(context, timeFormat),
style = MaterialTheme.typography.bodySmall
)
}
@@ -166,7 +170,10 @@ fun CalendarItem(
contentDescription = null
)
Text(
text = Html.fromHtml(calendar.description!!, Html.FROM_HTML_MODE_COMPACT).toString(),
text = Html.fromHtml(
calendar.description!!,
Html.FROM_HTML_MODE_COMPACT
).toString(),
style = MaterialTheme.typography.bodySmall,
maxLines = 8,
overflow = TextOverflow.Ellipsis,
@@ -249,7 +256,8 @@ fun CalendarItem(
)
val sheetManager = LocalBottomSheetManager.current
toolbarActions.add(DefaultToolbarAction(
toolbarActions.add(
DefaultToolbarAction(
label = stringResource(R.string.menu_customize),
icon = R.drawable.tune_24px,
action = { sheetManager.showCustomizeSearchableModal(calendar) }
@@ -274,11 +282,13 @@ fun CalendarItem(
.padding(16.dp)
) {
Icon(
painterResource(when (calendar.isCompleted) {
true -> R.drawable.check_circle_24px_filled
false -> R.drawable.radio_button_unchecked_24px
null -> R.drawable.circle_24px_filled
}),
painterResource(
when (calendar.isCompleted) {
true -> R.drawable.check_circle_24px_filled
false -> R.drawable.radio_button_unchecked_24px
null -> R.drawable.circle_24px_filled
}
),
null,
modifier = Modifier
.padding(end = 16.dp)
@@ -311,7 +321,7 @@ fun CalendarItem(
rememberSharedContentState("date"),
this@AnimatedContent
),
text = calendar.getSummary(context),
text = calendar.getSummary(context, timeFormat),
style = MaterialTheme.typography.bodySmall,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
@@ -353,7 +363,16 @@ fun CalendarItemGridPopup(
}
}
private fun CalendarEvent.formatTime(context: Context): String {
private fun CalendarEvent.formatTime(
context: Context,
timeFormat: TimeFormat,
): String {
val timeFormatFlag = when (timeFormat) {
TimeFormat.System -> 0
TimeFormat.TwelveHour -> DateUtils.FORMAT_12HOUR
TimeFormat.TwentyFourHour -> DateUtils.FORMAT_24HOUR
}
val startTime = startTime
if (startTime == null || isTask) {
if (allDay) {
@@ -361,14 +380,14 @@ private fun CalendarEvent.formatTime(context: Context): String {
context,
endTime,
endTime,
DateUtils.FORMAT_SHOW_DATE
DateUtils.FORMAT_SHOW_DATE or timeFormatFlag
)
}
return DateUtils.formatDateRange(
context,
endTime,
endTime,
DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_SHOW_TIME
DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_SHOW_TIME or timeFormatFlag
)
}
@@ -376,18 +395,27 @@ private fun CalendarEvent.formatTime(context: Context): String {
context,
startTime,
endTime,
DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_SHOW_WEEKDAY
DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_SHOW_WEEKDAY or timeFormatFlag
)
return DateUtils.formatDateRange(
context,
startTime,
endTime,
DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_SHOW_TIME or DateUtils.FORMAT_SHOW_WEEKDAY
DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_SHOW_TIME or DateUtils.FORMAT_SHOW_WEEKDAY or timeFormatFlag
)
}
private fun CalendarEvent.getSummary(context: Context): String {
private fun CalendarEvent.getSummary(
context: Context,
timeFormat: TimeFormat,
): String {
val timeFormatFlag = when (timeFormat) {
TimeFormat.System -> 0
TimeFormat.TwelveHour -> DateUtils.FORMAT_12HOUR
TimeFormat.TwentyFourHour -> DateUtils.FORMAT_24HOUR
}
val startTime = startTime
if (isTask || startTime == null) {
val isToday = DateUtils.isToday(endTime)
@@ -399,7 +427,7 @@ private fun CalendarEvent.getSummary(context: Context): String {
R.string.task_due_time, DateUtils.formatDateTime(
context,
endTime,
DateUtils.FORMAT_SHOW_TIME
DateUtils.FORMAT_SHOW_TIME or timeFormatFlag
)
)
}
@@ -408,7 +436,7 @@ private fun CalendarEvent.getSummary(context: Context): String {
R.string.task_due_date, DateUtils.formatDateTime(
context,
endTime,
DateUtils.FORMAT_SHOW_DATE
DateUtils.FORMAT_SHOW_DATE or timeFormatFlag
)
)
}
@@ -416,7 +444,7 @@ private fun CalendarEvent.getSummary(context: Context): String {
R.string.task_due_date, DateUtils.formatDateTime(
context,
endTime,
DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_SHOW_TIME
DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_SHOW_TIME or timeFormatFlag
)
)
}
@@ -431,7 +459,7 @@ private fun CalendarEvent.getSummary(context: Context): String {
context,
startTime,
endTime,
DateUtils.FORMAT_SHOW_TIME
DateUtils.FORMAT_SHOW_TIME or timeFormatFlag
)
}
} else {
@@ -440,14 +468,14 @@ private fun CalendarEvent.getSummary(context: Context): String {
context,
startTime,
endTime,
DateUtils.FORMAT_SHOW_DATE
DateUtils.FORMAT_SHOW_DATE or timeFormatFlag
)
} else {
DateUtils.formatDateRange(
context,
startTime,
endTime,
DateUtils.FORMAT_SHOW_TIME or DateUtils.FORMAT_SHOW_DATE
DateUtils.FORMAT_SHOW_TIME or DateUtils.FORMAT_SHOW_DATE or timeFormatFlag
)
}
}

View File

@@ -66,6 +66,7 @@ import de.mm20.launcher2.ui.launcher.widgets.clock.clocks.OrbitClock
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.locals.LocalTimeFormat
import de.mm20.launcher2.ui.settings.clockwidget.ClockWidgetSettingsScreenVM
import de.mm20.launcher2.ui.utils.isTwentyFourHours
import org.koin.compose.koinInject
@@ -251,16 +252,15 @@ fun Clock(
darkColors: Boolean = false
) {
val time = LocalTime.current
val timeFormat = LocalTimeFormat.current
val context = LocalContext.current
val clockSettings: ClockWidgetSettings = koinInject()
val showSeconds by clockSettings.showSeconds.collectAsState(initial = false)
val monospaced by clockSettings.monospaced.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)
val isTwentyFourHours = timeFormat.isTwentyFourHours(context)
when (style) {
is ClockWidgetStyle.Digital1 -> DigitalClock1(

View File

@@ -4,7 +4,7 @@ import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.text.format.DateFormat
import android.text.format.DateUtils
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateFloatAsState
@@ -79,6 +79,8 @@ import de.mm20.launcher2.ui.component.MissingPermissionBanner
import de.mm20.launcher2.ui.component.Tooltip
import de.mm20.launcher2.ui.component.weather.AnimatedWeatherIcon
import de.mm20.launcher2.ui.component.weather.WeatherIcon
import de.mm20.launcher2.ui.locals.LocalMeasurementSystem
import de.mm20.launcher2.ui.locals.LocalTimeFormat
import de.mm20.launcher2.ui.theme.transparency.transparency
import de.mm20.launcher2.ui.utils.formatPercent
import de.mm20.launcher2.ui.utils.formatPrecipitation
@@ -87,10 +89,7 @@ import de.mm20.launcher2.ui.utils.formatTemperature
import de.mm20.launcher2.weather.DailyForecast
import de.mm20.launcher2.weather.Forecast
import de.mm20.launcher2.widgets.WeatherWidget
import kotlinx.coroutines.flow.map
import java.text.DecimalFormat
import java.text.SimpleDateFormat
import java.util.Locale
@Composable
fun WeatherWidget(widget: WeatherWidget) {
@@ -107,16 +106,9 @@ fun WeatherWidget(widget: WeatherWidget) {
val selectedForecast by viewModel.currentForecast
val measurementSystem by viewModel.measurementSystem.collectAsState()
val timeFormat by remember(context) {
viewModel.timeFormat.map {
if (it == TimeFormat.System) {
if (android.text.format.DateFormat.is24HourFormat(context)) TimeFormat.TwentyFourHour else TimeFormat.TwelveHour
} else {
it
}
}
}.collectAsState(TimeFormat.TwentyFourHour)
val measurementSystem = LocalMeasurementSystem.current
val timeFormat = LocalTimeFormat.current
val compactMode = !widget.config.showForecast
val isProviderAvailable by viewModel.isProviderAvailable.collectAsStateWithLifecycle(true)
@@ -475,8 +467,6 @@ fun WeatherTimeSelector(
) {
val context = LocalContext.current
val useFahrenheit = measurementSystem == MeasurementSystem.UnitedStates
val listState = rememberLazyListState()
LazyRow(
state = listState,
@@ -647,12 +637,17 @@ fun WeatherDaySelector(
}
private fun formatTime(context: Context, timestamp: Long, timeFormat: TimeFormat): String {
val skeleton = if (timeFormat == TimeFormat.TwelveHour) "h:mm a" else "H:mm"
val dateFormat = SimpleDateFormat(
DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton),
Locale.getDefault()
val timeFormatFlag = when (timeFormat) {
TimeFormat.TwelveHour -> DateUtils.FORMAT_12HOUR
TimeFormat.TwentyFourHour -> DateUtils.FORMAT_24HOUR
else -> 0
}
return DateUtils.formatDateTime(
context,
timestamp,
DateUtils.FORMAT_SHOW_TIME or timeFormatFlag
)
return dateFormat.format(timestamp)
}
@Composable

View File

@@ -120,27 +120,6 @@ class WeatherWidgetVM : ViewModel(), KoinComponent {
val autoLocation = weatherSettings.autoLocation
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val measurementSystem = weatherSettings.measurementSystem
.map { ms ->
if (ms == MeasurementSystem.System) {
return@map if (isAtLeastApiLevel(28)) {
val systemMs = LocaleData.getMeasurementSystem(ULocale.getDefault())
when (systemMs) {
LocaleData.MeasurementSystem.UK -> MeasurementSystem.UnitedKingdom
LocaleData.MeasurementSystem.US -> MeasurementSystem.UnitedStates
else -> MeasurementSystem.Metric
}
} else {
MeasurementSystem.Metric
}
}
return@map ms
}
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), MeasurementSystem.Metric)
val timeFormat = weatherSettings.timeFormat
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), TimeFormat.TwentyFourHour)
fun selectDay(index: Int) {
selectedDayIndex = min(index, forecasts.lastIndex)
}

View File

@@ -6,6 +6,8 @@ import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.geometry.Size
import androidx.navigation3.runtime.NavBackStack
import androidx.navigation3.runtime.NavKey
import de.mm20.launcher2.preferences.MeasurementSystem
import de.mm20.launcher2.preferences.TimeFormat
import de.mm20.launcher2.preferences.ui.CardStyle
import de.mm20.launcher2.preferences.ui.GridSettings
import de.mm20.launcher2.ui.theme.WallpaperColors
@@ -20,6 +22,9 @@ val LocalFavoritesEnabled = compositionLocalOf { true }
val LocalGridSettings = compositionLocalOf { GridSettings() }
val LocalTimeFormat = compositionLocalOf { TimeFormat.TwentyFourHour }
val LocalMeasurementSystem = compositionLocalOf { MeasurementSystem.Metric }
val LocalSnackbarHostState = compositionLocalOf { SnackbarHostState() }
val LocalDarkTheme = compositionLocalOf { false }

View File

@@ -64,13 +64,6 @@ class ClockWidgetSettingsScreenVM : ViewModel(), KoinComponent {
settings.setMonospaced(monospaced)
}
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)

View File

@@ -145,15 +145,6 @@ class ClockWidgetSettings internal constructor(
}
}
val timeFormat
get() = launcherDataStore.data.map { it.localeTimeFormat }
fun setTimeFormat(timeFormat: TimeFormat) {
launcherDataStore.update {
it.copy(localeTimeFormat = timeFormat)
}
}
val useThemeColor
get() = launcherDataStore.data.map { it.clockWidgetUseThemeColor }