Commit Graph

6834 Commits

Author SHA1 Message Date
Benoit Marty 56623b8a7f Merge branch 'develop' into feature/bma/swipeAvatarAnnouncement
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>
2026-10-14 17:39:24 +02:00
Benoit Marty 2a048a5ee5 Merge pull request #7918 from element-hq/feature/bma/iterateOnAddAccountUi
Add a back button to the server screen and iterate on the identity confirmation screen
2026-10-14 17:16:35 +02:00
Benoit Marty 5a5d631a8d Animate all the banner changes of the room list in a single slot
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>
2026-10-14 17:04:32 +02:00
Benoit Marty c19dfd1db6 Merge pull request #7901 from element-hq/feature/bma/mainThreadBlocking
Avoid blocking the main thread in several places
2026-10-14 16:39:14 +02:00
Benoit Marty faa7956b93 Fix flaky NotificationSettingsPresenterTest after loading distributors off the main thread
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>
2026-10-14 14:46:15 +02:00
Benoit Marty 0e6bc91e9d Merge pull request #7917 from element-hq/feature/bma/switchSessionInRootFlowNode
Handle account switch in RootFlowNode instead of in presenters
2026-10-14 14:19:58 +02:00
Benoit Marty 933fc54e12 Iterate on add account onboarding UI. 2026-10-14 12:03:14 +02:00
Benoit Marty 6a516a02ae Iterate on the identity confirmation screen
Follow the multi-account design for the "Confirm your digital identity"
screen: use the dedicated "Sign out" string for the top bar action and add
a leading icon to the "Use another device" button.

Add a test to check that clicking on "Sign out" emits the expected event.

Part of #7768

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 11:56:28 +02:00
Benoit Marty f72af72d80 Add a back button to the account provider confirmation screen
The screen where the user enters the server address only supported the
system back gesture. Add a top app bar with a back button, consistent with
the screen to choose the account provider and FlowStepPage, so the user can
navigate back from it, especially when adding another account.

Part of #7768

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 11:35:13 +02:00
Benoit Marty 6c1cc4e3f7 Handle account switch in RootFlowNode instead of in presenters
PreferencesRootPresenter and HomePresenter were updating the latest session
in the session store directly, relying on RootNavStateFlow to trigger the
navigation as a side effect. Changing the current session is app-level
navigation, so it is now forwarded through a new `navigateToSession` callback
up to RootFlowNode, which performs the session store update.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 10:37:15 +02:00
Benoit Marty 799f60b955 Generate null MapTiler BuildConfig values when not provided
Remove the default values from the Gradle file, the public MapTiler defaults
are now only defined in MapTilerDefaults. buildConfigFieldStr now accepts a
null value.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 10:11:08 +02:00
Benoit Marty 0149e6f65b Do not mix the homeserver MapTiler config with the build time one
The map URL builders now only use the MapTilerConfig they are given, falling
back to the public MapTiler defaults for the missing values, instead of the
values defined at build time. This avoids for instance sending the build time
API key to a tile server provided by the homeserver.

When there is no config, no static map URL builder is created, so no request
is performed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 10:11:07 +02:00
Benoit Marty 4b97d6ab96 Disable location sharing when no MapTiler config is available
LocationService now exposes the MapTiler config to use: the one provided by the
homeserver takes precedence over the build time one, and null means that no map
provider is configured. It is now session scoped, and isServiceAvailable()
returns a StateFlow derived from this config.

When no config is available:
- the location sharing option is hidden from the composer, and location events
  cannot be opened in the location viewer;
- the "Location sharing" entry is hidden from the settings;
- location events in the timeline render a placeholder, without performing any
  request to MapTiler.

The show and share location screens now get the config from LocationService
rather than fetching the homeserver config by themselves.

