Cover the multi account branches of RootFlowNode.onLoginLink:
- the session matching the login hint becomes the latest session
- the login link is ignored when multi account is disabled
- the login link is ignored when the account provider is not allowed
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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>
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.
Closeselement-hq/element-android-enterprise#69
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* 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>
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>
* 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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
- 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>
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>
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>
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>
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>
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>