Commit Graph

652 Commits

Author SHA1 Message Date
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 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 23a79ad987 Merge pull request #7896 from element-hq/sync-localazy
Sync Strings
2026-10-12 13:56:43 +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
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 3c212ae9a3 Move SessionGraphFactory back to the appnav module
The share feature does not need it anymore, so there is no reason to expose it
in the matrix api module.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 15:50:09 +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 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 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 fe8b3764e5 Move matrixmedia sources to the libraries.matrixmedia.* packages
The matrixmedia modules were using the `libraries.matrix.ui.media` package
(shared with the matrixui module) and `libraries.matrix.ui.media.*`
namespaces. Use `libraries.matrixmedia.api`, `libraries.matrixmedia.impl`
and `libraries.matrixmedia.test` instead, matching the module paths.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 12:10:14 +02:00
Benoit Marty 9cb68844da Merge pull request #7865 from element-hq/feature/bma/multiAccountBottomSheetAnnouncement
Announce multi-account support
2026-10-06 12:09:38 +02:00
Benoit Marty 902631362e Merge pull request #7858 from element-hq/feature/bma/fixPackageNames
Fix wrong package names and namespaces in several modules
2026-10-06 10:58:25 +02:00
Benoit Marty b1f4178100 Replace AppMigration11 by a dismissal when entering the login flow
AppMigration11 was run once, in the first release containing it, whatever
the state of the MultiAccount feature flag. Users installing the app
while the feature flag was disabled were considered as new users and
would never see the announcement once the feature flag got enabled.

