fix: respect system 24-hour time setting - #1111
Conversation
Greptile SummaryThe PR centralizes user-facing activity timestamp formatting and makes activity rows and details respect the device’s 12/24-hour clock preference.
Confidence Score: 3/5The activity timestamps need reactive handling of system clock-format changes before this is safe to merge. The formatter reads the current 12/24-hour preference only during composition, while the system setting is not represented by observable state, leaving open activity screens in the previous format after the preference changes. Files Needing Attention: app/src/main/java/to/bitkit/ext/Context.kt and the activity Compose call sites
|
| Filename | Overview |
|---|---|
| app/src/main/java/to/bitkit/ext/Context.kt | Adds a wrapper around Android’s system 24-hour-format setting, but does not expose changes as observable Compose state. |
| app/src/main/java/to/bitkit/ext/DateTime.kt | Adds centralized activity timestamp styles and locale/timezone-aware instant formatting without breaking existing callers. |
| app/src/main/java/to/bitkit/ui/screens/wallets/activity/ActivityDetailScreen.kt | Migrates the separate date and time fields to system-aware formatting, which refreshes only when the screen recomposes. |
| app/src/main/java/to/bitkit/ui/screens/wallets/activity/components/ActivityRow.kt | Preserves age-based timestamp styles while applying the device clock preference during composition. |
| app/src/test/java/to/bitkit/ext/DateTimeExtTest.kt | Adds deterministic tests covering all new styles, 12/24-hour boundaries, locale handling, and timezone conversion. |
Reviews (1): Last reviewed commit: "fix: respect system 24-hour time setting" | Re-trigger Greptile
| val receiver = object : BroadcastReceiver() { | ||
| override fun onReceive(receiverContext: Context?, intent: Intent?) = run { | ||
| is24Hour = context.is24HourTimeFormat | ||
| } | ||
| } | ||
| ContextCompat.registerReceiver( | ||
| context, | ||
| receiver, | ||
| IntentFilter(Intent.ACTION_TIME_CHANGED), | ||
| ContextCompat.RECEIVER_NOT_EXPORTED, | ||
| ) |
There was a problem hiding this comment.
every instance will registerReceiver/unregisterReceiver one receiver, causing a real junk risk for a big list
There was a problem hiding this comment.
take a look at ui/Locals.kt pattern. Could create a LocalIs24HourFormat and provide a single rememberIs24HourFormat in ContentView/MainActivity with uiDateText reading LocalIs24HourFormat.current
There was a problem hiding this comment.
Fixed in 3e90024f4. LocalIs24HourFormat in ui/Locals.kt, provided once in ContentView — one receiver for the app instead of one per row. Left it nullable so previews and anything outside ContentView fall back to the device value rather than a hardcoded default.
| val style = when { | ||
| dateTime.toLocalDate() == now -> UiDateStyle.TIME | ||
| dateTime.year == now.year -> UiDateStyle.DATE_TIME | ||
| else -> UiDateStyle.DATE_TIME_YEAR |
There was a problem hiding this comment.
Extracting a pure fun uiDateStyleFor(timestamp: ULong, today: LocalDate, zone: ZoneId): UiDateStyle into ext/DateTime.kt would make it more testable
There was a problem hiding this comment.
Fixed in 0a102b0bc. Pure uiDateStyleFor(timestamp, today, zone) in ext/DateTime.kt, with today/zone defaulted so the call site stays a one-liner. Four tests cover today / this year / previous year and the zone boundary.
| ): String = Instant.ofEpochSecond(timestamp.toLong()) | ||
| .formatted(style.pattern(rememberIs24HourFormat()), locale, zone) |
There was a problem hiding this comment.
add a remember for avoid rebuilding the pattern string and a DateTimeFormatter on every recomposition of every row
There was a problem hiding this comment.
Fixed in 3e90024f4. The DateTimeFormatter is remembered on (style, is24Hour, locale, zone) and the formatted string on (formatter, timestamp).
Fixes #677
This PR makes activity timestamps follow the device's 12/24-hour time setting.
Description
DatePattern.ACTIVITY_TIMEwash:mm, a 12-hour pattern with no meridiem, so a payment received at 15:23 rendered as3:23. That is wrong for anyone on a 24-hour clock and ambiguous even for 12-hour users. The two neighbouring row patterns (ACTIVITY_ROW_DATE,ACTIVITY_ROW_DATE_YEAR) were hardcoded the other way, to 24-hour, so the same activity row changed clock format depending on how old the item was. Nothing in the app consulted the system setting.Rather than pass a 12/24-hour flag around the screens, this adds one entry point for user-facing timestamps:
uiDateText(timestamp, style)inui/utils/DateText.kt, backed by aUiDateStyleenum (TIME,DATE,DATE_TIME,DATE_TIME_YEAR) inext/DateTime.ktthat owns the patterns. It resolves the clock format throughrememberIs24HourFormat(), which registers forACTION_TIME_CHANGEDso timestamps update without waiting for an unrelated recomposition.ActivityRowandActivityDetailScreenpick a style and call it. Neither builds a pattern or sees the flag.ext/Numbers.kt(toActivityItemDate,toActivityItemTime) is removed as superseded, along with theACTIVITY_*pattern constants and the unusedINVOICE_EXPIRY, leavingDatePatternas plain constants.Instant.formatted()takes optionallocaleandzoneso patterns can be asserted without depending on the CI machine's locale or timezone. Existing call sites are unchanged.ChannelDetailScreenstill hardcodes 24-hour viaDatePattern.CHANNEL_DETAILS(#1110), andtoLocalizedTimestampdefaults toLocale.USso App Status and Backup timestamps ignore the device locale (#1112). Both are separate screens and are left out here; withuiDateTextin place each becomes a two-line adoption.Preview
QA Notes
Manual Tests
14:38), matching the status bar.2:38 PM.regression:Switch the device to a non-English locale and confirm activity dates still render with a localized month name.Automated Checks
DateTimeExtTest.kt: eachUiDateStylein both clock formats, midnight and noon boundaries, month-name localization, and timezone handling. All use a fixed instant with explicit locale and zone so they are deterministic.just compile,just test,just lintall pass, no new detekt findings.