Closes element-hq/element-android-enterprise#69

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 17:15:37 +02:00
Jorge Martin Espinosa 9cc5cf9a01 Add total unread notification count to home screen (#7891)
* Add total unread notification count to home screen

This will be calculated on each sync heartbeat (for now, using the state updates for the room list service).

* Simplify `counter` parameter logic

* Read the total unread count when the home screen is first displayed

---------

Co-authored-by: Benoit Marty <benoitm@element.io>
2026-10-13 10:58:07 +00:00
Benoit Marty f187409db3 Apply suggestion: 'rangeTo' or the '..' call should be replaced with '..<' 2026-10-13 12:55:50 +02:00
Benoit Marty 8923ba1e1b Animate the dismissal of the banners in the room list
Add DismissibleBanner, which makes a banner shrink and fade out when it is dismissed, and
invokes the dismiss callback when the animation is over, so that the banner is removed from
the list when its height is 0. Apply it to all the dismissible banners of the room list.

The banners are now rendered in a slot which is always the first item of the room list, so
that the rooms, which do not have a key, keep the same index and the scroll position is
preserved when a banner is removed. The visible range of rooms sent to the SDK does not
include this slot anymore.

FullScreenIntentPermissionBanner and BatteryOptimizationBanner now take callbacks instead
of their state, like the other banners.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 11:45:01 +02:00
Benoit Marty 14710599a7 Announce the swipe gesture on the account avatar
Display a banner on the room list when 2 or more accounts are logged in, to let the user know
that they can swipe their avatar to switch account. The banner is stored as a new
Announcement.SwipeAvatarToSwitchAccount, and is dismissed when the user taps on it, or when
the user switches account by swiping their avatar.

The banner is displayed at the same place as the new notification sound banner, but never
at the same time.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 11:44:44 +02:00
Benoit Marty 50aa723f63 Merge pull request #7899 from element-hq/feature/bma/multiAccountDeviceVerification
Show account information on the identity confirmation screen
2026-10-13 11:31:13 +02:00
Benoit Marty ffe8f4a14e Merge pull request #7900 from element-hq/feature/bma/settingsMainThreadJank
Fix animation glitches when opening the settings
2026-10-13 11:30:36 +02:00
Benoit Marty 259fc222da Import strings for the swipe account banner 2026-10-13 09:45:23 +02:00
Benoit Marty 6049f25a3e Create the LocalMedia off the main thread when picking or sharing media
LocalMediaFactory.createFromUri queries the ContentResolver and can decode the media, it was called on the main thread from the picker callbacks and when sharing a file.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 14:33:18 +02:00
Benoit Marty 865cca6764 Load the push distributors off the main thread in the notification settings
Getting the distributors queries the PackageManager and the Play Services, it was done during the first composition of the screen.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 14:33:17 +02:00
Benoit Marty 23a79ad987 Merge pull request #7896 from element-hq/sync-localazy
Sync Strings
2026-10-12 13:56:43 +02:00
Benoit Marty 8912341439 Avoid database access on the main thread when opening the settings
`getUserProfile()` was storing the user profile (DB read, avatar Base64 encoding, DB write) on the caller
dispatcher, i.e. the main thread when called from a presenter, causing glitches in the screen transition.
- Run the whole `getUserProfile()` on the session dispatcher.
- Make `DatabaseSessionStore` main-safe by running the queries on the IO dispatcher.
- Do not refresh the profile twice from the settings screen.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 10:42:19 +02:00
Benoit Marty 87e1deaccf Show account info on the identity confirmation screen
Render an AccountInfoCard with the current user on the "Confirm your digital identity" screen,
so the user knows which account needs to be verified.

Closes #7898

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 09:56:57 +02:00
bmarty 8100fa6555 Sync Strings from Localazy 2026-10-12 00:37:35 +00:00
Benoit Marty d29e4e5f0c Merge remote-tracking branch 'origin/develop' into feature/bma/multiAccountShareAtRoot
# Conflicts:
#	libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenterTest.kt
2026-10-09 17:11:58 +02:00
Benoit Marty 00dab131dd Ask for session verification before sharing with an unverified account
The incoming share is now displayed on top of the root, so the FTUE, and the session verification in particular, could be bypassed.

- When the current account is not verified, wait for the FTUE to be complete before displaying the incoming share, as before.
- When the user selects another account which is not verified in the room select screen, display the session verification flow of
  the FTUE for this account on top of the share. As in the FTUE, the user has to verify the session or to sign out.
- Expose the FTUE session verification flow with a new SessionVerificationEntryPoint, displayed by OtherSessionVerificationNode
  which sets up the session graph of the account to verify.
- Introduce RoomSelectNavigator so that RoomSelectPresenter can request the verification of the selected session.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 17:00:48 +02:00
Jorge Martin Espinosa d8a072755f Add edit and redaction send state support (#7840)
* Expose edit/redaction send states and retry/abort send from the Rust SDK

Pending edits and redactions have their own send queue entries, separate
from the original event. Surface them so the UI can reflect and act on them:

- Add `SendTarget` (Event, Edit, Redaction, Reaction) to the matrix API,
  with a mapper to the Rust SDK type.
- Add `editSendState` and `redactionSendState` to `EventTimelineItem` and
  map them from the SDK item.
- Add `Timeline.retrySend` / `Timeline.abortSend`, which take a `SendTarget`
  so the right queued request is retried or removed. Implement them in
  `RustTimeline` and in `FakeTimeline`.

* Track edit and redaction send states on timeline events

`TimelineItem.Event` now carries `editSendState` and `redactionSendState`
next to `localSendState`.

- `failedToSend` is true when any of the three states is failed, so a
  failed edit or redaction is shown like a failed message.
- `pendingSend()` returns the first pending state together with its
  `SendTarget`; the read receipt row, the send failure dialog and the
  timeline presenter use it instead of reading `localSendState` directly.
- `TimelineItemReadReceiptView` is restructured so the row is only
  rendered when there is something to show, and the send state takes
  precedence over read receipts.
- Update the previews provider and test fixtures for the new fields, and
  make `aMessageEvent`'s send state nullable.

* Retry and remove failed sends through the timeline

Retrying or removing a failed message used the event's `SendHandle`, which
only knows about the original send. Route both through the timeline with
the `SendTarget` of the pending request instead:

- `TimelineController` gains `retrySend` (which also re-enables the send
  queue) and `abortSend`, acting on the current timeline.
- `MessagesPresenter` retries via `retrySend`, and does nothing when the
  event has no pending send state.
- Redacting something that was never sent (no event ID) or a pending
  edit/redaction now aborts the queued request rather than sending a
  redaction, and no confirmation is asked for.
- Update the presenter tests accordingly.

* Log send queue errors

Log when the SDK reports a send queue error for a room, and when the
send queue is disabled, to help diagnose messages stuck in the queue.

* Make sure we display send queue errors before verification states

* Fix logic for displaying read receipts and add more screenshot tests

* Only use states with read receipts in `ReadReceiptBottomSheetStatePreviewParam`

* Handle `LocalEventSendState = null` state just like `LocalEventSendState.Sent`

* Add `TimelineItemAction.AbortSend` and use it for both:
  1. Aborting sending a message/edit/redaction from the 'sending failed' dialog.
  2. Abort sending a local message when selected from the timeline -> redact action.

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
2026-10-09 10:30:52 +02:00
mxandreas 13a5c5ce1d Merge remote-tracking branch 'origin/develop' into mxandreas/disable-public-rooms
# Conflicts:
#	enterprise
2026-10-09 09:58:27 +03:00
mxandreas 118b5448ac Revert to the SessionEnterpriseService approach 2026-10-09 09:50:14 +03:00
Jorge Martin Espinosa 1564de9b6e Iterate on unread counts again (#7869)
* Fix timestamp color to match the designs.

* Don't limit the call and unread row height to 16dp: the font size is not fixed and it might display components in a broken state

* Ensure the alignment works

* Remove `SemanticColors.unreadIndicator` and use `.iconAccentPrimary` and `.textActionAccent` instead

* Simplify and document `UnreadIndicatorAtom`

* Use `fontBodySmMedium` for the font size in the unread indicator

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
2026-10-09 08:30:43 +02:00
ganfra c4173ae185 Fix the row selection documentation about sliding content 2026-10-08 17:42:08 +02:00
ganfra 512bab05a6 Merge branch 'develop' into feature/fga/timeline-multi-forward 2026-10-08 17:38:14 +02:00
Benoit Marty 9bc7e751c4 Keep the incoming share displayed when the current account is signed out
The incoming share was displayed in the logged in flow of the current
session, so it was destroyed when the current session was signed out, for
instance from another device. When another session was signed out, the room
select screen was kept and displayed an error if this session was selected.

To make the behavior consistent:
- The incoming share is now displayed by the RootFlowNode, on top of the
  logged in flow, and does not depend on a session anymore. The latest used
  session is passed from the RootFlowNode to the room select screen, through the
  share flow, and is used as its initial session.
- When the root of the RootFlowNode backstack changes while an incoming share
  is displayed, the new root is set below the incoming share, using the new
  replaceRoot backstack operation, instead of removing all the elements.
- The room select screen handles the invalidation of the current session like
  for any other session.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 09:45:57 +02:00
Benoit Marty 3a93e8089e Get the media optimization config only once when sharing media
The config is the same for all the files and all the rooms, so there is no need to read it for each file.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 17:06:42 +02:00
Benoit Marty 72e3a7d92b Introduce ShareDataSender so that SharePresenter is not session scoped
Instead of creating a SharePresenter from the graph of another session, the
SharePresenter now gets the client of the selected session from the
MatrixClientProvider and uses an AppScope ShareDataSender to send the data.

To be able to send media from AppScope, MediaSenderRoomFactory is now bound in
AppScope and takes the MatrixClient as a parameter, and a new
MediaOptimizationConfigProviderFactory provides the media optimization config
of any session.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 15:50:08 +02:00
Benoit Marty 792e0adde3 Let the user switch account from the room select screen when sharing
When several accounts are signed in, sharing into the app no longer goes through the account selection screen. The room select screen (Share mode) now displays the selected account and an account switch on top of the room list, with a design similar to the multi account section of the settings.

Selecting another account displays the rooms of this account in place, scrolled to the top, without recreating the screen. The content is then shared from the room select screen using the selected account, with the same progress and error dialogs as when sharing with the current account. The current account is switched only once the share has succeeded, then the room is opened if the content has been shared to a single room.

Implementation:
- RoomSelectNode is now in the AppScope and uses MatrixClientProvider to get the room list of the selected session.
- ShareNode gets the SharePresenter of the selected session from its session graph, using the new ShareBindings.
- SessionGraphFactory is moved to the matrix api module so that it can be used by features.

The account selection screen is still used for permalinks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 15:50:06 +02:00
Benoit Marty dc031dd0e6 Move SessionData.toMatrixUser() extension to a common place, then use it across the codebase. 2026-10-07 15:50:05 +02:00
Jorge Martin Espinosa 3fe0e09e3c Add global search backfill worker (#7860)
* Add search backfill worker

This will run on background at most once an hour while the device has internet connectivity and the device is charging and start back-paginating through the user's rooms to make sure the search results are populated.

It'll run for the maximum time the default workers allow, 10 minutes.

* Create `RustMatrixClient.Factory` AssistedFactory to help with DI and add `SearchBackfillService`

* Start search backfill when entering the search screen, just once. This should help getting initial search results if none or few are found at first
2026-10-07 10:41:19 +00:00
Jorge Martin Espinosa 3944cb3bfc Fix toggling 'show all activity in room list' not working (#7871)
Toggling this value had no effect because the feature was marked as finished so it wouldn't be displayed in the developer settings, which makes our code ignore any user-set value for it.

Instead, just create a `Feature` outside the `FeatureFlags` enum.
2026-10-07 10:09:25 +02:00
ganfra 996e778222 Merge pull request #7845 from element-hq/renovate/maplibre.compose
Update maplibre.compose to v0.18.0
2026-10-06 16:23:45 +02:00
ganfra 604c5438c6 Adapt location feature to maplibre.compose 0.18.0 API 2026-10-06 15:45:40 +02:00
ganfra 2cda8a0315 Merge pull request #7851 from element-hq/sync-localazy
Sync Strings
2026-10-06 13:30:00 +02:00
Benoit Marty ac2b76de61 Merge pull request #7859 from element-hq/feature/bma/fixPackageNames2
Fix remaining package names and namespaces not matching their module
2026-10-06 13:13:39 +02:00
Benoit Marty 08a0885cfc Merge pull request #7821 from element-hq/bma/removeUnusedDependencies
Remove unused Gradle dependencies
2026-10-06 12:23:32 +02:00
Benoit Marty 1833eefaf0 Move FakeCreateRoomEntryPoint to the features.createroom.test package
It was in `features.createroom.api`, while it is in the `createroom/test` module.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 12:10:54 +02:00
Benoit Marty 422dcb19b0 Move FakeAnnouncementService to the features.announcement.test package
It was in `features.rageshake.test.logs`, probably a copy-paste leftover.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 12:10:53 +02:00
Benoit Marty 845a193500 Move viewfolder/impl tests to the features.viewfolder.impl package
The tests and fakes were in `features.viewfolder.test.*`, which looks like a package of a test module, while they are in the `impl` module and test code from `features.viewfolder.impl.*`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 12:10:20 +02:00
Benoit Marty 0cf1d82281 Move StartChatNavigator to the features.startchat.impl package
It was the only file of the `startchat/impl` module in the
`features.startchat` package.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 12:10:19 +02:00