The multi-account announcement is now pending by default (NeverShown),
and is marked as shown when the user enters the login flow while the
feature is enabled, either for the first time or to add another account.
Users who were already logged in before the feature became available
will see the announcement.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 10:30:21 +02:00
Benoit Marty c654bf0947 Announce multi-account support
Show a one-time bottom sheet on the home screen to announce that several
accounts can now be added (element-hq/element-meta#3349).

- Add `Announcement.MultiAccount`, which is never shown by default.
- AppMigration11 queues the announcement on application upgrade only,
  and marks it as shown if the user already has several accounts.
- HomePresenter shows the bottom sheet only when the announcement is
  pending, the MultiAccount feature flag is enabled and the user has a
  single account.
- The "Add account" button hides the sheet, marks the announcement as
  dismissed and starts the add account flow through the new
  `HomeEntryPoint.Callback.navigateToAddAccount()`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 09:05:28 +02:00
Jorge Martin Espinosa b76548f9a2 Dismiss notifications for rooms fully read in other devices (#7846)
* Dismiss notifications for rooms fully read in other devices
2026-10-05 17:56:16 +02:00
ganfra 5ab9860e89 Merge branch 'develop' into feature/fga/timeline-multi-forward 2026-10-05 17:54:00 +02:00
Benoit Marty ab20281b26 Fix wrong package names and namespaces in several modules
Some modules were using package names or namespaces inherited from copy-paste
or from older module names (e.g. `features.logout.impl` in the deactivation
module, `features.api` in the migration api module, `sessionstorage` in the
cachestore module), or containing typos (`roomaliasesolver`,
`roomdetetailsedit`).

Move the sources to the package matching their module, update the imports, and
rename the impacted screenshots accordingly.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 13:43:22 +02:00
Benoit Marty 954eb731e7 Remove unused Gradle dependencies
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>
2026-10-02 15:47:48 +02:00
Benoit Marty 4602e1f32d Show the account receiving the incoming user verification request 2026-10-02 08:11:57 +02:00
Benoit Marty 86caf33504 Use withPreviousValue() to detect removed sessions
Replace the hand-written runningFold in RootFlowNode.observeRemovedSessions() with the existing withPreviousValue() helper, as done in IncomingVerificationRequestObserver.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 18:37:40 +02:00
Benoit Marty d68e92a4f0 Remove verification listeners of logged out sessions
- Clear the listener when RustSessionVerificationService is destroyed.
- IncomingVerificationRequestObserver removes the listener of the clients which are not in the MatrixSessionCache anymore.
- Remove the sessions from the MatrixSessionCache when they are removed from the SessionStore (logout, account deactivation).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 18:23:14 +02:00
ganfra 2d30dc6cf0 Merge branch 'develop' into feature/fga/timeline-multi-forward 2026-10-01 15:23:42 +02:00
Benoit Marty 36547dd5c5 Display incoming verification requests of all the signed-in accounts
Restore all the sessions with a valid token so that they sync and can receive
incoming verification requests, and listen to the requests of all of them with
IncomingVerificationRequestObserver.

Requests for the current account are still displayed by LoggedInFlowNode.
Requests for another account are displayed on top of the current account,
without switching, by OtherSessionIncomingVerificationNode which sets up the
Session graph of the account receiving the request.

Closes #7770

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 17:01:14 +02:00
Benoit Marty d70316ea41 Merge branch 'develop' into renovate/androidx.compose.material3-material3-1.x 2026-09-30 08:02:03 +02:00
ganfra 4563a30795 Merge branch 'develop' into feature/fga/timeline-multi-forward 2026-09-29 20:57:37 +02:00
Benoit Marty 4c4389b29a Remove unnecessary Material3 experimental opt-ins
Many Material3 APIs are now stable, so the `ExperimentalMaterial3Api` and `ExperimentalMaterial3ExpressiveApi`
opt-ins are not required anymore at those places. Also remove the propagating `@ExperimentalMaterial3Api`
marker from `TopAppBarScrollBehaviorLayout`, which does not use experimental API anymore.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 18:45:30 +02:00
Valere f052f755b6 Follow a native call into picture-in-picture from the app
supportsPictureInPicture and smallestScreenSize have to be declared here, because a library cannot change a host Activity's manifest entry.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-29 18:44:49 +02:00
ganfra 193a5b1dbc Merge branch 'develop' into feature/fga/timeline-multi-forward 2026-09-29 17:15:40 +02:00
ElementBot c8c242476b Sync Strings (#7787)
* Sync Strings from Localazy

* Update screenshots

---------

Co-authored-by: bmarty <3940906+bmarty@users.noreply.github.com>
2026-09-28 11:32:40 +02:00
ganfra a0bfdd379c Pass a list of event ids to the forward screen 2026-09-25 18:47:17 +02:00
Benoit Marty 60000bfa58 Merge pull request #7692 from element-hq/bma/fix7689
Fix blank screen at launch when the homeserver expires the sliding sync session
2026-09-23 12:32:00 +02:00
bmarty d1e0544102 Sync Strings from Localazy 2026-09-21 00:54:07 +00:00
ganfra b54937793f Merge branch 'develop' into sync-localazy 2026-09-18 10:39:43 +02:00
Benoit Marty c512e3f299 Introduce an EmptyState, so that screenshot filename stays consistent. 2026-09-15 10:52:45 +02:00
Benoit Marty 25505d6e8a Do not automatically restart the sync in case it is Terminated.
Also simplify the computation of the backoff delay.
2026-09-15 10:04:19 +02:00
Benoit Marty 6887092ce4 Replace String by AccountProvider to be able to get baseUrl and serverName. 2026-09-14 09:56:16 +02:00
bmarty 5102f6b0fa Sync Strings from Localazy 2026-09-14 00:54:38 +00:00
Benoit Marty d59120aad0 Restart the sync service when it stops on an error
Synapse deletes sliding sync connections that have been unused for 7 days. When
an OEM battery manager has kept the application frozen for longer than that, the
next sliding sync request is answered 400 M_UNKNOWN_POS.

The Rust SDK handles that: it resets `pos` and, in offline mode, restarts the
sync. When that recovery gives up, the sync service ends up in the Error or
Terminated state and stops for good, and it is then up to the application to
call start() again. Element X iOS does it, we do not: SyncOrchestrator only
restarts the sync service when its state is Idle, so the sync stays dead for the
rest of the process lifetime.

Nothing recovers from there. The sync never reaches Running, so
waitForE2eeInitializationTasks() never returns, the session verified status
stays Unknown, and the FTUE is stuck on WaitingForInitialState. Clearing the
application data is the only workaround.

Restart the sync service when it is in the Error or Terminated state, with an
exponential backoff from 1s to 30s to avoid hammering a failing homeserver. The
backoff is only reset once the sync service has been running for a while, since
startSync() moves the state to Running right away.

Fixes #7689

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-11 12:48:58 +02:00
Benoit Marty 562e439447 Show a loading indicator on the placeholders which can stay on screen forever
emptyNode() renders nothing at all, which is fine for a placeholder that is left
within a few frames, but makes the application look frozen when the wait can
last, or never end.

Add a delayBeforeShowingContent parameter to emptyNode(): when it is finite, a
progress indicator and a "Loading…" label fade in after that delay, reusing the
existing DelayedVisibility helper. It defaults to Duration.INFINITE, so the
splash screen keeps rendering an empty canvas.

Use a one second delay on the two placeholders which are not guaranteed to be
left: the FTUE one, rendered while waiting for the session verified status, and
the LoggedInFlowNode one, rendered while the FTUE state is unknown.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-11 12:48:44 +02:00
ElementBot aced1ea64d Sync Strings (#7657)
* Sync Strings from Localazy

* Update screenshots

---------

Co-authored-by: jmartinesp <480955+jmartinesp@users.noreply.github.com>
2026-09-07 14:19:38 +02:00
jmartinesp dfccde93c6 Sync Strings from Localazy 2026-09-02 10:07:17 +00:00
hayaksi1 30c18b8a00 Say when the server cannot be reached instead of showing nothing
While the homeserver is unreachable but the device still has a working
network, the app showed no indication at all: the syncing chip is hidden
as soon as the SDK reports the session offline, and the offline banner
only appears when the device itself has no connectivity. A blocked or
down server was therefore indistinguishable from a quiet, healthy one.

The chip now has three states rather than a boolean, and the third says
"Server unreachable" for exactly that case. When the device has no
network the chip stays hidden, because the offline banner already says so
and two overlapping messages would be worse than one.

Part of #4099
2026-08-25 20:51:27 +03:00
Benoit Marty 64f1f299e6 LoggedInEvents -> LoggedInEvent 2026-08-25 09:50:04 +02:00
Benoit Marty e50dee8b9c Remove UserStatus feature flag. 2026-08-24 16:22:08 +02:00