Before Android 13, the first access to a Bundle received over IPC
unparcels all its entries, including the avatar bitmap. Guard the
userId read so that a failure to unparcel the avatar does not escape
the handler.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Pass media captions as HTML to the media viewer
`MediaInfo` and `GalleryInfo` carried the formatted caption as a `CharSequence`, which isn't a good fit for `Parcelable` classes. Some producers also passed the raw HTML body in that field, so the caption was rendered with its tags, and gallery messages didn't pass a formatted caption at all.
Replace `formattedCaption` with `htmlCaption: String?` and convert it to styled text in `MediaViewerPresenter`, using the same `HtmlConverterProvider` as the timeline. The converted captions are cached by HTML so page data equality stays stable across list updates.
* Pillify media captions in the media viewer
Move the `TextPillificationHelper` interface to `:features:messages:api` so the media viewer can use it, keeping `DefaultTextPillificationHelper` in the impl module. The fake moves to `:features:messages:test`.
`MediaViewerPresenter` now pillifies converted HTML captions before linkifying them, matching how captions are rendered in the timeline.
* Rename `TextPillificationHelper` to `TextPillificator`, also fix copyright notice.
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Element Classic can send the user avatar at its original resolution
(e.g. 6765x6765, ~183MB as ARGB_8888), which makes the login screen
crash with "Canvas: trying to draw too large bitmap".
Downscale the avatar to at most 512px on its longest side when it is
received, and fall back to no avatar if it cannot be read.
Fixes#7986
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Replace the pairs of true / false toggle tests and their private helpers by
single tests taking a @TestParameter initialState.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Add previews for the uninitialized state, the activity / invite / @room switches,
other room notification modes, the message sound dialog with a custom sound and a
single failing push distributor. Group the preview states by screen section.
Each factory now takes exactly the parameters of the object it builds
(aNotificationSettingsState, aValidMatrixSettings, anInvalidMatrixSettings,
anAppSettings, aSoundChannelUiState), and the default message sound display name
matches the default sound.
Fix the push provider test, which needs 2 distributors for the advanced settings
to be displayed.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
HomeView sends HomeEvent.DismissSwipeAvatarAnnouncement right before switching account. Switching
account replaces the LoggedInFlow root node, which destroys HomePresenter and cancels its
composition coroutine scope, so the dismissal could be cancelled before the announcement status
was stored. Use the app coroutine scope instead, which survives the account switch.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The test source set also has a RoomListState.kt file in the same package, so its RoomListStateKt
class was shadowing the one of the main source set, and the toRoomListBanner() functions were
not found when running the tests (NoSuchMethodError).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Account switching is now handled by RootFlowNode, through the onSwitchToAccount callback, instead
of HomeEvent.SwitchToAccount. To keep dismissing the swipe avatar announcement when the user
switches account by swiping their avatar, HomeView now also sends the new
HomeEvent.DismissSwipeAvatarAnnouncement.
Also opt in to ExperimentalCoroutinesApi in the HomePresenter tests using advanceTimeBy.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Replace DismissibleBanner, which used one AnimatedVisibility per banner, by RoomListBannerSlot,
which renders the banner to display with a single AnimatedContent. All the changes are now
animated: dismissing a banner, replacing a banner by the next one, and displaying a new banner.
Previously, when a banner was dismissed, the next banner was displayed without animation.
The dismiss event is now sent immediately, AnimatedContent keeps rendering the dismissed banner
while it animates out.
The banner slot of the LazyColumn now has a min height of 1px. When it was 0px high, the
LazyColumn was considering it as not visible and moved the first visible item to the first room,
so a new banner was added above the visible area of the list.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The distributors are now loaded on the IO dispatcher, which was a StandardTestDispatcher in the test, so the initial state could be read before they were loaded. Use an unconfined test dispatcher so that they are loaded deterministically.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Extract the logic to observe the total unread count of a MatrixClient, refreshed on each sync
heartbeat, to a shared extension and use it in HomePresenter and PreferencesRootPresenter.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
In the settings screen, when the other accounts section is expanded, display a counter with the
total number of unread notifications for each non-active account. The count is read from the
account's MatrixClient (if in memory) and refreshed on its sync heartbeat.
Part of #7769
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>