* 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>
Remove dependencies flagged as unused by the Dependency Analysis Gradle Plugin
(`./gradlew buildHealth`) and explicitly declared in module build files.
Removals were skipped when the dependency provides transitively something the
module (or a consumer of an `api` dependency) uses directly, as well as for
runtime-only dependencies (element-call-embedded, sentry-android,
rustls-platform-verifier). Dependencies added by the convention plugin helpers
(composeDependencies, testCommonDependencies) are left untouched.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* 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.
The `Provider` suffix reads as a dependency provider rather than as preview
data, and it collides with the real providers in the codebase — `StringProvider`,
`PickerProvider`, the login feature's `AccountProvider` — so the name says
nothing about which of the two a class is. `PreviewParam` says it.
195 classes are renamed, along with the 185 files named after them, the konsist
rule that enforces the convention, the presentation-class file templates and the
onboarding documentation.
This cannot be split. `Konsist.scopeFromProduction()` scans the whole project, so
flipping the assertion before every class has moved leaves the rule red, and a
rule that accepted either suffix during a transition would be exactly the kind of
compatibility layer this convention change is meant to remove.
Two judgement calls in the rule itself. The `name.endsWith("PreviewProvider")`
exclusion is dropped: its only job was to reject a redundant `Preview` before
`Provider`, and the new suffix already contains it; no class currently ends with
`PreviewProvider`, so removing it changes nothing today. And
`EditableAvatarViewUriProvider` is renamed rather than deleted from the exclusion
list even though no such class exists in this repository, since it presumably
comes from the modules overlaid for the enterprise build.
Nothing keyed off the old suffix: no snapshot filename contains a provider class
name, so no Paparazzi image changes, and the Kover variants match on `*Presenter`,
`*State` and `*ViewKt`, so coverage bounds are unaffected.
Document HtmlConverterProvider, MessageComposerContext,
PinnedEventsTimelineProvider, the MessagesEntryPoint node proxy and the voice
message composer factory.
HtmlConverterProvider carries the ordering constraint that matters: the converter
is rebuilt from the Compose theme, so Update has to be composed before provide is
called.
MessagesEntryPoint and its Callback are navigation plumbing and out of scope.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Add support for slash commands
* Update screenshots
* Rename module `slash` to `slashcommands`
* Rename `SlashCommand` to `SlashCommandService`
* Introduce MsgType in order to send text message with a different msgtype value.
* Format file and add parameter names, add default values and cleanup
* Add isSupported parameter to filter out unsupported yet commands.
* Slash commands: disable suggestions if the feature is disabled.
* Fix sending shrug command.
* Add missing test on SuggestionsProcessor
* Add tests on MessageComposerPresenter about slash command.
* Fix import ordering
* Add missing tests on CommandExecutor
* Add missing tests in MarkdownTextEditorStateTest
* Slash commands: Improve code when sending message with prefix.
* Slash commands: Add support for /unflip
---------
Co-authored-by: ElementBot <android@element.io>
* Separate thread notifications into their own notifications when the feature flag is enabled.
Otherwise, set the `threadId` to null so it'll behave as usual. It's done this way to avoid having to inject `FeatureFlagService` in several places.
* Add permalink navigation to threads from notifications, focusing on the latest event in the list of messages of the notification tapped
* Fix redactions in threads
* Clear notifications for a thread when visiting it
* Fix opening a thread happening twice, first because of the `openThreadId` value, then because of the `focusedEventId` one
* Make opening a room through a notification also focus on the latest event
* Add helper `NotificationCreator.messageTag` function
* Remove unused `ROOM_CALL_NOTIFICATION_ID`: `FOREGROUND_SERVICE_NOTIFICATION_ID`+ `ForegroundServiceType` is used instead
* Simplify `DefaultDeepLinkCreator`
* Make sure the main timeline focuses on the thread root id too when navigating to a thread
* Handle "Mark as read" action for thread notification, using `timeline.markAsRead`
* Log failures to mark rooms as read using the notification action
---------
Co-authored-by: Benoit Marty <benoit@matrix.org>
* Initial threads support: parse `ThreadSummary`.
Replace several `isThreaded` values with `EventThreadInfo`, which contains the info about the event either being the root of a thread or part of it.
* Add `Threaded` timeline mode
* Add a `liveTimeline` parameter to `TimelineController`'s constructor. This way we can customise which timeline will be used as the 'live' one. Also add `@LiveTimeline` DI qualifier for the actual live timeline of the room.
* Create `ThreadedMessagesNode`. Allow opening a thread in a separate screen.
* Add the callbacks for the list menu actions - even if they're the wrong ones and will send the data to the room instead
* Send attachments and location in threads
* Fix polls in threads, add support for sending voice messages in threads
* Display thread summaries only when the feature flag is enabled
* Use 'Reply' instead of 'Reply in thread' when in threaded timeline mode
* Remove incorrect usage of `Timeline` in `MessageComposerPresenter`. This led to replies to threaded events not appearing as actual replies.
---------
Co-authored-by: ElementBot <android@element.io>
* Add `VideoCompressorPreset` enum
This represents the different compression presets used for processing videos before uploading them
* Add `VideoCompressorHelper` util class to calculate the scaled output size of the video given an input size and its optimal bitrate
Also add `MediaOptimizationConfig` which will be used to decide how to apply compression in `MediaPreProcessor`
* Add `RustMatrixClient.getMaxFileUploadSize()` function and `MaxUploadSizeProvider` so we can import only this functionality into other components
* Try preloading the max file upload size the first time we get network connectivity - it's a best effort
This should help ensure we'll have this value available later, even if we still need to load it asynchronously.
* Split the `compressMedia` preference into `compressImages` and `compressMediaPreset`
* Modify the media processing parts to use the new classes and utils
* Add `MediaOptimizationSelectorPresenter`, which will retrieve the compression values and the max file upload size, also estimating the compressed video file sizes if needed.
* Add a feature flag to allow selecting the media upload quality per upload
* Integrate the previous changes with the attachments preview screen
Add strings from localazy too.
* Adapt the rest of the app calls to upload media to using the media optimization configs
* Allow modifying the default compression values in advanced settings, based on the feature flag value
* Pass the `fileSize` in `MediaUploadInfo` too, to be able to check it against the `maxUploadSize`
* Update screenshots
---------
Co-authored-by: ElementBot <android@element.io>
2 replace all actions have been performed:
- "SPDX-License-Identifier: AGPL-3.0-only" to "SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial"
- "Please see LICENSE in the repository root for full details." to "Please see LICENSE files in the repository root for full details."
* Add `formattedBody` to `TimelineItemTextBasedContent`.
This is pre-computed when timeline events are being mapped from the Rust SDK.
* Update `HtmlConverterProvider` styles.
* Improve `MentionSpan` to add missing `@` or `#` if needed
* Replace `HtmlDocument` with the `TextView` based component
* Improve extra padding calculation for timestamp by rounding the float offset result instead of truncating it.
* Remove composer line height workaround
* Use `ElementRichTextEditorStyle` instead of `RichTextEditorDefaults` for the theming
* Use slightly different styles for composer and messages (top/bottom line height discrepancies, mostly).
* Add `formattedBody` to notice and emote events.
---------
Co-authored-by: ElementBot <benoitm+elementbot@element.io>
* Update AGP to 8.0.0.
* Set JAVA_HOME to JDK17
* Update lint version.
* Use right JDK for dependency analysis, replace deprecated env var.
* Upgrade to Gradle 8.1.
* Remove `@Supress(DSL_SCOPE_VIOLATION)` as it's no longer needed.
---------
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>