Extract the logic to observe the total unread count of a MatrixClient, refreshed on each sync
heartbeat, to a shared extension and use it in HomePresenter and PreferencesRootPresenter.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This is mostly done when uploading bug reports and on one hand, it doesn't really make sense since the binary contents will be unreadable anyway, and on the other it also messes up with our debug tools, making debugging some issues harder.
In the settings screen, when the other accounts section is expanded, display a counter with the
total number of unread notifications for each non-active account. The count is read from the
account's MatrixClient (if in memory) and refreshed on its sync heartbeat.
Part of #7769
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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>
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>
The case where we move the pointer once we've started the click on a link wasn't taken into account, so the click or long click gestures weren't canceled if the gesture ended up being a 'move' one.
Simplify the handler and make it more idiomatic too.
The `File.length()` docs state:
> Returns the length of the file denoted by this abstract pathname. The return value is unspecified if this pathname denotes a directory.
On Android, it *seems* to currently have the right behaviour, summing up the size of its children, which made `File.getSizeOfFiles()` double-count the size of some directories.
To avoid this, and be able to control the actual behaviour no matter what future Android versions do, let's count those ourselves.
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>
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>
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>
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>
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>
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>
* Update Matrix Rust SDK to 26.10.07-nightly
* Adapt to the new QR code login task API
`generate()` and `scan()` of `GrantLoginWithQrCodeHandler` and `LoginWithQrCodeHandler`
now return a task instead of suspending until the flow is over. Wait for the task to
complete, and cancel it if the coroutine is cancelled, so that the flow is also stopped
on the Rust side.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Add tests for the cancellation of the QR code login tasks
Check that cancelling the coroutine of the link new device handlers and of the QR code
login cancels the task returned by the SDK, so that the flow is stopped on the Rust side.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Fix import ordering in the link new device handler tests
ktlint expects the imports to be sorted in lexicographic order.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Benoit Marty <benoit@matrix.org>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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>
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>
The push history was written to the database synchronously, from the app coroutine scope which runs on the main thread, for every received push. Make PushHistoryService.onPushResult suspend and run all the database queries on the IO dispatcher.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Checking if the work is already scheduled does a blocking WorkManager query, which was done on the main thread at session start.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Same issue as when refreshing the profile manually: the profile listener stored the profile (Base64 encoding and session database access) from the session coroutine scope, which runs on the main thread.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The thread list subscription was launched in the room coroutine scope, which runs on the main thread, so the initial sync FFI call and the mapping of all the items and diffs were done on the main thread every time a room was opened.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`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>
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>