Improve handling of 12/24 hour timestamps on configuration change.

This fixes an edge case seen on ConversationFragment, where if the
device time format is switched between 12/24 hour format while the app
is running, the old time format will still be displayed when the app
is resumed.

This is due to a design flaw in `DateTimeFormatter.ofLocalizedTime`,
where the time format is statically cached and not updated upon
configuration change. The `LocalTime.formatHours()` extension method
was updated to no longer rely on the misbehaving `ofLocalTime` method.

In addition, `ConversationMessaageComputeWorkers.recomputeFormattedDate`
was designed to skip recomputing non-relative timestamps. This works
in most cases but not this specific edge case. A `force: Boolean` flag
was added to force all items to be updated. And the `force = true` flag
was passed upon `onResume` of the fragment.

Closes #14121
This commit is contained in:
Doug Melton
2025-05-01 21:05:54 -07:00
committed by Michelle Tang
parent 918b792d83
commit c865ed0cdc
3 changed files with 25 additions and 16 deletions

View File

@@ -665,6 +665,11 @@ class ConversationFragment :
outState.putBoolean(SAVED_STATE_IS_SEARCH_REQUESTED, isSearchRequested)
}
override fun onStart() {
super.onStart()
recomputeMessageDates(forceUpdate = true)
}
override fun onResume() {
super.onResume()
@@ -1173,12 +1178,7 @@ class ConversationFragment :
getVoiceNoteMediaController().voiceNotePlaybackState.observe(viewLifecycleOwner, inputPanel.playbackStateObserver)
val conversationUpdateTick = ConversationUpdateTick {
disposables += ConversationMessageComputeWorkers.recomputeFormattedDate(
requireContext(),
adapter.currentList.filterIsInstance<ConversationMessageElement>()
).observeOn(AndroidSchedulers.mainThread()).subscribeBy { adapter.updateTimestamps() }
}
val conversationUpdateTick = ConversationUpdateTick { recomputeMessageDates() }
viewLifecycleOwner.lifecycle.addObserver(conversationUpdateTick)
@@ -1188,6 +1188,17 @@ class ConversationFragment :
}
}
private fun recomputeMessageDates(forceUpdate: Boolean = false) {
disposables += ConversationMessageComputeWorkers
.recomputeFormattedDate(
context = requireContext(),
items = adapter.currentList.filterIsInstance<ConversationMessageElement>(),
forceUpdate = forceUpdate
)
.observeOn(AndroidSchedulers.mainThread())
.subscribeBy { adapter.updateTimestamps() }
}
private fun initializeInlineSearch() {
inlineQueryController.onOrientationChange(resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE)

View File

@@ -21,13 +21,14 @@ object ConversationMessageComputeWorkers {
fun recomputeFormattedDate(
context: Context,
items: List<ConversationMessageElement>
items: List<ConversationMessageElement>,
forceUpdate: Boolean = false
): Single<Boolean> {
return Single.fromCallable {
var hasUpdatedProperties = false
for (item in items) {
val oldDate = item.conversationMessage.computedProperties.formattedDate
if (oldDate.isRelative) {
if (oldDate.isRelative || forceUpdate) {
val newDate = ConversationMessage.getFormattedDate(context, item.conversationMessage.messageRecord)
item.conversationMessage.computedProperties.formattedDate = newDate
hasUpdatedProperties = true