* Bump SDK bindings to `v26.09.26`
This version contains a fix for issues in `v26.09.25` related to certificate verification.
* Fix breaking API changes in the latest bindings
* Unify `FeatureFlags.RoomThreadList` with `FeatureFlags.Threads`
* Fix forwarding an event not working in threads
Add the `TimelineProvider` for the event to the navigation flow so the events can be fetched from the right timeline.
* Integrate the new renderer with the timeline UI
* Composable functions are now in `:libraries:htmlrenderer:api` so we don't need to add `impl` to other modules.
* Improve inline code span
* Return both the text and URL in `onLinkClick` and `onLinkLongClick` so we can check the URL is not misleading
* Remove `SelectionContainer` for now - it creates issues when long pressing
* Make `LocalComposeTimelineEventRenderer` contain a `ComposeLocalTimelineEventRendererData` instance. This checks if the new rendering is enabled and what is the current user id so it can be used for styling mention pills
* Add fading edges to code block to indicate scrolling is possible
* Long pressing in a code block copies its contents to the clipboard
* Fix alignment of ordered list item prefixes
* Don't use default values in State data classes
---------
Co-authored-by: ElementBot <android@element.io>
* Add linear animation to voice message playback progress
Instead of animating the small `progress` value changes, calculate an animation for the range `maxProgress - progress`, with a linear interpolator so it's completely smooth. Previously, the progress was updated in 'steps' that were quite noticeable.
* Added HTML parser to abstract tree nodes
* Add HTML renderer using `HtmlMessageContent`
* Add custom rendering for inline code nodes besides the monospace font
* Support `<s>` and `<strike>` too
* Add `ContentAvoidingLayoutData` support
* Improve the last block node behaviour and its integration with `ContentAvoidingLayout`
* Move `ContentAvoidingLayout` to `:libraries:ui-common` so it can be shared
* Add missing `:libraries:htmlrenderer:impl` entry in `DependencyHandlerScope.allLibrariesImpl`
* Update screenshots
---------
Co-authored-by: ElementBot <android@element.io>
detekt's default configuration excludes test source sets from
`TooManyFunctions` and `StringLiteralDuplication`, but not from
`LargeClass`, so large test classes were reported and had to carry a
`@Suppress("LargeClass")` annotation.
Add the same kind of exclusion to the `LargeClass` rule in
`tools/detekt/detekt.yml` and remove the 24 now-redundant suppressions
from test files. The two remaining suppressions are in production code,
where the rule still applies.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The problem seemed to be sometimes the background work to switch from live timeline to a focused event timeline wasn't finished in time and an assertion failed from time to time.
By providing the coroutine to run this on we can pass a `TestScope` and force it to reliably run immediately instead in unit tests.
* Expose the timeline item gestures as TalkBack actions
Long pressing a timeline item opens the action list, and swiping it
starts a reply. Neither gesture is reachable with a screen reader
enabled, so those two actions were unavailable to TalkBack users.
Publish them as accessibility custom actions on the event row, reusing
the existing "Message actions" and "Reply" strings. The reply action is
only offered when the user is allowed to reply, matching the swipe.
* Update screenshots
---------
Co-authored-by: ElementBot <android@element.io>
Deciding whether a text message is emoji only compares the formatted body to
the plain one and runs a grapheme scan over the whole message. Nothing was
remembered, so both ran again on every recomposition of every visible message.
Remember the decision against the body and the formatted body. The preview
branch moves into the same computation, so the composable wrapper around
containsOnlyEmojis, which had a single caller and existed only to read
LocalInspectionMode, is no longer needed.
The dialog now reads "Confirm removal" over "Are you sure you wish to remove
this message?", with the reason field marked as optional beneath it, rather
than a bare title and a labelled field. TextFieldDialog gained a supporting
text so the field can carry that note.
The pending removal moves from a nullable timeline item to redactEventAction,
an AsyncAction whose Confirming state carries the event id, so it is modelled
the same way as every other action the user has to confirm.
Editing the caption of a media message dropped all of its formatting: bold,
links and the rest came back as plain text, and saving wrote that plain text
over the formatted caption.
The composer was being opened on the plain caption. Editing a message body
already starts from its HTML when rich text editing is on, and the caption
path was the only one that did not - it read `captionOrNull()` and never had
the caption's HTML to read, because the timeline content only kept the
already-parsed `formattedCaption`.
Media contents now carry the caption's HTML alongside the parsed one, the
same way text contents carry `htmlBody`, and editing a caption starts from it
when rich text editing is on. The save path already sent the composer's HTML,
so nothing changes there.
Part of #6730
Deleting a message that headed a thread took the thread with it: the "N
replies" summary under the message disappeared and there was no way back to
the replies, which are still there.
Every branch of the timeline content mapper passes `MsgLikeContent`'s thread
information on except the redacted one, which returned a bare
`RedactedContent` and dropped it. The thread summary lives on
`MsgLikeContent` next to `kind`, so it survives the redaction; only the app
was throwing it away. `RedactedContent` now carries the thread info like the
other contents do, and `EventTimelineItem.threadInfo()` reads it.
A deleted message that still heads a thread is also left out of the "N
removed messages" group, which would otherwise hide the summary again as soon
as three deletions ended up next to each other.
Part of #7397
The timeline renders a message with the sender's raw Matrix ID whenever the
SDK has no profile for them, even though the room member list the same screen
already holds usually knows their display name and avatar.
The timeline item factory now falls back to the room member for a sender whose
profile is not resolved, both when the item is first built and when the member
list arrives afterwards, so the name and the avatar settle on the display name
instead of staying on the ID.
The protocol carries a reason on a redaction and other clients let the sender
give one, but removing a message here sent the redaction straight away with no
reason and no confirmation.
Removing a message now opens a dialog with an optional reason, and the
redaction is only sent once it is confirmed. A blank reason is sent as none, so
confirming without typing anything behaves exactly as before. A message which
was never sent has nobody to give a reason to, so the send failure dialog's
Remove still redacts it immediately.
The member suggestions were emitted in whatever order the room member list
happened to be in, so a member who only matches somewhere in the middle of
their name could be offered above one whose name starts with the query.
Matches are now ordered by how well they match: display name prefix first,
then user ID localpart prefix, then any other match. The sort is stable, so
members of equal rank keep the order the member list gave them.
The @room suggestion was also matched case sensitively, so typing @Ro or
@ROOM dropped it while @ro kept it. It now matches the query the same way
the members do.
The HTML sanitiser in toHtmlDocument() drops every tag outside its safelist,
which does not include img. A formatted body whose only content is an image —
a custom emoji, or a bot posting an image inline — therefore cleans down to an
empty document, and because that document is not null the ?: body fallbacks in
the timeline item factory, the room list preview, the notification resolver and
plainText are all dead. The message renders as an empty bubble.
Return null from toHtmlDocument() when no text survives the sanitisation, so the
existing fallbacks take the plain text body, which for these messages carries the
alt text the sender provided.
Every @BindingContainer now lives in a di subpackage, in a file named after
it. Containers that were declared inside a shared file (SimplePlayer.kt,
LockScreenConfig.kt, VoiceMessagePresenter.kt, TimelineItemPresenterFactories.kt,
MediaItemPresenterFactories.kt) are extracted into their own file.
Method names are made consistent too:
- @Binds functions are named bind<ConcreteParameterType>, fixing three that
named something else (notably RoomAliasResolverBindingContainer, whose
provider was still called providesJoinRoomPresenterFactory).
- @Provides functions are named after what they provide, and all use the
provides prefix rather than a mix of provide and provides.
ANDROID_KEYSTORE moves back to the cryptography impl package, next to
KeyStoreSecretKeyRepository which uses it; it is not DI specific and would
otherwise be unreachable from there.
No behaviour change: Metro discovers containers via @ContributesTo and does
not care about method names, so the only call site to follow was in
DefaultVoiceRecorderTest.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Metro's @BindingContainer replaced Dagger's @Module, but the declarations
kept the old Module suffix. Rename them (and their dedicated files) so the
type name matches the annotation, matching UserStatusBindingContainer which
already followed this convention.
No behaviour change: Metro discovers containers via @ContributesTo, so there
were only two call sites to update, both in tests.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The 174 commits develop gained since this branch was cut touch preview data
in three ways, and each is resolved so the flipped konsist rule still holds
across the merged tree.
Two screens the branch had renamed were deleted upstream by "Remove the
account provider search screen and homeserver resolver": the
changeaccountprovider and searchaccountprovider packages. Their deletion
wins, so ChangeAccountProviderStatePreviewParam, SearchAccountProviderStatePreviewParam
and the two views that used them go with them rather than coming back
under the new name.
Two preview classes develop added still carried the old suffix and are
renamed here, along with the `@PreviewParameter` reference in each view:
GlobalSearchStateProvider -> GlobalSearchStatePreviewParam and
SendFailureDialogStateProvider -> SendFailureDialogStatePreviewParam.
Everything else merged textually. No production class extending
PreviewParameterProvider is left with a `Provider` name, and no reference
to any of the 194 renamed names survives outside the CHANGES.md entry that
records a past pull request title.