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
`AttachmentSaver` was missing logic to show a toast message after the user denies `WRITE_EXTERNAL_STORAGE` permission.
#### Changeset
- Add missing toast after write external storage permission is denied.
- Add unit test coverage for `AttachmentSaver` result messages.
- Rename `AttachmentSaver` string resource names so they all have the same prefix.
Introduces `AttachmentSaver` to centralize all of the steps needed to save message attachments to the device storage. It handles the entire workflow including:
- Showing the save to storage warning/confirmation dialog.
- Requesting `WRITE_EXTERNAL_STORAGE` permission.
- Showing/dismissing media save progress.
Goals of this new class:
- Make it easy to save media attachments anywhere with just a few lines of code (and easier to replace the deprecated `SaveAttachmentTask`).
- Ensure all of the necessary steps are consistently performed at each usage site (which wasn't the case before).
- Make it easier to unit test the save attachment logic.
Improves the error handling in `SaveAttachmentUtil.saveAttachments()` to continue processing all requested attachment saves even after individual save operations fail.