1461 Commits

Author SHA1 Message Date
Sedat Qaja f18c3acdfc docs(healthchat): record the ruleset toggle for the sync force-push 2026-10-20 15:01:37 +02:00
Sedat Qaja 767703f845 chore(release): date the 26.10.0 release notes 2026-10-20 13:58:51 +02:00
Sedat Qaja 67dadb4571 feat(healthchat): drop Element links, labs, bug reports and location sharing
Bloom gradient fades the brand blue instead of Compound's green.
2026-10-20 13:39:31 +02:00
Sedat Qaja 9df5d95732 docs(healthchat): add the upstream sync skill 2026-10-20 11:03:45 +02:00
ElementBot 44451c733f Update screenshots 2026-10-19 17:33:31 +02:00
Sedat Qaja 2a79103060 ci(release): pin the actions and keep inputs out of the shell
zizmor flags the HealthChat release workflow now that the rebase PR shows
the whole file as new: five actions referenced by floating major tags, the
workflow_dispatch version input expanded straight into a shell script, and
a Gradle cache on a release build.

- Pin checkout, setup-java, setup-gradle, upload-artifact and
  upload-google-play to commit SHAs with their version in a comment, the
  way every upstream workflow does.
- Pass the version input through the environment instead of expanding it
  into the run script.
- Disable the Gradle cache for the release build so a poisoned cache from
  another workflow cannot reach the signed bundle.
2026-10-19 16:42:35 +02:00
Sedat Qaja b0a577d2b2 ci: let the screenshot recorder pull LFS on the private fork
The cached LFS checkout action checks out with persist-credentials: false
and then runs `git lfs pull` on a cache miss. Upstream is public, so that
pull works anonymously; on this private fork it fails with
`could not read Username for 'https://github.com'`, which is why the
Record-Screenshots label never produced a recording here (the only
successful runs were manual dispatches with a warm LFS cache).

Keep the job token configured for both LFS checkout steps. The push of
the recorded goldens already authenticates with the same token.
2026-10-19 16:34:45 +02:00
Sedat Qaja db839dc5c3 Adapt HealthChat to upstream v26.10.0
Follow-ups needed after rebasing the HealthChat series from v26.07.0 onto
v26.10.0 that span several of the replayed commits:

- Rename the five PreviewParameterProvider subclasses in features/healthchat
  to the *PreviewParam suffix now enforced by Konsist.
- Rename MemberDirectoryEvents and HealthChatProfileSectionEvents to the
  *Event suffix used by every upstream sealed interface.
- Import FakeStartDMAction from features/startchat/test, where it lives now.
- ListItem lost its headlineContent parameter in favour of content.
- HomeView gained onAddAccountClick and onSwitchToAccount, and onRoomClick
  now carries an EventId; update HomeViewBackHandlingTest accordingly.
- Restore upstream's codecov-action pin (v7.1.1) that the CI squash carried
  back to v7.0.0.
- Gate the new pr-checks.yml on the upstream repository: this fork does not
  use the PR- label convention.
- Add the 26.10.0 section to HEALTHCHAT_CHANGES.md.
2026-10-19 09:05:35 +02:00
joel.poyet 9cb72c74aa chore(release): prepare 26.08.0 release notes
Fold the member directory, profile editor, invite and status banner features into the unreleased 26.08.0 changelog section.
2026-10-19 08:56:59 +02:00
joel.poyet eabfca5c75 Add profile editor, member invite and status banners (#4)
* feat(healthchat): add profile editor, member invite and status banners

Native 13-field profile editor with diff-only saves and avatar upload
through the HealthChat API (replaces the upstream display-name editor),
email invitation for approved accounts, and non-dismissible account
status banners in settings and the member directory.

* fix(healthchat): satisfy detekt and konsist rules

Konsist presenter helper shape, no '=' in state parameter docs, present-tense invite callback, and complete renderer KDoc.
2026-10-19 08:56:59 +02:00
joel.poyet 1cf01cdc62 Add member directory tab to home navigation (#3)
* feat(healthchat): add member directory tab to home navigation

Third bottom-nav tab backed by the HealthChat API: searchable member
cards with native start-chat, approval-pending banner, retry on
failure and navigation into the native profile screens.

* fix(healthchat): stop tab back handler swallowing back presses

While a screen is pushed on top of Home, the still-composed tab
BackHandler intercepted back presses during the transition and
invisibly switched to the Chats tab. Gate it on the node's target
visibility in both the parent and Home's own backstack.

* fix(healthchat): satisfy detekt, ktlint and konsist rules

Import alias order, composable parameter order in HomeView, and konsist presenter helper shape.

* ci: pass the real repository name to the screenshot recorder

recordScreenshots.yml set GITHUB_REPOSITORY from `secrets.GITHUB_REPOSITORY`.
No such secret exists on this fork, so the expression expands to an empty
string and overrides the built-in environment variable of the same name.
recordScreenshots.sh checks it up front and exits 1 with "No repo specified",
so the workflow could never record anything here.

Use the `github.repository` context instead, which is always populated.

Claude-Session: https://claude.ai/code/session_01MQbp1EPM4TSXq8P8Pyq44F

* Update screenshots

* Revert "Update screenshots"

This reverts commit b99e0d643f.

* Reapply "Update screenshots"

This reverts commit 85bceb6d67.

* test(screenshots): stop a LayoutLib teardown crash failing the shard tests

Paparazzi 2.0.0-alpha05 / LayoutLib 16.2.1 races on HandlerThread startup:
HandlerThread_Delegate.run calls Process.setThreadPriority, which throws
NoSuchMethodError for Thread.setPosixNicenessInternal and kills the
"android.bg" thread. If the test thread reaches getLooper() after that, it
gets null, BackgroundThread.ensureThreadReady() NPEs, and the static
initialiser of LatencyTracker fails. The JVM then marks that class erroneous
for the lifetime of the fork, so every later preview showing the soft
keyboard dies with NoClassDefFoundError.

LayoutLibErrorFilterStatement already swallowed the bare NoSuchMethodError,
but PaparazziLogger re-throws collected background errors from teardown()
wrapped in a MultipleFailuresException when there is more than one, and that
wrapper slipped through. A recent run reported 17 failures with zero image
comparisons failing and zero delta images written.

Unwrap the wrapper by reflection and swallow it only when every collected
cause matches the known-bug list; anything unrecognised, and anything that
cannot be decomposed, is rethrown.

Also close a hole this filter had from the start. Paparazzi runs the test as
`try { body } finally { teardown() }`, so a teardown error discards whatever
the body threw — including a genuine snapshot mismatch. renderFailureCapture
is registered inside the Paparazzi rule to record the body's exception so it
wins over any teardown error, and the filter refuses to swallow anything if
that rule is not registered. Two HorizontalFloatingToolbar previews have been
mismatching silently behind this, and may now surface as real failures.

Claude-Session: https://claude.ai/code/session_01MQbp1EPM4TSXq8P8Pyq44F

---------

Co-authored-by: ElementBot <android@element.io>
2026-10-19 08:56:59 +02:00
joel.poyet 21444b45e7 test(healthchat): satisfy konsist presenter helper rule
Konsist requires createPresenter helpers to omit the return type.
2026-10-19 08:47:59 +02:00
joel.poyet 8f82e578f4 feat(healthchat): show profile fields on profile screens
Renderer slot in UserProfileView (all three profile screens) shows
specialty, practice, address, contact links and further info from
the HealthChat API, with inline retry on failure.
2026-10-19 08:47:59 +02:00
joel.poyet 9f8a39bb74 Add HealthChat API service layer (#1)
* feat(healthchat): add HealthChat API service layer

New features/healthchat modules: typed Retrofit client with
per-request Matrix token auth, member directory, profile and
invite services, plus test fakes and fixtures.

* test(healthchat): use runCatchingExceptions

Detekt's RunCatchingNotAllowed rule forbids plain runCatching.
2026-10-19 08:47:34 +02:00
joel.poyet 5eee7737b9 fix(lint): drop dead onboarding title overrides 2026-10-19 08:47:33 +02:00
joel.poyet 345a6bb8ec feat(analytics): disable analytics unless credentials are configured
Removes the onboarding opt-in screen, the Analytics settings entry and
the unused Posthog and Sentry SDKs.
2026-10-19 08:47:33 +02:00
joel.poyet 55ea61bf5a fix(icon): ship the monochrome launcher icons as webp
Matches upstream's format, which is what tools/lint/lint.xml already
exempts from IconExpectedSize.
2026-10-19 08:47:33 +02:00
Sedat Qaja 708535469e ci: run the fork's pipelines on the self-hosted runners
Squash of ten CI commits from 2026-08-04 (all part of HealthChat release 26.08.0):

- ci(release): move releases to the persistent self-hosted runners
- ci: grant contents:read so checkout works on this private fork
- ci: disable the Label Community PRs job on this fork
- ci: run the heavy Gradle jobs on the self-hosted runners
- ci: rely on the runner image for the Android SDK
- ci: size the Gradle heap for the self-hosted containers
- ci: report runner capacity on the self-hosted jobs
- ci: record why the Gradle heap is reduced
- ci: cap Gradle workers so test JVMs fit the container
- ci: skip the codecov upload when no token is configured

The danger.yml hunk from the contents:read commit is omitted: upstream
replaced Danger with a github-script workflow in v26.09.
2026-10-19 08:47:32 +02:00
joel.poyet 3db17825de fix(release): drop changesNotSentForReview from Play upload
Play now sends this app's changes for review automatically and the Edits
API rejects the parameter, so the AAB uploaded but the edit never committed.
2026-10-19 08:46:59 +02:00
joel.poyet c8e444e2e0 feat(share): share user permalinks via to.healthchat.ch
Mention pills keep the matrix.to wire format other clients expect.
2026-10-19 08:46:59 +02:00
joel.poyet e4ca0e6208 feat(deeplink): point deeplinks at HealthChat hosts
Claims app.healthchat.ch and to.healthchat.ch as App Links, registers the
healthchat:// scheme, and drops the unreachable mobile.element.io handling.
2026-10-19 08:46:58 +02:00
joel.poyet fd29aea9a9 docs(release): document self-hosted runner and heap sizing 2026-10-19 08:46:58 +02:00
joel.poyet f49e5b8ac6 fix(gplay): strip permissions Play Console rejects 2026-10-19 08:46:58 +02:00
joel.poyet 4778fe4a98 ci(release): add HealthChat Play Store release pipeline 2026-10-19 08:46:57 +02:00
Sedat Qaja b975611e63 feat(branding): add HealthChat launcher icon and logo
Official cross-in-circle icon (same art as the iOS X app); onboarding
title localized for en/de/fr/it.
2026-10-19 08:46:57 +02:00
Sedat Qaja 659f3e174e feat(branding): rebrand app identity to HealthChat
App id ch.healthchat.element, locked matrix.healthchat.ch homeserver.
2026-10-19 08:46:57 +02:00
Benoit Marty 21a9411676 Merge branch 'release/26.10.0' 2026-10-16 11:54:56 +02:00
Benoit Marty e6aa7d5306 Adding fastlane file for version 26.10.0 2026-10-16 11:54:53 +02:00
Benoit Marty b199bc74c6 Setting version for the release 26.10.0 2026-10-16 11:51:28 +02:00
Benoit Marty 2df5591f8c Merge pull request #7947 from element-hq/feature/bma/fixMaestroLogout
Fix Maestro logout test when the device is the only session
2026-10-16 11:11:55 +02:00
Benoit Marty f18e699bbb Fix Maestro logout test when the device is the only session
Since #7861 all the sessions of the Maestro account are ended after each run,
so the device under test is always the last session. In this case the title of
the sign out screen does not contain "Remove this device" anymore, and tapping
on the second occurrence of this text fails. Tap on the submit button using its
test tag instead.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 09:21:26 +02:00
Benoit Marty b1f04799a4 Merge pull request #7940 from element-hq/update-matrix-rust-sdk
Update SDK bindings to `26.10.15`
2026-10-15 23:09:24 +02:00
Benoit Marty 68dca1450a Merge pull request #7943 from element-hq/feature/valere/bump_ec_native_to_0.1.0-rc.8
Bump element-call-android to 0.1.0-rc.8 and the Rust SDK to 26.10.15
2026-10-15 23:07:25 +02:00
Benoit Marty 8972098b4f Merge pull request #7935 from element-hq/feature/bma/fixBadRoomId
Fix invalid room id built from thread notification tags
2026-10-15 23:05:18 +02:00
Valere 54f0e03c46 Update element-call-android to 0.1.0-rc.8 and the Rust SDK to 26.10.15
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 22:25:54 +02:00
Valere 555dca38f5 Native call: always ask the core to ring, and ring a DM only once its member has joined
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 22:21:20 +02:00
Jorge Martín 646ef491ee Update SDK bindings to 26.10.15 2026-10-15 17:05:28 +02:00
ElementBot 4ebcf960ce Update Matrix Rust SDK to 26.10.14-nightly (#7930)
* Update Matrix Rust SDK to 26.10.14-nightly

* `withSearchIndexStore` now uses `key` instead of passphrase

* Fix `RustMatrixClient` now returning `searchIndex` size too

---------

Co-authored-by: Jorge Martín <jorgem@element.io>
2026-10-15 16:47:56 +02:00
Jorge Martin Espinosa 287001f9aa Prevent foreground backfill from running when the app is active (#7932)
* Prevent foreground backfill from running when the app is active

This should be a background task, since it can severely degrade performance if the app is in use while it runs.

The worker won't run any more if the app is in foreground and will stop running if it was in background and now it's in foreground.

* Fix return inside `withTimeout`

Co-authored-by: Benoit Marty <benoitm@element.io>

* Remove initial delay: it shouldn't be necessary if the foreground state of the app is checked when the worker starts

Instead, if the app was in foreground, cancel the work and retry in 5 minutes.

---------

Co-authored-by: Benoit Marty <benoitm@element.io>
2026-10-15 16:09:09 +02:00
ganfra 8689961f0d Merge pull request #7936 from element-hq/feature/fga/fix-pill-rendering
Fix mention pill overflowing the end of the line when too wide
2026-10-15 15:16:57 +02:00
Benoit Marty 9ac71fd35d Fix room id parsing from notification tags
Thread notification tags have the form "roomId|threadId". clearReadRoomsNotifications
was building a RoomId from the whole tag, which is only validated in debug builds, so
in release an invalid room id reached RoomListService.room(). Add
NotificationCreator.getMessageNotificationRoomId(), the inverse of messageTag(), which
only keeps the part before '|' and checks it is a valid room id.

Also make RoomListServiceInterface.roomOrNull take a RoomId instead of a String.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 12:56:46 +02:00
ganfra c27bbaa1f6 Fix mention pill overflowing the end of the line when too wide 2026-10-15 12:54:25 +02:00
Benoit Marty e1b9ba21dc Merge pull request #7929 from element-hq/renovate/com.google.firebase-firebase-bom-35.x
Update dependency com.google.firebase:firebase-bom to v35
2026-10-15 10:53:04 +02:00
renovate[bot] 70151591e2 Update dependency com.google.firebase:firebase-bom to v35 2026-10-14 22:42:17 +00:00
Benoit Marty f62f5be16c Merge pull request #7922 from element-hq/feature/bma/otherAccountUnreadCount
Show the unread count of the other accounts in the multi-account section
2026-10-14 21:30:36 +02:00
Benoit Marty a3fa7aeedd Merge branch 'develop' into feature/bma/otherAccountUnreadCount
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 20:15:02 +02:00
ganfra 7fe10b32b0 Merge pull request #7905 from element-hq/feature/fga/timeline-multi-forward-ay11
Improve accessibility of timeline selection mode
2026-10-14 18:56:38 +02:00
Benoit Marty 0cf2eeb931 Merge pull request #7904 from element-hq/feature/bma/swipeAvatarAnnouncement
Announce the swipe gesture on the account avatar to switch account
2026-10-14 18:19:48 +02:00
Benoit Marty 9efe0db570 Dismiss the swipe avatar announcement in the app coroutine scope
HomeView sends HomeEvent.DismissSwipeAvatarAnnouncement right before switching account. Switching
account replaces the LoggedInFlow root node, which destroys HomePresenter and cancels its
composition coroutine scope, so the dismissal could be cancelled before the announcement status
was stored. Use the app coroutine scope instead, which survives the account switch.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 18:00:56 +02:00
ElementBot c3f178985d Update screenshots 2026-10-14 16:00:19 +00:00
Benoit Marty e186e62a0e Move RoomListBanner to its own file
The test source set also has a RoomListState.kt file in the same package, so its RoomListStateKt
class was shadowing the one of the main source set, and the toRoomListBanner() functions were
not found when running the tests (NoSuchMethodError).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 17:41:07 +02:00
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 24eee458d2 Merge pull request #7923 from element-hq/feature/bma/ciContinue
Let the CI report all the failing tests and code quality issues
2026-10-14 17:16:13 +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
ganfra 34a075db9c Merge pull request #7913 from element-hq/renovate/sqldelight
Update sqldelight to v2.4.1
2026-10-14 15:27:20 +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 2bec8e1487 Let Gradle continue after a task failure in CI checks
Add --continue to the unit tests, screenshot tests, detekt and ktlint
Gradle invocations, so that a failure in one module does not prevent
the other modules from being checked, and the CI reports all the
failures at once.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 14:46:05 +02:00
ElementBot c62c1491bc Update screenshots 2026-10-14 14:32:50 +02:00
Benoit Marty 095fda3d9e Add MatrixClient.totalUnreadCountFlow() extension
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>
2026-10-14 14:31:31 +02:00
renovate[bot] 1c500374c1 Update actions/upload-artifact action to v7.0.2 (#7921)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-10-14 12:25:19 +00: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 d00d32e98a Merge pull request #7915 from element-hq/feature/bma/doNotMixMapTilerConfigs
Do not mix the homeserver MapTiler config with the build time one
2026-10-14 14:19:22 +02:00
Jorge Martin Espinosa 3ef59a8910 Don't log OkHttp binary bodies in debug builds (#7919)
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.
2026-10-14 12:04:07 +00:00
Benoit Marty b37f1d5b71 Show the unread count of the other accounts in the multi-account section
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>
2026-10-14 12:33:14 +02:00
ElementBot be98d04f40 Update screenshots 2026-10-14 10:26:25 +00: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
Jorge Martin Espinosa 566eb1f4f3 Use local SDK library for EC native too (#7916)
When a local SDK AAR file is present, ignore the version needed by the Element Call Matrix library.

This allows us to test local SDK builds.
2026-10-14 11:14:38 +02:00
Jorge Martin Espinosa 241f8e67bd Fix side-effects when tapping on a link in the new renderer (#7906)
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.
2026-10-14 11:13:59 +02:00
Jorge Martin Espinosa 8a5c50d2b8 Fix File.getSizeOfFiles() double counting some files (#7907)
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.
2026-10-14 11:13:14 +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
renovate[bot] 9b3a9a0a2d Update dependency io.element.android:element-call-embedded to v0.28.0 (#7912)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-10-14 09:49:35 +02:00
Benoit Marty 80cfc2be8d Merge pull request #7911 from element-hq/feature/bma/disableLocationSharingWithoutMapTiler
Disable location sharing when no MapTiler config is available
2026-10-14 09:43:52 +02:00
Jorge Martin Espinosa 213ed22cad Bump SDK bindings to 26.10.12-nightly (#7909)
* Bump SDK bindings to `26.10.12-nightly`

* Fix API breaks in SDK bindings

* Fix doc comment
2026-10-13 22:28:22 +02:00
ElementBot cff4562b79 Update screenshots 2026-10-13 18:15:31 +00:00
renovate[bot] b84cf2aced Update sqldelight to v2.4.1 2026-10-13 17:56:01 +00: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
ganfra 9e12ec7505 Improve accessibility of timeline selection mode 2026-10-13 14:45:00 +02:00
Benoit Marty 5104e044ff Merge pull request #7862 from element-hq/renovate/kotlin
Update dependency org.jetbrains.kotlinx:kover-gradle-plugin to v0.9.11
2026-10-13 13:01:19 +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
ElementBot 0dce4b9e49 Update screenshots 2026-10-13 10:11:22 +00: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
ElementBot 092cded211 Update Matrix Rust SDK to 26.10.07-nightly (#7880)
* 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>
2026-10-13 11:21:21 +02:00
Benoit Marty 3170ff4422 Remove useless Unit type. 2026-10-13 10:42:05 +02:00
Benoit Marty 259fc222da Import strings for the swipe account banner 2026-10-13 09:45:23 +02:00
Benoit Marty d5780507c9 Make storeUserProfile main-safe instead of wrapping getUserProfile 2026-10-13 09:28:49 +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 6efb10df11 Write the push history off the main thread
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>
2026-10-12 14:33:17 +02:00
Benoit Marty 9be2a31c5f Schedule the background search backfill off the main thread
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>
2026-10-12 14:33:16 +02:00
Benoit Marty 149dedcc03 Store the user profile off the main thread when it is updated by the SDK
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>
2026-10-12 14:33:16 +02:00
Benoit Marty 404c26c62e Fetch and map the thread list items off the main thread
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>
2026-10-12 14:33:15 +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 80578d799f Merge pull request #7882 from element-hq/feature/bma/multiAccountShareAtRoot
Keep the incoming share displayed when the current account is signed out
2026-10-12 11:19:49 +02:00
ElementBot 9beef99760 Update screenshots 2026-10-12 09:07:31 +00: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
ElementBot 7ffb36fe4b Update screenshots 2026-10-12 08:13:03 +00: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
Jorge Martin Espinosa f12b9c77d5 Enable global search feature flag (#7890)
Enable the global search feature by default for all variants of the app
2026-10-09 15:24:29 +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
Benoit Marty 5d498ee37b Merge pull request #7883 from element-hq/feature/bma/roomSelectSearchField
Use a search field to filter the rooms in the room select screen
2026-10-09 15:09:50 +02:00
Benoit Marty 135c10b37f Merge pull request #7886 from element-hq/feature/valere/element_call_rc7
Update element-call-android to 0.1.0-rc.7
2026-10-09 14:45:39 +02:00
Benoit Marty 3ae227946b Merge pull request #7798 from element-hq/renovate/io.element.android-element-call-embedded-0.x
Update dependency io.element.android:element-call-embedded to v0.27.0
2026-10-09 14:39:16 +02:00
Benoit Marty 3b5696d37d Merge pull request #7843 from element-hq/renovate/io.sentry-sentry-android-8.x
Update dependency io.sentry:sentry-android to v8.59.0
2026-10-09 14:38:34 +02:00
Benoit Marty 85224988af Room select: address review remarks
- Scroll the list to the top using a LaunchedEffect instead of recreating the LazyListState on each search query change.
- Use isBlank to check the search query, to match the logic of RoomSelectSearchDataSource.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 14:34:45 +02:00
Benoit Marty 10afe7c18c Release room list entries resources even if onControllerCreated is cancelled
Use try/finally instead of the awaitClose block, so that the entries stream, the controller and the
dynamic adapters are destroyed even if the suspending onControllerCreated callback is cancelled or
throws before awaitClose is reached.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 14:28:17 +02:00
Benoit Marty 9bb03f7cc4 Merge pull request #7885 from element-hq/feature/bma/selectedItemFocus
Draw the keyboard focus indicator around the whole selected item
2026-10-09 11:44:48 +02:00
Benoit Marty 30a6818b7e Merge pull request #7888 from element-hq/sync-enterprise-submodule
Update enterprise submodule to latest main
2026-10-09 10:59:53 +02:00
Benoit Marty dc8d8c10b9 Merge pull request #7889 from element-hq/feature/bma/syncEnterpriseSubmoduleNoLeak
Do not list private enterprise commit messages in the submodule update PR
2026-10-09 10:47:34 +02:00
Benoit Marty dfccc42326 Do not list enterprise commit messages in the submodule update PR
The enterprise repository is private, so only mention the number of new
commits in the generated PR description.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 10:33:22 +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
github-actions[bot] 90d5e3b617 Update enterprise submodule to latest main
Update to 5c06c0f1e4fa12bf36f73cb8b4ea9018a4dece20
2026-10-09 08:30:21 +00:00
Benoit Marty 748669eaff Merge pull request #7887 from element-hq/feature/bma/syncEnterpriseSubmodule
Add a workflow to keep the enterprise submodule on the latest main commit
2026-10-09 10:29:35 +02:00
Benoit Marty 76c1d9a4d2 Add workflow to keep the enterprise submodule on the latest main
When a PR is merged on develop with the enterprise submodule targeting a commit
which is not on the enterprise main branch, open a PR to update it to the latest
commit of main. The workflow also runs daily and manually to keep the submodule
up to date.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 09:54:54 +02:00
Valere 1c6d9971f5 Update element-call-android to 0.1.0-rc.7 2026-10-09 09:45:57 +02:00
Benoit Marty a7ed73c225 Merge pull request #7853 from element-hq/mxandreas/disable-public-rooms
Add ability to disable public rooms/spaces when necessary
2026-10-09 09:40:18 +02:00
Benoit Marty 5cfbfc4746 Merge pull request #7874 from element-hq/renovate/net.zetetic-sqlcipher-android-4.x
Update dependency net.zetetic:sqlcipher-android to v4.19.1
2026-10-09 09:35:09 +02:00
Benoit Marty b62999d4c0 Draw the focus indicator around the whole SelectedItem
Previously, only the small remove button could get the keyboard focus, and the
focus ring was barely visible. Now the whole item is focusable, the focus ring is
drawn around it, and pressing an activation key (Enter, Space, D-pad center)
removes the item. Touch behavior is unchanged: only the remove button is clickable.

Also add a shared `activationKeys` set in the designsystem module.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 09:31:15 +02:00
ElementBot 2f7e2316e1 Update screenshots 2026-10-09 07:23:33 +00:00
Benoit Marty 9429bd9420 Clean up 2026-10-09 09:04:41 +02:00
Benoit Marty 2177a62ff1 Apply the room list filter requested before the controller is created
The dynamic entries controller of a room list is created asynchronously, once
the entries are observed. Filters set before that were silently dropped, so for
instance the search query was not applied to the rooms of a newly selected
account in the room select screen.

RustDynamicRoomList now owns the controller, remembers the last requested filter,
and applies it when the controller is created if it differs from the initial one.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 09:00:48 +02:00
Benoit Marty 0c6da39acb Room select: update empty search UI and behavior. 2026-10-09 09:00:47 +02:00
Benoit Marty 59bc387c7d Replace the SearchBar by a SearchField in the room select screen
The room select screen was using a SearchBar, with an active mode displaying the search results instead of the room
list. Use a SearchField instead, displayed above the room list, which filters the rooms directly. The account switch
section and the selected rooms are now also displayed while searching.

- Remove isSearchActive from the state, and the ToggleSearchActive event.
- The "No results" message is displayed when the query is not blank and no room matches it.
- The back button now always closes the screen.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 09:00:46 +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
renovate[bot] 6cb8a85539 Update maplibre.compose to v0.19.0 (#7884)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-10-09 08:43:49 +02: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 367cea0213 Merge pull request #7864 from element-hq/feature/fga/timeline-multi-forward
Initial implementation of timeline multi forward
2026-10-08 18:45:35 +02:00
ElementBot a53bf586e9 Update screenshots 2026-10-08 16:04:27 +00:00
renovate[bot] fe419890a5 Update dependency io.sentry:sentry-android to v8.59.0 2026-10-08 15:45:14 +00: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
ganfra d8c8384c26 Merge pull request #7879 from element-hq/gradlew-update-9.8.1
Update Gradle Wrapper from 9.8.0 to 9.8.1
2026-10-08 12:19:11 +02:00
Benoit Marty 51515a8fac Merge pull request #7881 from element-hq/feature/bma/fixRoomSelectScroll
Fix room list not scrollable in the room select screen when there is a single account
2026-10-08 10:20:43 +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 920e37d097 Scroll the rooms header with the search results in room select screen
Move the "Rooms" section header inside the LazyColumn of the search results, so that it scrolls with the list
instead of staying fixed above it, as it is done for the room list when the search is not active.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 09:41:05 +02:00
Benoit Marty 7ad1277faf Fix room list not scrollable in room select screen when the account switch is hidden
The nested scroll connection of the account switch section was always attached. When the section is not displayed
(single account, Forward mode, or search active), its height offset limit is never set and stays at -Float.MAX_VALUE,
so the connection consumed all the scroll events and the room list could not be scrolled.

Only attach the connection when the account switch section is displayed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 09:32:39 +02:00
gradle-update-robot c793735511 Update Gradle Wrapper from 9.8.0 to 9.8.1
Signed-off-by: gradle-update-robot <gradle-update-robot@regolo.cc>
2026-10-08 00:48:16 +00:00
Benoit Marty 48fc09a7c3 Merge pull request #7839 from element-hq/feature/bma/multiAccountShare
Multi account: switch account from the room select screen when sharing
2026-10-07 18:01:48 +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
ElementBot 4dfb35206e Update screenshots 2026-10-07 16:05:23 +02:00
Benoit Marty 54b1a909bc Display an error in the room select screen if the selected account has an invalid token
All the accounts are proposed in the account switch, even the ones with an
invalid token, so that the user can still see them. If the selected account
has an invalid token, or is invalidated while the screen is displayed (for
instance signed out from another device), the error is displayed instead of
the room list, the selected rooms are cleared, and the client of this account
is not used anymore.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 16:01:14 +02:00
Benoit Marty 3c4632ee9d Render an error in the room select screen if the rooms cannot be loaded
If the client of the selected account cannot be restored, the room list was
never loaded and there was no feedback to the user.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 15:50:11 +02:00
Benoit Marty 6f1033ee67 RoomSelectView: improve scroll behavior
Ref: https://www.figma.com/design/kEAcfun9iSpszeUDvdKZ6b/ER-351--Multi-account-in-EX?node-id=195-40292
2026-10-07 15:50:10 +02:00
Benoit Marty 30b71cf90b Use derivedStateOf for selectedAccount too 2026-10-07 15:50:09 +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 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 ed2a553736 Fix flaky test. 2026-10-07 15:50:07 +02:00
ElementBot 15f7afc7c5 Update screenshots 2026-10-07 15:50:07 +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
Benoit Marty def8f7f348 Merge pull request #7877 from element-hq/bugfix/bma/sessionDelegatePerClient
Fix the wrong account being signed out when the session of another account is revoked
2026-10-07 15:29:35 +02:00
Jorge Martin Espinosa a9d7e7e5ab Fix multi-line split inline code background drawing (#7875)
In the new timeline renderer, the line end position is sometimes incorrectly returned when you pass the offset of the last character in the line, returning `0.0f`.

This caused the shape to draw for the line to be reversed, where the start was `0.0f` and the end was the actual start.

When we detect a `0.0f` returned value for this,  just get the coordinates for the end of the line instead.
2026-10-07 15:10:19 +02:00
Benoit Marty 6e810f2ffd Use one RustClientSessionDelegate per client, so that an auth error only affects the session it belongs to
The RustMatrixClientFactory was using a single RustClientSessionDelegate for
all the clients. The delegate keeps a reference to the last bound client only,
and didReceiveAuthError does not tell which session is concerned. With several
accounts, when the session of one account was revoked, the session of the last
restored account was invalidated and its local data deleted instead, while the
revoked session kept on syncing with an invalid token. Also the isLoggingOut
flag was never reset, so any later auth error was ignored.

The clients which are not associated to a stored session (temporary clients
and clients used to perform a login) now use a NoOpClientSessionDelegate.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 14:26:47 +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
ganfra 5631a91e28 Update the multi-select feature flag description 2026-10-07 12:25:43 +02:00
ElementBot 9805422a8a Update Matrix Rust SDK to 26.10.05-nightly (#7844)
* Update Matrix Rust SDK to 26.10.01-nightly

* Add ivy repository for nightly SDK binding versions

* Fix API breaks

* Make sure we can't release a version with a `-nightly` SDK version

* Fix typo

Co-authored-by: Benoit Marty <benoitm@element.io>

* Update Matrix Rust SDK to 26.10.05-nightly

---------

Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Jorge Martín <jorgem@element.io>
Co-authored-by: Benoit Marty <benoitm@element.io>
2026-10-07 11:13:36 +02: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
renovate[bot] 03ea0ecc27 Update dependency com.google.guava:guava to v33.7.2-android (#7873)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-10-07 08:47:00 +02:00
renovate[bot] d223d6ac7d Update dependency org.jetbrains.kotlinx:kover-gradle-plugin to v0.9.11 2026-10-07 00:31:04 +00:00
renovate[bot] add7099e44 Update dependency net.zetetic:sqlcipher-android to v4.19.1 2026-10-07 00:30:51 +00:00
ganfra f10d0caad3 Merge pull request #7804 from element-hq/renovate/com.github.matrix-org-matrix-analytics-events-0.x
Update dependency com.github.matrix-org:matrix-analytics-events to v0.38.0
2026-10-06 18:35:36 +02:00
ganfra 7a6aef11ad Merge branch 'develop' into renovate/com.github.matrix-org-matrix-analytics-events-0.x 2026-10-06 17:34:34 +02:00
ganfra 47d9791a81 Merge pull request #7833 from element-hq/renovate/androidx.webkit-webkit-1.x
Update dependency androidx.webkit:webkit to v1.17.1
2026-10-06 17:30:32 +02:00
ganfra c7f5024729 Merge pull request #7868 from element-hq/renovate/gradle-actions-6.x
Update gradle/actions action to v6.4.0
2026-10-06 17:29:18 +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
renovate[bot] 4d6bc87162 Update dependency io.element.android:element-call-embedded to v0.27.0 2026-10-06 11:16:14 +00: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 e86b20d3c5 Merge pull request #7861 from element-hq/feature/bma/maestroCleanup
End all the remaining sessions of the Maestro account after the tests
2026-10-06 12:20:08 +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 caac447672 Move FullScreenIntentPermissionsPresenterTest to the libraries.fullscreenintent.impl package
The test was in `libraries.fullscreenintent.test`, while the presenter is in `libraries.fullscreenintent.impl`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 12:10:22 +02:00
Benoit Marty c64aa67e56 Move voicerecorder/impl test fakes next to the interfaces they fake
The fakes were in `libraries.voicerecorder.test`, the package of the `voicerecorder/test` module. Move them to `libraries.voicerecorder.impl.audio` and `libraries.voicerecorder.impl.file`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 12:10:21 +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 bbaa83c8b8 Move PickerTypeTest to the libraries.mediapickers.api package
The test was in `libraries.mediapickers`, while `PickerType` is in
`libraries.mediapickers.api`.

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
Benoit Marty abad21218d Move call/impl tests to the features.call.impl package
Some test sources were in `features.call`, `features.call.ui`,
`features.call.utils` and `features.call.notifications`, while the code
they test is in the corresponding `features.call.impl.*` packages.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 12:10:18 +02:00
Benoit Marty de6f820401 Fix usersearch/test namespace
The namespace was `libraries.usersearch`, without the `.test` suffix, while
the sources are in `libraries.usersearch.test`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 12:10:17 +02:00
Benoit Marty a88a5cf698 Fix pushstore/impl namespace
The namespace was `libraries.push.pushstore.impl`, while the sources are in
`libraries.pushstore.impl`. Also update the `adb uninstall` hint of the
instrumentation test accordingly.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 12:10:16 +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 6be1242ef0 Merge pull request #7867 from bardiharborow/feature/full-screen-intent-banner-refresh
Hide full screen intent banner as soon as the permission is granted
2026-10-06 11:46:15 +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 ddcd01a2bf Follow naming convention 2026-10-06 09:46:28 +02:00
Benoit Marty 72f4c4f897 Fix multi-account announcement never being displayed 2026-10-06 09:41:53 +02:00
ElementBot 86e72ac11e Update screenshots 2026-10-06 07:38:09 +00:00
Benoit Marty d91c22d1b2 Move new states at the end of the sequence 2026-10-06 09:10:47 +02:00
Benoit Marty 4d183ffeaf Add consign when AI adds new states for screenshot. 2026-10-06 09:05:28 +02:00
Benoit Marty 4e25ecd34c Dismiss the multi-account announcement when the user has several accounts
If the announcement was queued at upgrade while the user had a single
account, and the user then added another account without seeing it, the
announcement stayed pending and was shown later if the user removed an
account. Mark it as dismissed as soon as several accounts are detected.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 09:05:28 +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
Benoit Marty 2de0d78ced Import multi-account announcement strings from Localazy
Add the `screen.multi_account_announcement.*` keys to the home module
Localazy configuration and import the latest strings.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 09:05:27 +02:00
mxandreas 2e5a6b72c9 Merge branch 'mxandreas/disable-public-rooms' of github.com:element-hq/element-x-android into mxandreas/disable-public-rooms 2026-10-06 09:57:06 +03:00
mxandreas f2310cc530 Made the hooks of room creation and security & privacy screen more generic. 2026-10-06 09:53:26 +03:00
renovate[bot] 7be369a5da Update gradle/actions action to v6.4.0 2026-10-06 06:37:04 +00:00
renovate[bot] f1723efa32 Update dependency com.google.testparameterinjector:test-parameter-injector to v1.24 (#7863)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-10-06 08:33:44 +02:00
Bardi Harborow 90806a1c85 Hide full screen intent banner as soon as the permission is granted
The permission is granted from the system settings, outside of the app,
so nothing triggered a recomposition of FullScreenIntentPermissionsPresenter
when the user came back. The "Enhance your call experience" banner kept
being displayed until something else caused a recomposition.

Check the permission again with LifecycleResumeEffect, as done in
BatteryOptimizationPresenter, and add a test for this case.
2026-10-06 09:40:56 +11:00
ganfra 752b0c59b6 Move the composable lambda parameter last in TimelineItemRowContent 2026-10-05 20:48:35 +02:00
ganfra cb501f3b5f Fix the Detekt issues on the timeline selection code 2026-10-05 20:48:24 +02:00
ganfra 9b2dcd4b26 Fix the Konsist preview naming for the selection previews 2026-10-05 20:48:14 +02:00
ganfra d4310cb6e9 Use toImmutableSet instead of toPersistentSet 2026-10-05 20:48:05 +02:00
ganfra 7ef93b51e4 Use the Element Creations license header on the new selection files 2026-10-05 20:47:57 +02:00
ElementBot 721cedbc45 Update screenshots 2026-10-05 16:25:38 +00: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 774b4087f5 Merge pull request #7759 from element-hq/feature/bma/startChatIconUpdate
Update Chat icon and text font for Empty room list.
2026-10-05 17:12:47 +02:00
ElementBot 19a0b3d517 Update screenshots 2026-10-05 14:26:03 +00:00
Benoit Marty 56f8925ff3 Update Chat icon and text font for Empty room list.
Closes #7739
2026-10-05 16:03:55 +02:00
Benoit Marty f1cdd5aff5 Merge pull request #7832 from element-hq/renovate/androidx.javascriptengine-javascriptengine-1.x
Update dependency androidx.javascriptengine:javascriptengine to v1.1.1
2026-10-05 15:22:34 +02:00
Benoit Marty 3cff5f35a3 Merge pull request #7700 from element-hq/renovate/kotlin
Update kotlin
2026-10-05 15:21:42 +02:00
Benoit Marty 2864e867fa End all the remaining sessions of the Maestro account after the tests
Each Maestro run logs in to @maestroelement:matrix.org, and the sessions
pile up on the account. Run tools/mas/delete_all_sessions.py at the end
of the Maestro job, even if the tests failed, to end them all.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 15:00:37 +02:00
Benoit Marty 1f44eb4249 Merge pull request #7857 from element-hq/feature/bma/copyToClipboardNoDoubleToast
Mark the recovery key as sensitive when copying it, and do not show a copy confirmation on Android 13+
2026-10-05 14:51:04 +02:00
Benoit Marty 4464c311ad Bump enterprise module
Follow the package rename of `InMemoryCacheStore`, see
https://github.com/element-hq/element-android-enterprise/pull/66

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 14:36:56 +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
ElementBot 54409a82af Update screenshots 2026-10-05 10:02:52 +00:00
Benoit Marty b9ed090400 Use only ClipboardHelper to copy to the clipboard, and do not render feedback on Android 13+
Since Android 13, the system displays its own confirmation when content
is copied to the clipboard, so the application does not render its own
Toast or Snackbar anymore on those versions.

Context.copyToClipboard, CopyToClipboardUseCase and the direct
ClipboardManager usage in EventDebugInfoView are removed.
ClipboardHelper.copyPlainText(text, isSensitive, onSuccessOnOldDevice)
is now the only entry point, and AndroidClipboardHelper is the only
class using ClipboardManager.

Copy actions from the views are moved to their Presenter through new
events, or to the Node when there is no Presenter. A ToastHelper is
introduced so that Toasts are kept where they were used before.
Snackbars are kept where they were already used.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 11:40:08 +02:00
Jorge Martin Espinosa b038e30d28 Exclude custom state events from the timeline (#7854)
Don't display custom state events not supported by the SDK
2026-10-05 11:12:12 +02:00
Benoit Marty 38e74ff43d Merge PR #7849: Mark the recovery key as sensitive when copying it to the clipboard 2026-10-05 10:44:39 +02:00
Bardi Harborow 48d6b5cadf Improve the reset identity password field (#7850)
Use the password keyboard type, mark the field as a password for
autofill, and submit the reset with the IME "Done" action, matching
the login password field.
2026-10-05 09:38:32 +02:00
mxandreas 84fdbbe573 Add a hook to disable public rooms/spaces on UI. 2026-10-05 09:31:31 +03:00
bmarty 50460ad031 Sync Strings from Localazy 2026-10-05 00:40:51 +00:00
Bardi Harborow f59f5da64c Mark the recovery key as sensitive when copying it to the clipboard
The recovery key was copied without EXTRA_IS_SENSITIVE, so Android 13+
showed it in the clipboard preview and keyboards could store it in
their clipboard history.

Add an isSensitive option to CopyToClipboardUseCase and
Context.copyToClipboard, and set it when copying the recovery key.
2026-10-04 20:26:48 +11:00
ganfra e99861a5c1 Keep selection mode active until explicitly exited 2026-10-02 20:41:04 +02:00
Benoit Marty a6d9d27c68 Merge pull request #7827 from element-hq/feature/bma/multiAccountIncomingUserVerification
Multi account incoming user verification
2026-10-02 18:56:58 +02:00
Benoit Marty a2be5176c0 fix aaptDump.txt 2026-10-02 18:49:13 +02:00
renovate[bot] 125c38c4d2 Update maplibre.compose to v0.18.0 2026-10-02 14:44:14 +00:00
renovate[bot] 096a937a39 Update metro to v1.4.5 (#7842)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-10-02 16:41:58 +02:00
Jorge Martin Espinosa 7fd8e5b721 Fix unread indicator for messages (#7841)
* The expected logic for unread messages is displaying just a dot

The count might not be accurate, so we keep the dot for now.

* Fix tests
2026-10-02 16:36:22 +02:00
Benoit Marty 9e8fb63d8d Restore matrixui dependency needed by signedout impl
Code merged on develop (AccountInfoCard in SignedOutView) now uses
libraries.matrixui, which was removed as unused before the rebase.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 15:54:37 +02:00
Benoit Marty 094f6de40e Fix aaptDump.txt permission order
Removing unused dependencies changed the library manifest merge order, so
RECEIVE_BOOT_COMPLETED is now listed after DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION.
The set of permissions is unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 15:47:51 +02:00
Benoit Marty b9575f1740 Restore matrix.api dependency needed by lockscreen impl
PinUnlockActivity calls EnterpriseService.semanticColorsFlow(SessionId?), so the
Kotlin compiler needs matrix.api on the classpath even if no symbol from it is
imported directly. enterprise.api only exposes it as an implementation dependency.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 15:47:50 +02:00
Benoit Marty 598ea0a6d5 Remove unused androidx_constraintlayout from the version catalog
It is not used anymore since the dependency has been removed from features/messages/impl.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 15:47:50 +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
ganfra d826a72d49 Merge pull request #7815 from element-hq/renovate/org.robolectric-robolectric-4.x
Update dependency org.robolectric:robolectric to v4.17
2026-10-02 15:47:36 +02:00
ganfra 6f41999489 Merge pull request #7835 from element-hq/renovate/work
Update dependency androidx.work:work-runtime-ktx to v2.12.0
2026-10-02 15:47:03 +02:00
Jorge Martin Espinosa f5d6c00827 Bump enterprise module (#7838)
* Bump enterprise module

* Fix lint issues
2026-10-02 11:26:53 +02:00
Jorge Martin Espinosa a12977b4dc Fix remainingPlaybackAnimationMs crash (#7836)
This happens when trying to round `Float.NaN` to `Int`
2026-10-02 11:09:46 +02:00
Jorge Martin Espinosa 558c4541cd Fix StrictMode config (#7837)
Remove `penaltyDialog()` as it makes the app unusable even in debug builds
2026-10-02 10:05:08 +02:00
ElementBot 3130de8273 Update screenshots 2026-10-02 08:11:58 +02:00
Benoit Marty 4602e1f32d Show the account receiving the incoming user verification request 2026-10-02 08:11:57 +02:00
Benoit Marty 5ed880d539 Merge pull request #7823 from element-hq/feature/bma/multiAccountIncomingVerification
Multi account incoming device verification
2026-10-02 08:10:19 +02:00
Jorge Martín bfdfa836d9 Fix Robolectric 4.17 issues 2026-10-02 08:02:41 +02:00
Jorge Martin Espinosa 2dd0a47eb4 Allow displaying only unread notifications (#7803)
* Allow displaying only unread notifications

Add `FeatureFlags.ShowAllActivityInRoomList` (enabled by default, to match the current behavior) that, when disabled, will display the unread messages of a room instead of unread notifications if no unread notifications are found

* Restore UI changes to the badges, `RoomSummaryRow` and `HorizontalFloatingToolbar`

* Fix `NavigationBarIcon`: use `BadgeBox` component.

* Adjust padding in `RoomSummaryRow` to 6.dp

* Fix other indicators not displaying a dot anymore

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
2026-10-01 19:46:01 +02:00
Jorge Martin Espinosa a3ff7e1486 Make StrictMode more lenient for network on main thread violations (#7830)
* Make `StrictMode` more lenient for network on main thread violations

`rustls-platform-verifier-android:0.2.0` performs the certification revocation checks in whatever thread called the Rust code that triggered the network request. In our codebase, sometimes this is the main thread through `MainScope`.

This would usually be fine if we were using suspend APIs, since it would just wait for a result and not block the main thread. However, the cert verification logic uses some `HttpUrlConnection` calls under the hood that are fully blocking and this triggers `NetworkOnMainThreadException`, which in turn make the revocation fail.

With these changes, we log those issues but don't make them break the certificate verification. That said, we should revert this logic once a fix lands in the verifier.

* Make the `StrictMode` weaker only for release and nightly builds

This way we can fix the issues when we detect them in debug builds.

* Fix `org.matrix.rustsdk.Android.init` not being called immediately in `PlatformInitializer`

This must be called before `platformService.init(tracingConfiguration)`

* Add `penaltyDialog`, to check if it's useful or just annoying
2026-10-01 16:55:23 +00: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
renovate[bot] 11fa8a76a4 Update dependency androidx.work:work-runtime-ktx to v2.12.0 2026-10-01 16:22:42 +00:00
renovate[bot] 6e012ec7ed Update dependency androidx.annotation:annotation-jvm to v1.11.0 (#7834)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-10-01 18:18:19 +02:00
Jorge Martin Espinosa 6cde0706be Add remove item and clear history buttons to search history (#7816)
* Add remove and clear history buttons to search

* Rename `SearchHistoryResultItem` to `SearchHistoryListItem` and `MessagesSearchResultItem` to `MessageSearchListItem`

Also, add some docs.

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
2026-10-01 18:17:18 +02:00
Benoit Marty d589ff37dc Merge pull request #7831 from element-hq/feature/bma/fixScreenshotFlakiness
Stop rewriting screenshots that did not meaningfully change
2026-10-01 17:43:25 +02:00
ganfra 3847922895 Merge pull request #7826 from element-hq/feature/fga/user-status-update
Update user status UI
2026-10-01 17:33:33 +02:00
ElementBot 852d540dd0 Update screenshots 2026-10-01 14:48:38 +00:00
Benoit Marty cb19232e5a Fix compilation warnings after Kotlin 2.4.20 and KSP 2.3.12 upgrade
- Update .idea/kotlinc.xml to Kotlin 2.4.20.
- Remove `-Xannotation-default-target=first-only`: the new annotation
  default target rule is stable in Kotlin 2.4, and disabling it now
  produces a CLI_ARG_DISABLES_STABLE_FEATURE warning.
- Replace deprecated `KSNode.validate()` with
  `validate(enableNewFeatures = false)` to keep the same behavior.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 16:45:18 +02:00
renovate[bot] 3b96d840c5 Update dependency org.robolectric:robolectric to v4.17 2026-10-01 14:10:17 +00:00
renovate[bot] d00d303676 Update dependency androidx.webkit:webkit to v1.17.1 2026-10-01 14:10:03 +00:00
renovate[bot] fd9bd1b9af Update dependency androidx.javascriptengine:javascriptengine to v1.1.1 2026-10-01 14:09:54 +00:00
Benoit Marty ea9eec245a Merge pull request #7811 from element-hq/renovate/com.google.testparameterinjector-test-parameter-injector-1.x
Update dependency com.google.testparameterinjector:test-parameter-injector to v1.23
2026-10-01 16:07:48 +02:00
Benoit Marty 56f8cffe34 Remove LayoutLibErrorFilterStatement
Paparazzi 2.0.0-alpha05.1 ships layoutlib 16.2.3, which fixes the errors this
rule was swallowing. All screenshot tests pass without it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 15:58:52 +02:00
Benoit Marty 73b3282939 Stop rewriting screenshots that did not meaningfully change
Recording rewrote every screenshot unconditionally, so renderer noise far below
the verification tolerance produced large "Update screenshots" commits.

Use `app.cash.paparazzi.overwriteOnMaxPercentDifference` to keep screenshots that
are within tolerance. Existing screenshots are no longer deleted before recording;
obsolete ones are pruned afterwards, only when their preview produced no
screenshot at all.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 15:46:28 +02:00
ganfra 2d30dc6cf0 Merge branch 'develop' into feature/fga/timeline-multi-forward 2026-10-01 15:23:42 +02:00
ganfra 2fb82c5e81 Make the whole avatar row clickable, with ripple on the avatar 2026-10-01 15:21:25 +02:00
ganfra fd60ba1018 Merge pull request #7825 from element-hq/feature/fga/fix-home-scroll-connection
Fix home screen top bar consuming its own scroll events
2026-10-01 09:38:01 +02:00
ElementBot 306f96e2a6 Update screenshots 2026-09-30 19:07:56 +00:00
ganfra ff8bb78f12 Fix home screen top bar consuming its own scroll events 2026-09-30 18:02:19 +02:00
ganfra e50e6e599e Display the user status emoji beside the avatar 2026-09-30 17:58:03 +02:00
ElementBot ee65fb99ae Update screenshots 2026-09-30 15:21:35 +00: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 e6750dd301 Show the account receiving the device verification request
Replace SessionDetailsView by AccountInfoCard with the DeviceVerification mode
on the incoming device verification screen, so that the account concerned by
the request is clearly indicated, with its avatar and Matrix ID.

The current user is built from the SessionStore, including the locally stored
avatar thumbnail, used as a fallback when the avatar cannot be loaded, for
instance for an account which is not the current one.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 17:01:14 +02:00
Benoit Marty 7502ab16c5 Merge pull request #7824 from element-hq/feature/bma/iconPrimaryColor
Make sure the icon header use primary color
2026-09-30 16:47:49 +02:00
Jenna Vassar 0f29e8e875 Add an extension for room details sections (#7781)
* Add an extension for room details sections

Provide a no-op rendering hook for build-specific room details sections and shared room audio labels. Keep the default room details behavior unchanged.

* Cover the default room details extension

Verify that the no-op extension renders no content and leaves surrounding UI unchanged through recomposition. Cover its constructor and both generated render paths.

* Refine the room details extension and link the enterprise picker

Expose ColumnScope to contributed room details sections and use room-details string keys. Pin the enterprise submodule to the reviewed picker implementation so integrated builds include it behind the default-off flag.

* Make the room details extension a functional interface

Declare the single composable rendering contract as a fun interface to satisfy the Sonar check while preserving its named dependency injection binding.

* Update localazy strings for the audio section in room details

* Bump enterprise commit

---------

Co-authored-by: Jorge Martín <jorgem@element.io>
2026-09-30 14:18:13 +00:00
ElementBot d1c041094e Update screenshots 2026-09-30 14:07:57 +00:00
Benoit Marty 439898a74c Merge pull request #7819 from element-hq/renovate/sqldelight
Update sqldelight to v2.4.0
2026-09-30 15:57:25 +02:00
ganfra df81f5c0f7 Remove user status from the Matrix user header 2026-09-30 15:54:41 +02:00
Benoit Marty a597ec2dd3 Add a Figma link 2026-09-30 15:48:39 +02:00
Benoit Marty 1ac63ebec5 Icon in BigIcon have a iconPrimary color according to Figma. 2026-09-30 15:44:12 +02:00
Benoit Marty 485f71c824 Merge pull request #7822 from element-hq/feature/bma/signedOutScreen
Update signed out screen
2026-09-30 15:41:21 +02:00
Jorge Martin Espinosa 8c0d23771c Enable automatic back pagination and unread indicators by default (#7800)
* Enable automatic back pagination and unread indicators by default

We want to test these in the nightlies.
2026-09-30 15:09:32 +02:00
Benoit Marty 9459e3673e Remove unused parameter 2026-09-30 14:58:32 +02:00
ElementBot df8d76a730 Update screenshots 2026-09-30 12:29:02 +00:00
Benoit Marty 48b3eb7a0d Make the content of SignedOutView scrollable for small screens 2026-09-30 14:08:48 +02:00
ganfra e5606e6162 Merge pull request #7820 from element-hq/fix/fix-textblock-style-after-headers-pr
Remove the hardcoded text style in `TextBlock`
2026-09-30 13:25:50 +02:00
ElementBot 9b37ecc243 Update screenshots 2026-09-30 10:42:27 +00:00
Benoit Marty 6a7f0ea536 Update UI and wording of the SignedOut screen 2026-09-30 12:17:14 +02:00
Benoit Marty 84a6586551 Merge pull request #7337 from element-hq/renovate/androidx.compose.material3-material3-1.x
Update dependency androidx.compose.material3:material3 to v1.5.0-alpha29
2026-09-30 12:14:14 +02:00
Jorge Martin Espinosa 55b0273a4b Fix rustls failing the certification revocation checks on app start (#7817)
* Fix rustls failing the certification revocation checks on app start
* Also add dispatchers.io to `isElementProEnforced`
2026-09-30 10:10:59 +00:00
Benoit Marty f453cdf2ac Signed out screen: add account info.
Display the account which has been signed out, using the `AccountInfoCard`. The data comes
from the session database, including the stored avatar thumbnail, since there is no Matrix
client available to load the avatar. `SignedOutState.signedOutSession` is now a non-null
`MatrixUser`, defaulting to a user with only the session id.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 11:59:39 +02:00
Benoit Marty a2b2c2360a Merge pull request #7788 from element-hq/feature/bma/removeAccount
Improve the sign out screen
2026-09-30 11:53:19 +02:00
ganfra ae409aa8d2 Add preview for messages view in selection mode 2026-09-30 11:49:26 +02:00
ganfra bb64983832 Move TimelineItemSelectionData to model package 2026-09-30 11:48:31 +02:00
ElementBot a74252def2 Update screenshots 2026-09-30 09:32:31 +00:00
ElementBot 1510262899 Update screenshots 2026-09-30 07:34:45 +00:00
Jorge Martín c7af5b2209 Remove the hardcoded text style in TextBlock
This should fix the text size (again) in the new timeline renderer.

Looks like this happened when a cherry-pick operation went wrong.
2026-09-30 09:14:27 +02:00
renovate[bot] 07d5ba3d77 Update sqldelight to v2.4.0 2026-09-30 06:36:52 +00:00
renovate[bot] 923f2176bb Update dependency com.google.firebase:firebase-bom to v34.19.0 (#7805)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-30 08:34:37 +02:00
Benoit Marty d70316ea41 Merge branch 'develop' into renovate/androidx.compose.material3-material3-1.x 2026-09-30 08:02:03 +02:00
ElementBot 767377193a Update screenshots 2026-09-29 22:32:44 +02:00
Jorge Martín 41f7cb7b74 Fix aaptDump.txt and changelog
The previous commit changed the aaptDump.txt file too much
2026-09-29 22:03:13 +02:00
ganfra 4563a30795 Merge branch 'develop' into feature/fga/timeline-multi-forward 2026-09-29 20:57:37 +02:00
Benoit Marty 5f317aa2a0 Fix MarkdownTextInputTest with Compose 1.13
The Compose 1.13 test libraries start the test window in touch mode, where the editor is
not focusable in Robolectric, so `requestFocus()` was not giving it the focus anymore.
Use `requestFocusFromTouch()`, which leaves the touch mode first.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 20:54:27 +02:00
ganfra 62f9b7f0b4 Use "Forward to" as forward screen title 2026-09-29 20:53:51 +02:00
Benoit Marty f52242ebdd Fix PinKeypadTest with Compose 1.13
material3 1.5.0-alpha29 brings Compose UI/foundation 1.13.0-alpha01, where clickable
nodes cannot be focused in touch mode anymore when requested from the test. Switch to
the keyboard input mode first, like a hardware keyboard would do, so that the key gets
the focus and receives the key events.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 20:25:58 +02:00
Benoit Marty 4d7ba1dcd3 Update apptDump after last release. 2026-09-29 20:19:54 +02:00
Jorge Martín 3985d4b712 Add missing parts of the 26.09.4 release to develop:
- Update CHANGES.md.
- Update aaptDump.txt.
2026-09-29 19:06:33 +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 01bc7dd070 Keep the app content composed when a native call starts
The overlay now draws with no controller too, so starting a call no longer resets the content's state, such as the composer draft.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:44:49 +02:00
Valere a2be854cf9 fix: linter LocalContext should not be cast to Activity 2026-09-29 18:44:49 +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
Valere e786e2d22f Draw the native call in the app
A wrapper around the logged-in content rather than an overlay, because a minimized call takes a strip of height above it while a maximized one covers it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-29 18:44:49 +02:00
Valere 2ca33296b5 Update element-call-android to 0.1.0-rc.6
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:44:49 +02:00
Valere 0d5a79f3f2 Add the camera foreground service permission to the manifest snapshot 2026-09-29 18:44:49 +02:00
Valere e35b522a9a Answer a ringing call through the call entry point
The notification's Answer action named ElementCallActivity outright, so answering always opened the WebView whatever FeatureFlags.NativeCall was set to.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-29 18:44:49 +02:00
Valere 75605f1932 Make a call with no UI actually run, and cover it with tests
Found on a device: with no UI to answer the microphone permission or unset the maximized snapshot, the call parks forever and blanks the screen through the proximity wake lock.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-29 18:44:49 +02:00
Valere 40cb43ae87 Build the native call stack on demand for each session
Keyed by session id in app scope rather than session-scoped, because answering a call notification after a cold start has no session graph to reach a session-scoped stack from.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-29 18:44:49 +02:00
Valere 43a1364671 Give the native call component its view of Matrix
The factory lives in libraries/matrix/impl because that is the only module compiled against the Rust SDK, and is app-scoped because a call is started for whichever account it belongs to rather than the one on screen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-29 18:44:49 +02:00
Valere 0d36f70c96 Resolve element-call-android from its GitHub releases
The component and the matrix-rust-rtc core publish as GitHub release assets rather than to Maven, so two filtered ivy repositories resolve them; both become plain coordinates the day they reach Maven Central.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-29 18:44:49 +02:00
Valere 66d6d365d7 Route calls through an entry point the native stack can implement
Adds the seam a native MatrixRTC call stack plugs into, with nothing behind it
yet, so that the stack itself lands as a self-contained change.

`FeatureFlags.NativeCall` is off by default and unfinished.
`features/callnative/api` declares `NativeCallEntryPoint`, and
`features/callnative/impl` provides a default that logs and does nothing:
turning the flag on today gets no call and a line in the log. That is the
honest outcome, where falling back to the WebView would make the flag look
broken rather than unimplemented.

`DefaultElementCallEntryPoint` forks on the flag. The read is asynchronous
because the flag is storage-backed, and caching it in an eagerly started
StateFlow would race a call placed just after app start onto the default
value.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-29 18:44:49 +02:00
Valere 0059c72a32 Expose the write sides of the current-call and ringing state
`CurrentCallService` and `ActiveCallManager` are read by the whole app but
written only by whoever is actually running a call. Writing has so far been
reachable only from inside `features/call/impl`, because that is where both
implementations live.

Adds `CurrentCallTracker` and `RingingCallTracker` to `features/call/api`, so
that a call implementation in another module can report the call it is running
and answer a ring, without depending on `features/call/impl`.

No behaviour change: both interfaces are implemented by the existing classes,
and their methods are aliases of methods that already existed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-29 18:44:49 +02:00
Benoit Marty 0406189567 Update material3 to 1.5.0-alpha29
No new deprecation warnings compared to alpha28.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 18:43:21 +02:00
Benoit Marty 8427b49481 Suppress SearchBar deprecation warnings
The SearchBarState based replacement displays the expanded search bar in a dialog,
which would change the UX (in particular for the emoji picker bottom sheet).
Keep the current behavior for now and migrate later.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 18:43:19 +02:00
Benoit Marty f92b0a6c29 Migrate Slider to the SliderState API
material3 1.5.0-alpha28 removed the value-based Slider overload that accepts custom thumb and track.
Keep the public signature of our wrapper and remember a SliderState internally,
as the deprecated material3 overload does.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 18:43:18 +02:00
renovate[bot] b591fbe0cd Update dependency androidx.compose.material3:material3 to v1.5.0-alpha28 2026-09-29 18:43:16 +02:00
renovate[bot] 93b08e3508 Update dependency io.sentry:sentry-android to v8.57.0 (#7814)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-29 18:37:32 +02:00
Jorge Martin Espinosa ed80c5c537 Remove Firebase push integration from Posthog (#7810)
This causes issues with F-Droid, since now Posthog unconditionally references some Firebase classes, which are detected by their checks.

Note these don't do anything if Firebase messaging isn't included, which is already the case for the F-droid variant of the app.

The R8 changes should remove any code referencing Firebase in Posthog, making the offending classes unused and removing them from the final APKs.

This is not the ideal fix, that would be Posthog actually extracting these classes to an optional dependency, but it's a workaround to be able to publish F-droid versions.
2026-09-29 18:35:36 +02:00
Jorge Martín 6c9d501009 Merge branch 'hotfix/26.09.4' into develop 2026-09-29 18:31:14 +02:00
Benoit Marty fc63d95b65 Merge pull request #7717 from element-hq/renovate/zizmorcore-zizmor-action-0.x
Update zizmorcore/zizmor-action action to v0.6.4
2026-09-29 17:42:12 +02:00
ElementBot 46ba7729f4 Update screenshots 2026-09-29 17:32:24 +02:00
Benoit Marty 1664e0e15d Reorder states: new state at the end 2026-09-29 17:32:24 +02:00
ganfra 193a5b1dbc Merge branch 'develop' into feature/fga/timeline-multi-forward 2026-09-29 17:15:40 +02:00
Jorge Martín 8f07865953 Merge branch 'hotfix/26.09.4' 2026-09-29 16:39:58 +02:00
Jorge Martín 5b84bd4340 Adding fastlane file for version 26.09.4 2026-09-29 16:37:05 +02:00
Jorge Martín 7cbd60448d Setting version for the release 26.09.4 2026-09-29 16:36:06 +02:00
ganfra 69a1870f58 Use existing strings 2026-09-29 16:35:33 +02:00
Jorge Martín a30730462b Revert "Fix unread notification count in room list (#7749)"
This reverts commit b80a24e0aa.
2026-09-29 16:31:01 +02:00
Benoit Marty 1e6bb0a168 Fix Maestro tests 2026-09-29 15:57:08 +02:00
Benoit Marty 83c3b646c2 Make the content of LogoutView scrollable 2026-09-29 15:57:08 +02:00
Benoit Marty 26ea6be44c Logout: do not double render the progress state and disable the cancel button during the process. 2026-09-29 15:57:07 +02:00
Benoit Marty 672b6eaf81 Remove blank line 2026-09-29 15:57:06 +02:00
Benoit Marty e1ec0b3c55 Logout screen: add account info. 2026-09-29 15:57:06 +02:00
Benoit Marty 3de59bbd88 Logout screen: add Cancel button and update icon. 2026-09-29 15:57:05 +02:00
Benoit Marty d4c1bb61bb Always open the sign out screen from the settings
Remove the direct logout dialog from the settings and from the home screen, where it was never triggered.
The sign out screen will later display the account information (#7765).
The direct logout is still used in the FTUE session verification screen, so remove only the now unused `canDoDirectSignOut` field.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 15:57:04 +02:00
Benoit Marty 3fc5a56189 Merge pull request #7809 from element-hq/bma/upgradePaparazzi
Update paparazzi version to 2.0.0-alpha05.1
2026-09-29 14:25:34 +02:00
ElementBot 9eac3c8c2a Update screenshots 2026-09-29 09:15:08 +00:00
Benoit Marty d7e8111e6d Merge branch 'develop' into bma/upgradePaparazzi 2026-09-29 10:55:34 +02:00
renovate[bot] 39159a951a Update dependency com.google.testparameterinjector:test-parameter-injector to v1.23 2026-09-29 08:54:55 +00:00
ganfra 8dc052cc50 Merge pull request #7718 from element-hq/renovate/maplibre.compose
Update maplibre.compose to v0.16.0
2026-09-29 10:50:24 +02:00
ganfra 64aafc09ef Merge branch 'develop' into renovate/maplibre.compose 2026-09-29 10:13:48 +02:00
ElementBot 14ed697596 Update screenshots 2026-09-29 08:03:25 +00:00
Benoit Marty 8b8204167e Update paparazzi version to 2.0.0-alpha05.1 2026-09-29 09:50:07 +02:00
ElementBot 850048c182 Update screenshots 2026-09-28 16:01:44 +00:00
ganfra 397c19ecfa Merge branch 'develop' into renovate/maplibre.compose 2026-09-28 17:42:16 +02:00
renovate[bot] 4761c4a8d1 Update dependency com.github.matrix-org:matrix-analytics-events to v0.38.0 2026-09-28 15:37:36 +00:00
Jorge Martin Espinosa 03fd288c47 Upgrade to rustls-platform-verifier:0.2.0 (#7791)
* Upgrade to `rustls-platform-verifier:0.2.0`.

This re-enables certificate revocation checks, but v0.2.0 contains a list of CRL servers that can be accessed using clear text requests to check the validity of certain certificates, which should fix the issue that prevented us from using certificate revocation and at the same time strengthening the security of the client app.

Since we already had some network configuration in Element X, and `network_security_config.xml` files can't be merged, what we can do is manually download and merge the XML contents using `tools/dependencies/update_rustls_platform_verifier.py` like this:

```sh
./update_rustls_platform_verifier.py 0.2.0
```

It will automatically bump the Gradle dependency and merge the security config entries.

* Bump SDK bindings to `v26.09.28`, containing the rustls bump on the SDK side

* Fix unused resources caused by `app/src/main/res/xml/network_security_config_element.xml`
2026-09-28 15:35:33 +00:00
Jorge Martin Espinosa 0f98c5c0e0 Migrate SqliteStoreBuilder.passphrase usage to use highEntropyPassphrase (#7802)
* Migrate `SqliteStoreBuilder.passphrase` usage to use `highEntropyPassphrase`

This is the first step in migrating existing sessions from using a passphrase that needs key derivation, slowing down opening SDK DBs to just a raw key using the already random keys of the 'passphrase'

* Remove no longer necessary `keyOfSize` extension function
2026-09-28 17:32:42 +02:00
ganfra 8c7e76cc19 Merge pull request #7792 from element-hq/renovate/codecov-codecov-action-7.x
Update codecov/codecov-action action to v7.1.1
2026-09-28 15:50:41 +02:00
Jorge Martin Espinosa 0adc09db1e Tweak renovate config again to avoid rate-limits in Maven Central (#7794)
Reduce the max concurrent requests and max requests per second to 5. We bumped it to 10 before because it seemed to work well, but we're seeing rate-limit issues again.
2026-09-28 15:33:36 +02:00
Benoit Marty 8ad38ff27e Merge pull request #7763 from element-hq/gradlew-update-9.8.0
Update Gradle Wrapper from 9.7.1 to 9.8.0
2026-09-28 15:27:52 +02:00
Benoit Marty 1acc49d8de Merge pull request #7790 from element-hq/renovate/metro
Update metro to v1.4.4
2026-09-28 15:08:54 +02:00
Benoit Marty 74b9c85c7c Merge pull request #7760 from element-hq/feature/bma/removeHaze
Remove Haze.
2026-09-28 15:07:15 +02:00
Benoit Marty 00bfb97b92 Merge pull request #7784 from yncyrydybyl/fix/link-new-device-navigation
Fix "Link new device" opening the "Add an account" screen
2026-09-28 14:59:52 +02:00
Benoit Marty 6d717ff745 Merge pull request #7793 from element-hq/feature/bma/storeUserAvatar
Display the avatar of other accounts even if it cannot be loaded from the current account
2026-09-28 12:43:03 +02:00
Benoit Marty c2a2e16f46 Use the stored avatar thumbnail as a fallback for other accounts
Add an optional thumbnail to `MatrixUser` and `AvatarData`, filled from the session
database for the other accounts in the room list header, the settings and the account
selection screen. `ImageAvatar` renders this thumbnail instead of the initial letter while
the avatar is loading or when it fails to load, for instance when the homeserver of the
current account is not federated with the one of the other account.

Fixes #5571

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 11:38:45 +02:00
renovate[bot] 00b40815d6 Update codecov/codecov-action action to v7.1.1 2026-09-28 09:35:19 +00: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
Benoit Marty db23023eda Store the user avatar thumbnail in the session database
Add a `userAvatarData` column to the session database, containing the base64 encoded
thumbnail of the user avatar. It will allow rendering the avatar of an account without
having to use its Matrix client, for instance on the signed out screen, or for other
accounts when the homeservers are not federated.

The thumbnail is downloaded when the user profile is retrieved or updated, and only if
the avatar URL has changed. The profile is now also persisted when received from the
own profile subscription.

Part of #5571

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 11:12:47 +02:00
renovate[bot] 35d8d1197f Update coil to v3.6.3 (#7789)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-28 07:54:31 +00:00
renovate[bot] 80591449ab Update zizmorcore/zizmor-action action to v0.6.4 2026-09-28 07:30:15 +00:00
renovate[bot] 72644428ed Update metro to v1.4.4 2026-09-28 07:30:01 +00:00
renovate[bot] 2dcaffcdf6 Update kotlin 2026-09-28 07:29:34 +00:00
Yan Minagawa 83a01eb880 Send "Link new device" to the link-new-device flow again
PreferencesFlowNode routed navigateToLinkNewDevice() to
callback.navigateToAddAccount(), so tapping "Link new device" under
Account settings opened the "Add an account" screen instead — the
opposite direction, signing an extra account into this device rather
than authorising a new one.

The correct destination was already implemented and simply unreachable:
LoggedInFlowNode handles navigateToLinkNewDevice() by pushing
NavTarget.LinkNewDevice. Call that instead.

Regression from 9b2577f1dd ("Split settings: app settings / account
settings"); the previous code called callback.navigateToLinkNewDevice().
It went unnoticed because FeatureFlags.QrCodeLogin defaults to false,
which hides the entry point on default builds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017y1etzTCPzowrNcPyRBdjm
2026-09-26 16:48:46 +02:00
Jorge Martin Espinosa 4bf2a094c7 Fix some issues in the new timeline renderer (#7751)
* Don't show fading edge if the code block can't scroll. Use `canScrollForward || canScrollBackwards` for displaying the fading edges in the code blocks

* Don't add extra vertical padding to the mention pills. This should make them look like they did in the previous renderer

* Allow passing through touch events in `Modifier.linkTapHandler` if no url annotation is clicked or long clicked

`detectTapGestures` always consumes the touch events, no matter if they are handled or not, blocking these events from reaching parent components. Should fix the issue of `ParagraphView` blocking the long press action in the message bubbles

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
2026-09-26 13:02:56 +02:00
ganfra d8b6694575 Apply ktlint formatting 2026-09-25 18:50:51 +02:00
ganfra 971f196d54 Intercept clicks on every row in selection mode 2026-09-25 18:48:58 +02:00
ganfra 0a11e88f06 Align the selection indicator with the message bubble 2026-09-25 18:48:58 +02:00
ganfra f4399a392b Use a Checkbox for the timeline selection indicator 2026-09-25 18:47:48 +02:00
ganfra cec42ae4e6 Enter selection mode from the Forward action 2026-09-25 18:47:28 +02:00
ganfra a0bfdd379c Pass a list of event ids to the forward screen 2026-09-25 18:47:17 +02:00
ganfra 93831ffcbc Forward several events at once 2026-09-25 18:47:17 +02:00
Benoit Marty a42d91a050 Merge pull request #7697 from bxdxnn/misc/keystore
Allow changing keystore path
2026-09-25 16:45:46 +02:00
Jorge Martín fdb9951e12 Changelog for version 26.09.3 2026-09-25 16:38:53 +02:00
Jorge Martín c2c94547e8 Merge tag 'v26.09.3' into develop 2026-09-25 16:11:34 +02:00
Jorge Martín d2ca5629c8 Merge branch 'release/26.09.3' 2026-09-25 16:11:33 +02:00
Jorge Martín 6b62f0d78c Adding fastlane file for version 26.09.3 2026-09-25 16:11:32 +02:00
Jorge Martín bbb9750b3c Setting version for the release 26.09.3 2026-09-25 16:10:17 +02:00
Jorge Martin Espinosa 306e1cb950 Upgrade SDK bindings to v26.09.26 (#7780)
* Bump SDK bindings to `v26.09.26`

This version contains a fix for issues in `v26.09.25` related to certificate verification.

* Fix breaking API changes in the latest bindings

* Unify `FeatureFlags.RoomThreadList` with `FeatureFlags.Threads`
2026-09-25 16:08:19 +02:00
Benoit Marty 5c871c0fc2 Merge pull request #7750 from element-hq/bma/settingsIterationStep2Bis
Settings iteration: split account settings and application settings.
2026-09-25 10:25:06 +02:00
ElementBot 05373c2988 Update screenshots 2026-09-25 07:45:49 +00:00
Benoit Marty 7e5866a2b7 Merge branch 'develop' into bma/settingsIterationStep2Bis 2026-09-25 09:36:00 +02:00
gradle-update-robot ee87ef31b3 Update Gradle Wrapper from 9.7.1 to 9.8.0
Signed-off-by: gradle-update-robot <gradle-update-robot@regolo.cc>
2026-09-25 00:45:46 +00:00
Benoit Marty b643e7b8b6 Merge pull request #7762 from element-hq/bma/masDeleteAllSessions
Add a script to end all the sessions of a Matrix account using MAS
2026-09-24 21:54:57 +02:00
Jorge Martin Espinosa b80a24e0aa Fix unread notification count in room list (#7749)
* Display the unread notification count only when needed

* Adjust `CounterAtom` and `UnreadIndicatorAtom` to use the official M3's `Badge` component, with a few tweaks to ensure single digit items are always displayed as a circle

* +99 is now 99+

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
2026-09-24 18:01:12 +02:00
Benoit Marty 6cb4343081 Add a script to end all the sessions of a Matrix account using MAS
The script logs in to MAS with the web login form (username and password),
then uses the MAS GraphQL API to end all the active compat, OAuth 2.0 and
browser sessions of the account. MAS pagination of browser sessions does not
always progress, so sessions are fetched and ended in batches until none are
left.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-24 17:13:00 +02:00
Benoit Marty c3f5a1884f Update Maestro tests 2026-09-24 17:12:17 +02:00
Benoit Marty 70058e0581 Run ./tools/manifest/aaptDump.sh 2026-09-24 15:02:00 +02:00
Benoit Marty 30e518d0a5 Remove Haze, we are not using it now that we have the floating toolbar. 2026-09-24 10:51:12 +02:00
Benoit Marty e5c078c01a Merge pull request #7756 from element-hq/renovate/media3
Update media3 to v1.11.1
2026-09-24 10:10:32 +02:00
Benoit Marty 72d828f2ec Merge pull request #7757 from element-hq/feature/bma/removeMessageUpdate
Update remove message confirmation dialog.
2026-09-24 09:54:33 +02:00
ElementBot 478138730c Update screenshots 2026-09-23 14:47:35 +00:00
Benoit Marty 01681e9fff Move the "Blocked users" item to the Moderation and safety screen.
The item is removed from the account section of the root settings screen and
added at the bottom of the Moderation and safety screen. As before, it is only
shown when at least one user is blocked, with the number of blocked users.

ModerationAndSafetyPresenter now computes the number of blocked users, and
ModerationAndSafetyNode exposes a Callback to navigate to the blocked users
screen. Tests are moved accordingly.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 16:17:26 +02:00
Benoit Marty c612c80b1d Merge account settings into the root preference view. 2026-09-23 16:03:15 +02:00
ElementBot cc70885552 Update screenshots 2026-09-23 12:43:52 +00:00
Jorge Martin Espinosa b6cf293e2a Add headers (h1-h6) tag support for the new timeline renderer (#7754)
* Add headers (`h1-h6`) tag support for the new timeline renderer

* Make H1 and H2 use the same (smaller) style

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
2026-09-23 12:35:05 +00:00
Benoit Marty 3cafaaec94 Update remove message confirmation dialog.
Closes #7753
2026-09-23 14:25:37 +02:00
renovate[bot] fb84c5b3c0 Update dependencyAnalysis to v3.19.2 (#7755)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-23 13:02:58 +02:00
Jorge Martin Espinosa 4fa98752d4 Add a script to install and launch an APK given a PR URL (#7752)
* Add a script to install and launch an APK given a PR URL

This is easier than manually downloading, then installing the APK or scanning the QR code using the device

* Allow fetching the APK using the PR number too
2026-09-23 10:42:57 +00: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
renovate[bot] 2ab3c7ec53 Update media3 to v1.11.1 2026-09-23 10:28:33 +00:00
Benoit Marty f5ed93b474 Merge pull request #7729 from element-hq/renovate/androidx.sqlite-sqlite-ktx-2.x
Update dependency androidx.sqlite:sqlite-ktx to v2.7.1
2026-09-23 12:25:23 +02:00
Benoit Marty 6642383d03 Merge pull request #7730 from element-hq/renovate/compose.bom
Update dependency androidx.compose:compose-bom to v2026.09.00
2026-09-23 12:22:55 +02:00
Benoit Marty 2669ac4eae Change to "produceState" 2026-09-23 11:55:03 +02:00
Benoit Marty c8748f4d77 nbOfBlockedUsers -> numberOfBlockedUsers 2026-09-23 11:51:54 +02:00
Benoit Marty e0defcea66 Fix Maestro tests. Disable searchRoomList.yaml as it's always failing here. 2026-09-23 10:15:40 +02:00
Jorge Martin Espinosa 8b1b5560d9 Add recent search results to global search (#7742)
* Add global search results history: keep the latest 25 items used in the global search, save them to an encrypted file store.

* Integrate the search history in the global search feature

* Add `MatrixClient.getRoomInfo` and use it in `GlobalSearchPresenter`

---------

Co-authored-by: ElementBot <android@element.io>
2026-09-22 15:17:57 +00:00
Benoit Marty ac54596ca3 Update Maestro tests 2026-09-22 16:43:58 +02:00
Jorge Martin Espinosa 51fe66cd88 Add linkification to the new timeline renderer (#7731)
* Add linkification to the new timeline renderer

Reuse `AnnotatedString.linkify` for now, even though it creates `Spannable` instances for that: it's the closest one to what we already had.

Also, make sure we use the same URL annotation identifier in the renderer and the linkificator.

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
2026-09-22 15:29:20 +02:00
Benoit Marty e688b174fa Merge branch 'develop' into bma/settingsIterationStep2Bis 2026-09-22 14:47:40 +02:00
Benoit Marty 9b3d4c7c27 Merge pull request #7736 from element-hq/sync-localazy
Sync Strings
2026-09-22 14:45:58 +02:00
Benoit Marty d29368cdf1 Add missing ElementPreview wrapper 2026-09-22 14:12:03 +02:00
ElementBot 80644b860e Update screenshots 2026-09-22 11:52:36 +00:00
Benoit Marty 872bf5a70f Let Claude update the tests. 2026-09-22 13:17:35 +02:00
Benoit Marty 9b2577f1dd Split settings: app settings / account settings 2026-09-22 13:17:16 +02:00
Jorge Martin Espinosa bec3780c9c Fix forwarding an event not working in threads (#7743)
* Fix forwarding an event not working in threads

Add the `TimelineProvider` for the event to the navigation flow so the events can be fetched from the right timeline.
2026-09-22 10:59:12 +02:00
Jorge Martin Espinosa 4e47c6170e Integrate the new timeline renderer behind a feature flag (#7722)
* Integrate the new renderer with the timeline UI

* Composable functions are now in `:libraries:htmlrenderer:api` so we don't need to add `impl` to other modules.

* Improve inline code span

* Return both the text and URL in `onLinkClick` and `onLinkLongClick` so we can check the URL is not misleading

* Remove `SelectionContainer` for now - it creates issues when long pressing

* Make `LocalComposeTimelineEventRenderer` contain a `ComposeLocalTimelineEventRendererData` instance. This checks if the new rendering is enabled and what is the current user id so it can be used for styling mention pills

* Add fading edges to code block to indicate scrolling is possible

* Long pressing in a code block copies its contents to the clipboard

* Fix alignment of ordered list item prefixes

* Don't use default values in State data classes

---------

Co-authored-by: ElementBot <android@element.io>
2026-09-22 10:26:35 +02:00
ganfra 789e02fe0b Update MapBottomSheetScaffold KDoc 2026-09-21 16:18:25 +02:00
ElementBot 3e7253cdb7 Update screenshots 2026-09-21 14:09:39 +00:00
Benoit Marty 0ea76ecebf Merge pull request #7741 from element-hq/bma/settingsIterationStep1
Settings iteration: move user status and update multi-account section
2026-09-21 15:54:11 +02:00
ElementBot d74263a7a1 Update screenshots 2026-09-21 13:12:36 +00:00
ElementBot dceb57c9fc Update screenshots 2026-09-21 12:37:28 +00:00
Jorge Martin Espinosa 84a907d906 Fix regex for invalid placeholders (#7738)
This issue added some false positives to the list and broke the string synchronization.
2026-09-21 14:35:33 +02:00
Jorge Martin Espinosa 3c3e8f8f6f Start sync manually after completing the auth flow (#7734)
Do it before we wait for the known verification state so we can receive updates from the encryption sliding sync flow.
2026-09-21 14:34:34 +02:00
Benoit Marty da2b2e99d6 Fix chevron rotation issue. 2026-09-21 14:19:54 +02:00
Jorge Martin Espinosa c4529b87b4 Remove Roborazzi, use Paparazzi for Compound (#7735)
* Remove Roborazzi, use Paparazzi for Compound

The new screenshots will be recorded with the rest of the Paparazzi ones. Note the names changed, since Paparazzi enforces a `$packagename.$testname.$name.$extension` format

* Set a max width value for color previews

* Merge the `verifyPaparazziDebug` CI steps

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
2026-09-21 12:27:56 +02:00
ElementBot 178c80a1a1 Update screenshots 2026-09-21 09:00:46 +00:00
Benoit Marty cc3b788b3e Animate user status state change 2026-09-21 10:41:03 +02:00
Benoit Marty 95d734efc9 Merge pull request #7705 from element-hq/bma/fixLeak
Close the threaded timelines created to create a poll, share a location or send media
2026-09-21 10:18:37 +02:00
bmarty d1e0544102 Sync Strings from Localazy 2026-09-21 00:54:07 +00:00
ganfra 4cdb72d2fa Adapt location feature to maplibre-compose 0.17.0 API 2026-09-18 18:08:20 +02:00
Benoit Marty ae36cc3b1e Move user status next to the current account header.
Also make the other account section collapsable
2026-09-18 17:48:19 +02:00
ganfra f8fdfcae56 Merge pull request #7699 from element-hq/sync-localazy
Sync Strings
2026-09-18 16:54:21 +02:00
ElementBot 1379d79171 Update screenshots 2026-09-18 08:56:36 +00:00
ganfra b54937793f Merge branch 'develop' into sync-localazy 2026-09-18 10:39:43 +02:00
bxdxnn 5cac4767f1 Add smooth animation to voice messages play progress (#7698)
* 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.
2026-09-18 09:39:15 +02:00
Jorge Martin Espinosa c0bb68c872 New timeline event renderer: add new modules (#7721)
* Added HTML parser to abstract tree nodes

* Add HTML renderer using `HtmlMessageContent`

* Add custom rendering for inline code nodes besides the monospace font

* Support `<s>` and `<strike>` too

* Add `ContentAvoidingLayoutData` support

* Improve the last block node behaviour and its integration with `ContentAvoidingLayout`

* Move `ContentAvoidingLayout` to `:libraries:ui-common` so it can be shared

* Add missing `:libraries:htmlrenderer:impl` entry in `DependencyHandlerScope.allLibrariesImpl`

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
2026-09-17 19:08:32 +02:00
ElementBot 0e4533045d Update screenshots 2026-09-17 15:13:36 +00:00
Johannes Marbach 97d9707d6a Make NotificationResultProcessor.emit wait until the batch is processed (#7640)
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-09-17 16:13:06 +02:00
renovate[bot] 9396349b39 Update dependency androidx.compose:compose-bom to v2026.09.00 2026-09-17 13:45:51 +00:00
Benoit Marty 82c841a6b0 Merge pull request #7728 from element-hq/renovate/actions-setup-java-6.x
Update actions/setup-java action to v6.0.1
2026-09-17 15:40:26 +02:00
renovate[bot] 47b8261fb2 Update dependency androidx.sqlite:sqlite-ktx to v2.7.1 2026-09-17 12:54:22 +00:00
renovate[bot] ca753ee600 Update actions/setup-java action to v6.0.1 2026-09-17 12:54:12 +00:00
ganfra 47620e905f Merge pull request #7711 from element-hq/renovate/metro
Update metro to v1.4.3
2026-09-17 14:51:47 +02:00
ganfra 4a141c5702 Merge pull request #7716 from element-hq/renovate/net.zetetic-sqlcipher-android-4.x
Update dependency net.zetetic:sqlcipher-android to v4.19.0
2026-09-17 14:51:19 +02:00
ganfra 51c06b4bec Timeline : initial multi-select state 2026-09-17 14:49:03 +02:00
Benoit Marty 58a00fd4e5 Merge pull request #7679 from element-hq/feature/bma/messageDeletionConfirmation
Let the user give an optional reason when removing a message
2026-09-16 16:37:51 +02:00
Jorge Martin Espinosa c3f128b6f0 Use patched Konsist version (forked, 0.18.0) (#7719)
* Use a patched fork of Konsist

The patch contains code to ignore hidden folders in the root project: by default, Konsist scans *every single file* in the root directory except for a few exceptions, and hidden folders were not a part of that.

That means `.git`, `.github`, etc. would be scanned to for no good reason and even worse,  hidden folders used for worktrees would get scanned too, so Konsist would end up scanning the same project with small variations several times and OOMing / taking ages to finish.

* Make `Immutable/Stable` annotation check more performant. Also, fix case that was previously not found
2026-09-16 11:27:44 +02:00
Benoit Marty ba2e2a02d1 Merge branch 'develop' into bma/fixLeak 2026-09-16 09:42:36 +02:00
renovate[bot] 5a791b3bab Update maplibre.compose to v0.16.0 2026-09-16 07:23:00 +00:00
renovate[bot] 35af1fca48 Update dependency io.element.android:element-call-embedded to v0.26.0 (#7715)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-16 08:38:38 +02:00
renovate[bot] 344b67f8e8 Update dependency net.zetetic:sqlcipher-android to v4.19.0 2026-09-15 21:18:31 +00:00
bxdxnn b772aebf49 Address review 2026-09-15 14:45:26 +00:00
bxdxnn 903d31bbab Update docs/nightly_build.md
Co-authored-by: Benoit Marty <benoit.marty@gmail.com>
2026-09-15 17:26:18 +03:00
bxdxnn 9f3905e830 Apply suggestion from @bmarty
Co-authored-by: Benoit Marty <benoit.marty@gmail.com>
2026-09-15 16:49:29 +03:00
Benoit Marty 3e861cc646 Do not report detekt LargeClass issues on test classes (#7713)
detekt's default configuration excludes test source sets from
`TooManyFunctions` and `StringLiteralDuplication`, but not from
`LargeClass`, so large test classes were reported and had to carry a
`@Suppress("LargeClass")` annotation.

Add the same kind of exclusion to the `LargeClass` rule in
`tools/detekt/detekt.yml` and remove the 24 now-redundant suppressions
from test files. The two remaining suppressions are in production code,
where the rule still applies.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 13:56:33 +02:00
renovate[bot] 08eabd3ed5 Update metro to v1.4.3 2026-09-15 09:36:56 +00:00
Benoit Marty a4286a932a Merge pull request #7712 from element-hq/renovate/roborazzi
Update roborazzi to v1.74.0
2026-09-15 11:33:56 +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 bcf136025a Merge pull request #7703 from element-hq/bma/accountProviderIteration
Iteration on Acount provider
2026-09-15 09:58:16 +02:00
Benoit Marty 98c19de6e3 Merge pull request #7707 from element-hq/bma/gitFlowToGit
Replace git flow with regular git commands in the release script V2
2026-09-15 09:20:34 +02:00
Benoit Marty fd315318a0 Update ref to main branch. 2026-09-15 09:15:38 +02:00
Benoit Marty c92bf4bc43 Sealed interface must be immutable when uses in Composable function parameters. 2026-09-15 09:11:40 +02:00
renovate[bot] dc466f1f5c Update roborazzi to v1.74.0 2026-09-15 05:09:14 +00:00
Benoit Marty 5b0d7bf5a7 Log errors and fix quality issues 2026-09-14 18:30:29 +02:00
ganfra ba80abcbf0 Merge pull request #7671 from element-hq/renovate/org.sonarqube-7.x
Update plugin sonarqube to v7.5.0.8588
2026-09-14 18:16:06 +02:00
Jorge Martin Espinosa 7aa43411e0 Reduce parallel JVMs spawned for tests (#7701)
* Set `maxParallelForks = 1` for most tests, except for screenshot ones

It turns out the Robolectric setup penalty compounds the more parallel JVMs you spawn. Using 1 JVM vs 8 was up to 2.5x faster in local tests.

* Split the roborazzi tests tasks config and CI step
2026-09-14 18:14:46 +02:00
Benoit Marty f97dac2d5b Update ref
Point to the enterprise submodule rebased on main, which brings the X.509
feature, and resolves the MdmData conflict between the new UseX509 entry and
the split of AccountProvider into AccountProviderServerName,
AccountProviderManagedBaseUrl and AccountProviderManagedCanUseServerName.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-14 18:09:52 +02:00
renovate[bot] 600a989854 Update kotlinpoet to v2.4.0 (#7709)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-14 16:09:23 +00:00
ganfra 99237f0146 Merge branch 'develop' into renovate/org.sonarqube-7.x 2026-09-14 17:30:21 +02:00
Benoit Marty 84b6224e4d Update ref 2026-09-14 17:18:18 +02:00
Benoit Marty f9a493ef0c Add Kdoc on data class AccountProvider 2026-09-14 16:55:25 +02:00
Benoit Marty ed250320b3 Close the threaded timelines created by ShareLocationPresenter and DefaultMediaSender
Both created a dedicated timeline for each operation performed in a thread, and
never closed it, leaking SDK resources. Apply the same treatment as PollRepository:
replace getTimeline() with a scoped withTimeline() helper, which closes the created
timeline once the operation completes, whether it succeeds, fails or gets cancelled.

In DefaultMediaSender.sendGallery(), waiting for the upload handler is now done
inside the block, so that the timeline stays open until the upload completes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-14 15:21:14 +02:00
Jorge Martin Espinosa 3d198d8065 Fix flaky test in TimelinePresenterTest (#7702)
The problem seemed to be sometimes the background work to switch from live timeline to a focused event timeline wasn't finished in time and an assertion failed from time to time.

By providing the coroutine to run this on we can pass a `TestScope` and force it to reliably run immediately instead in unit tests.
2026-09-14 15:19:14 +02:00
Benoit Marty e2fb2962f6 Close the threaded timeline created by PollRepository
PollRepository created a dedicated timeline for each operation performed in a
thread, and never closed it, leaking SDK resources. Replace getTimelineProvider()
with a scoped withTimeline() helper, so that the created timeline is closed once
the operation completes, whether it succeeds, fails or gets cancelled.

The live timeline is left untouched, since it is owned by its room.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-14 15:17:24 +02:00
ElementBot aa69321898 Update screenshots 2026-09-14 12:36:16 +00:00
Benoit Marty bf70bb3e50 Update ref 2026-09-14 11:30:23 +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
bxdxnn 7191313ce5 Allow changing keystore path 2026-09-12 18:10:53 +00:00
Jorge Martin Espinosa 8cd3750cde Make global search use a Joined filter for room results (#7695)
Before this, global search returned also invites, which wasn't planned.
2026-09-11 17:36:38 +02:00
ElementBot b28b2ceeed Update screenshots 2026-09-11 15:04:02 +00:00
Benoit Marty 5b09fdf13f Replace git flow with regular git commands in the release script V2
The script was relying on the external git-flow tool and on a local
`git config gitflow.*` initialisation, which made its behaviour depend on
the machine setup and hid what was actually done to the branches.

The release branch is now created with `git checkout -b`, and the release
is finished with explicit merge, tag, back-merge and branch deletion
commands. The merge and tag messages are provided explicitly so that the
generated history stays identical to the previous releases. Each step now
aborts the script with a clear message when it fails, instead of letting
the script continue.

Note that tools/release/release.sh (V1) still uses git flow.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-11 16:57:01 +02:00
Benoit Marty e7372c7f49 Use PreviewParameterProvider 2026-09-11 16:45:21 +02:00
renovate[bot] d2c49bf4af Update coil to v3.6.2 (#7693)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-11 13:53:11 +02:00
Benoit Marty 58b0f2ebc3 Merge pull request #7591 from element-hq/bma/iterateOnClientCreation
Iterate on client creation
2026-09-11 13:22:42 +02:00
ElementBot c643acda38 Update screenshots 2026-09-11 11:08:34 +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
Benoit Marty cbfef6a321 Update ref 2026-09-11 12:35:45 +02:00
renovate[bot] a5b95a18b2 Update dependency io.sentry:sentry-android to v8.55.0 (#7685)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-11 10:19:03 +02:00
ganfra 26578c79a9 Changelog for version 26.09.2 2026-09-10 22:24:50 +02:00
ganfra 518919c28b Merge tag 'v26.09.2' into develop
Release 26.09.2
2026-09-10 21:54:38 +02:00
ganfra 477c813997 Merge branch 'release/26.09.2' 2026-09-10 21:52:00 +02:00
ganfra 3e08454ff8 Adding fastlane file for version 26.09.2 2026-09-10 21:51:59 +02:00
ganfra 24a257bd38 Setting version for the release 26.09.2 2026-09-10 21:51:49 +02:00
ganfra 8101abf67d Merge pull request #7683 from element-hq/renovate/org.matrix.rustcomponents-sdk-android-26.x
Update dependency org.matrix.rustcomponents:sdk-android to v26.09.9
2026-09-10 11:48:42 +02:00
renovate[bot] 45ad16d17d Update dependency org.matrix.rustcomponents:sdk-android to v26.09.9 2026-09-10 05:10:32 +00:00
Jorge Martín c623105da6 Make the google-repo-only dependencies on renovate match those in gradle 2026-09-09 18:40:16 +02:00
Jorge Martín 5f499ce4b3 Ensure official Android dependencies are checked only against Google's Android repo 2026-09-09 18:40:16 +02:00
Jorge Martín 0b7d089791 Make Renovate only look for Element dependencies in maven central 2026-09-09 18:40:16 +02:00
Jorge Martín 10776a7e6b Make sure Android dependencies are fetched from Google's repo in Gradle sync 2026-09-09 18:40:16 +02:00
Jorge Martín 666081e2f2 Adjust concurrent requests for renovate 2026-09-09 18:40:16 +02:00
ElementBot 5df0dca56b Update screenshots 2026-09-09 15:26:04 +00:00
Benoit Marty 069fb2a03d Merge pull request #7678 from element-hq/feature/bma/removeShowkase
Remove Showkase and its in-app component browser
2026-09-09 16:59:30 +02:00
Benoit Marty a7ff96c1c3 Update wording of the remove message confirmation dialog.
And get strings from Localazy
2026-09-09 16:26:51 +02:00
Benoit Marty 50ec5f7dec Merge pull request #7538 from hayaksi1/fix/1612-redaction-reason
Let the user give a reason when removing a message
2026-09-09 16:11:33 +02:00
Benoit Marty 1ee156f480 Remove Showkase from the project. 2026-09-09 15:56:25 +02:00
Benoit Marty 8e1a34cff8 Merge pull request #7649 from element-hq/feature/bma/replaceDangerByGhAction
Replace Danger with actions/github-script
2026-09-09 11:30:13 +02:00
renovate[bot] 476e863734 Update plugin sonarqube to v7.5.0.8588 2026-09-09 07:53:31 +00:00
Jorge Martin Espinosa 3106fec05f Try improving renovate issues with Maven Central rate limiting (#7667)
* Try improving renovate issues with Maven Central rate limiting

Try not to make too many concurrent requests to Maven Central

The logs display rate limit responses followed by OK responses, which could mean we're hammering the Maven Central repo with too many concurrent requests

* Try not hitting Maven Central for `androidx` packages
2026-09-09 09:49:24 +02:00
Benoit Marty 3cde1523b4 Fix quality and tests. 2026-09-08 17:47:53 +02:00
ganfra 875ff9825b Merge pull request #7663 from element-hq/feature/fga/maplibre-compose-update
Update maplibre compose to 0.15
2026-09-08 17:15:32 +02:00
Benoit Marty a817b6f219 Rename methods and update documentation. 2026-09-08 17:06:53 +02:00
Benoit Marty 59e72fc3ee Stop deleting the session data of a logged in account on the next login
sessionPaths keeps pointing at the directories of the account which has
just been logged in, and it was never reset. Adding another account
calls setHomeserver(), whose rotateSessionPath() starts by deleting the
directories of the previous sessionPaths, so the file and cache
directories of the first account were wiped.

finalizeClientCreation() now forgets the session paths once the login
succeeded. It is done there rather than in clear() on purpose: after a
failed attempt the directories are orphaned and must still be deleted by
the rotateSessionPath() of the next attempt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 16:47:58 +02:00
Benoit Marty 579ec9bd6a Close the client used to log in before creating the client of the session
Since the login flows stopped handing their client over to
RustMatrixClientFactory and let finalizeClientCreation() build a fresh
one from the SessionData, two problems appeared.

The client built by loginWithQrCode() was never owned by anyone: it is
not assigned to currentClient, so clear() had nothing to close and the
Rust Client stayed open for the whole app lifetime, holding the session
stores of the freshly logged in account. It is now tracked in
currentClient, and also disposed of when the QR login fails, which is
safe because that flow always builds its own client.

The client of the session was also built while the login client was
still open. Both use the same session paths, so their state and crypto
SQLite stores, and the search index when message search is enabled, were
opened twice at the same time with no cross process lock to protect
them. clear() now runs before the final client is created.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 16:47:58 +02:00
Benoit Marty 1f18a18917 Iterate on hooks implementation 2026-09-08 16:47:57 +02:00
Benoit Marty 5ce531fd44 Ensure we create a fresh Client when the sessionId is know after authentication. 2026-09-08 16:46:46 +02:00
renovate[bot] 3df1906d2a Update dependency org.matrix.rustcomponents:sdk-android to v26.09.08 (#7668)
* Update dependency org.matrix.rustcomponents:sdk-android to v26.09.08

* Fix API breaks:

- There's no `SyncServiceBuilder.withProfilesExtension()` method anymore. This is now enabled unconditionally in the SDK.
- Timeline filters no longer use the `org.matrix.rustcomponents.sdk.TimelineEventFilter` FFI wrapper, but the `uniffi.matrix_sdk_ui.TimelineEventFilter` type directly. This also changes the underlying types used for inclusion/exclusion.

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jorge Martín <jorgem@element.io>
2026-09-08 16:22:14 +02:00
Jorge Martin Espinosa 039c0df9ed Fix remote config getting refreshed with Origin.Cache (#7666) 2026-09-08 13:50:01 +02:00
ganfra 4201ece647 Update gplay release manifest dump after location changes 2026-09-08 12:46:38 +02:00
ganfra 2c9c24921a Merge pull request #7664 from element-hq/renovate/coil
Update coil to v3.6.1
2026-09-08 11:43:13 +02:00
ganfra 9a4beb4278 Fix stability on UserLocationState 2026-09-08 10:15:03 +02:00
renovate[bot] 55d0503da8 Update coil to v3.6.1 2026-09-08 05:26:36 +00:00
ganfra 6c01ad1364 Fix formating 2026-09-07 19:04:13 +02:00
ganfra be666176a7 Merge pull request #7658 from element-hq/renovate/zizmorcore-zizmor-action-0.x
Update zizmorcore/zizmor-action action to v0.6.3
2026-09-07 19:03:36 +02:00
ganfra 7773ead79b Prevent location bottomsheet from being hidden 2026-09-07 18:56:25 +02:00
ganfra f72e1ad2cc Support Google Play Services fused location backend 2026-09-07 18:11:38 +02:00
ganfra 3754258d88 Adapt location feature to updated maplibre-compose API 2026-09-07 15:25:39 +02:00
ganfra 7045465ffe Allow BSD-2 clause 2026-09-07 15:25:39 +02:00
ganfra 7507475afd Use vulkan runtime 2026-09-07 15:25:39 +02:00
ganfra 57d1948784 Bump maplibre compose and remove unnecessary dependencies 2026-09-07 15:25:39 +02:00
Benoit Marty 10eb7ceb60 Merge pull request #7655 from andreBurnt/fix/maestro-skip-fork-prs
Maestro (local): skip the lane on pull requests from forks
2026-09-07 14:26:52 +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
Jorge Martin Espinosa f0a0700f5d Bump SDK bindings to v26.09.3 (#7637)
Fix API breaks:

- A new `Mentions` room list filter is available.
- `RoomListService.subscribeToRooms` is now `RoomListService.setRoomSubscriptions`.
- `enableAutomaticBackPagination` moved from `Client` to `ClientBuilder`.
2026-09-07 14:19:19 +02:00
Jorge Martin Espinosa 79f4e2e06f Stop clearing the remote config when clearing the cache (#7662) 2026-09-07 13:05:15 +02:00
Benoit Marty 16f51e717c Merge pull request #7656 from andreBurnt/fix/maestro-concurrency-queue-max
Maestro (local): queue pending runs in the maestro-test concurrency group instead of cancelling them
2026-09-07 12:27:41 +02:00
renovate[bot] 3a8b096e45 Update dependency org.jetbrains.kotlinx:kotlinx-collections-immutable to v0.5.2 (#7650)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-07 09:49:28 +02:00
renovate[bot] 35e0f83722 Update zizmorcore/zizmor-action action to v0.6.3 2026-09-07 02:25:04 +00:00
andreBurnt 11016da8ae Maestro (local): skip the lane on pull requests from forks
Secrets are never exposed to pull_request runs from forks, so checkEnv.js
throws on MAESTRO_PASSWORD after a full APK build and emulator boot, and the
red check is indistinguishable from a real test failure. The existing guard
checked github.event.pull_request.fork, a field that does not exist on the
pull_request payload (it lives at head.repo.fork), so it was always true.

Gate both jobs at job level on head.repo.full_name == github.repository
(workflow_dispatch still runs), and drop the dead step-level condition.

Fixes #7653

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 07:28:33 -07:00
andreBurnt 443112706a Maestro (local): queue pending runs in the maestro-test concurrency group
With the default (queue: single), a run that finishes Build APK while
another run is already pending in the maestro-test group cancels that
pending run, which then never gets a Maestro verdict for its commit.
queue: max keeps the one-at-a-time constraint (no cancel-in-progress) and
lets up to 100 runs wait, at no runner cost.

Fixes #7654

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 07:28:33 -07:00
Benoit Marty 66c502853c Merge pull request #7647 from element-hq/feature/bma/cleaupProguardFiles
Clean up ProGuard keep rules files
2026-09-04 17:37:53 +02:00
ganfra 356f7eae9f Merge pull request #7636 from element-hq/renovate/io.sentry-sentry-android-8.x
Update dependency io.sentry:sentry-android to v8.54.0
2026-09-04 17:08:06 +02:00
Benoit Marty d0d30dc6e2 Replace Danger with actions/github-script
The PR metadata checks move to a new PR Checks workflow, and the lint,
ktlint and detekt reports are now surfaced as inline annotations from the
quality workflow. This removes the danger-js dependency, its ad-hoc npm
installs and the need for the ElementBot token, so the checks also run on
pull requests from forks.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 15:54:02 +02:00
Benoit Marty 0e12d4e119 Revert "Say so when this session has no secrets to verify another one with" (#7644) 2026-09-04 14:39:50 +02:00
Benoit Marty 5c0c330bbd Import string from Localazy. (#7645) 2026-09-04 14:38:37 +02:00
Benoit Marty 6f3758099b Rename the Firebase consumer keep rules file
The -dontwarn is still required: firebase-messaging ships no consumer rules and
we exclude firebase-analytics from its dependencies. Only rename the file to
consumer-rules.keep and reword the comment, which embedded a local path.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 14:05:08 +02:00
Benoit Marty 230279566e Keep the Showkase codegen constructor in the designsystem module
The rule only kept the class, but ShowkaseBrowserActivity instantiates it with
Class.forName(...).getDeclaredConstructor().newInstance(), so R8 was free to
strip the constructor. Also drop the AGP template comments and rename the file
to .keep, for consistency with app/proguard.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 14:05:01 +02:00
Benoit Marty fc519c7ddf Remove obsolete Retrofit keep rules from the network module
The three rules came from a 2022 comment on Retrofit issue #3751. Retrofit
3.0.0 now ships META-INF/proguard/retrofit2.pro, which contains two of them
verbatim and covers retrofit2.Call through its conditional return-type rule.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 14:04:54 +02:00
Jorge Martin Espinosa 7797d302c3 Bump PR priority for our dependencies in Renovate (#7642)
This is now 7, which is higher than the max value (6) renovate adds by default, so they are *always* prioritised above the rest, even if renovate gets rate-limited.
2026-09-04 12:18:32 +02:00
Benoit Marty 55bfcac6ff Remove empty (or comment only) proguard files. 2026-09-04 11:51:59 +02:00
renovate[bot] 29955bcd34 Update coil to v3.6.0 (#7630)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-04 11:05:23 +02:00
Johannes Marbach 8a32f5a001 Keep the push foreground service alive while notifications are fetched (#7639)
* Keep the push foreground service alive while notifications are fetched

* Release the push foreground service as soon as results are handed over

---------

Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-09-04 10:56:41 +02:00
renovate[bot] 39b392f10c Update dependency io.sentry:sentry-android to v8.54.0 2026-09-03 14:06:44 +00:00
renovate[bot] 67f287af39 Update camera to v1.6.2 (#7632)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-03 10:27:21 +02:00
Jorge Martin Espinosa dcd0bc782f Make sure enforce_element_pro is taken into account in FOSS (#7631)
* Make sure `enforce_element_pro` is taken into account in FOSS

* Make sure we crate `RustTemporaryMatrixClient` instances with a proper `serverName` value
2026-09-03 10:05:40 +02:00
Jorge Martin Espinosa 3b2e416855 Fix fetched remote config being cached with the wrong key (#7626)
* Fix fetched remote config being cached with the wrong key

* Bump enterprise repo
2026-09-03 09:57:52 +02:00
renovate[bot] da44cc4075 Update danger/danger-js action to v14 (#7602)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-03 09:27:54 +02:00
renovate[bot] dd8877e6cd Update actions/setup-java action to v6 (#7614)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-03 09:27:24 +02:00
renovate[bot] b4473b18b0 Update android.gradle.plugin to v9.3.2 (#7603)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-03 09:26:41 +02:00
Benoit Marty a758447175 Merge pull request #7627 from element-hq/renovate/wysiwyg
Update wysiwyg to v2.42.1
2026-09-03 09:12:47 +02:00
hayaksi1 dbf18e688d Merge branch 'develop' into fix/1612-redaction-reason 2026-09-03 03:02:35 +03:00
hayyaksi d2f540ae79 Expose the timeline item long press and swipe to reply as TalkBack actions (#7458)
* Expose the timeline item gestures as TalkBack actions

Long pressing a timeline item opens the action list, and swiping it
starts a reply. Neither gesture is reachable with a screen reader
enabled, so those two actions were unavailable to TalkBack users.

Publish them as accessibility custom actions on the event row, reusing
the existing "Message actions" and "Reply" strings. The reply action is
only offered when the user is allowed to reply, matching the swipe.

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
2026-09-02 16:58:26 +02:00
Benoit Marty 94bfcc41a6 Merge pull request #7628 from element-hq/sync-localazy
Let's merge before we get a conflict.
2026-09-02 15:03:21 +02:00
Benoit Marty 5018bf8247 Merge pull request #7629 from element-hq/ci/add-gradle-opts-to-sync-localazy-flow
Add `GRADLE_OPTS` for `sync-localazy.yml` flow
2026-09-02 14:58:38 +02:00
Benoit Marty 992da95e4c Merge pull request #7551 from hayaksi1/fix/5344-verification-unavailable
Say so when this session has no secrets to verify another one with
2026-09-02 14:56:51 +02:00
ElementBot eea7fb9424 Update screenshots 2026-09-02 11:31:48 +00:00
Jorge Martin Espinosa f2cd0bf888 Improve detection of invalid placeholders in localazy strings (#7624) 2026-09-02 13:02:40 +02:00
jmartinesp dfccde93c6 Sync Strings from Localazy 2026-09-02 10:07:17 +00:00
Benoit Marty c584e20dc3 Merge pull request #7625 from element-hq/feature/bma/moveKnockAndGalleryToLabs
Move knock and gallery to labs
2026-09-02 12:00:03 +02:00
renovate[bot] b9c3cb855e Update wysiwyg to v2.42.1 2026-09-02 09:30:43 +00:00
Jorge Martín eb121289fe Add GRADLE_OPTS for sync-localazy.yml flow 2026-09-02 11:29:41 +02:00
Benoit Marty 924cf2caf3 Reorder feature flags in labs: Thread, Gallery, Knock 2026-09-02 11:02:49 +02:00
Benoit Marty 7a2f3952cb Move Gallery and Knock feature flags to Labs. 2026-09-02 10:59:51 +02:00
Benoit Marty 679c8b146b Merge pull request #7623 from element-hq/renovate/dependencyanalysis
Update dependencyAnalysis to v3.19.1
2026-09-02 10:19:10 +02:00
renovate[bot] 09d95393d9 Update dependency io.element.android:element-call-embedded to v0.25.0 (#7622)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-02 09:31:06 +02:00
renovate[bot] afc6406312 Update dependencyAnalysis to v3.19.1 2026-09-02 06:55:15 +00:00
renovate[bot] b97d085f9b Update dependency org.jsoup:jsoup to v1.23.2 (#7620)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-02 08:53:22 +02:00
renovate[bot] 3b37bef39c Update dependency com.posthog:posthog-android to v3.60.7 (#7616)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-01 18:20:02 +02:00
Jorge Martin Espinosa 27e161605f Allow retrying finding the release assets in release script (#7619) 2026-09-01 16:59:56 +02:00
Jorge Martín 99fce78f76 Changelog for version v26.09.1 2026-09-01 14:39:23 +02:00
Jorge Martín 83b58b798c Merge tag 'v26.09.1' into develop
v26.09.1
2026-09-01 14:07:09 +02:00
Jorge Martín 18bb4632f2 Merge branch 'release/26.09.1' 2026-09-01 14:01:46 +02:00
Jorge Martín aa695d2ad6 Adding fastlane file for version 26.09.1 2026-09-01 13:57:54 +02:00
Jorge Martín 7c42f22b7c Setting version for the release 26.09.1 2026-09-01 13:57:32 +02:00
Jorge Martin Espinosa e5065d9baa Fix invalid placeholders in translations (#7612)
* Add logic to find invalid string placeholders in `checkForbiddenTerms.py`

* Fix found invalid placeholder in Chinese translations
2026-09-01 12:40:05 +02:00
Benoit Marty 52fcc797b7 Merge pull request #7523 from hayaksi1/fix/1874-empty-formatted-body
Fall back to the message body when the formatted body has nothing to render
2026-09-01 11:37:59 +02:00
Benoit Marty 3c789b4c12 Merge pull request #7618 from element-hq/renovate/roborazzi
Update roborazzi to v1.73.0
2026-09-01 10:05:08 +02:00
Benoit Marty b6ec225e54 Merge pull request #7613 from element-hq/feature/bma/polls-max-selections
Support creating polls with multiple answers and send response with multiple answers
2026-09-01 09:50:13 +02:00
renovate[bot] 987d343379 Update roborazzi to v1.73.0 2026-09-01 07:05:00 +00:00
Jorge Martín 8ec4035368 Merge branch 'release/26.09.0' 2026-08-31 19:00:52 +02:00
Jorge Martín b385a93683 Adding fastlane file for version 26.09.0 2026-08-31 19:00:51 +02:00
Jorge Martín e6c2b96275 Setting version for the release 26.09.0 2026-08-31 18:59:38 +02:00
Benoit Marty f3b34b7267 Merge pull request #7608 from element-hq/feature/bma/cleanUpExternalSession
Remove ExternalSession, fallback login with Element Web is gone.
2026-08-31 17:01:00 +02:00
Benoit Marty 012c5a1f81 Do some renaming now that user can select multiple answers per poll 2026-08-31 15:45:14 +02:00
Benoit Marty 0ad3ab1d9d Merge pull request #6477 from bxdxnn/feat/polls-max-selections
Support multi-select polls
2026-08-31 15:26:35 +02:00
Benoit Marty ab09500e7e Remove unused import 2026-08-31 15:06:39 +02:00
hayyaksi c6fe9786a9 Stop rescanning message bodies for emoji only on every recomposition (#7531)
Deciding whether a text message is emoji only compares the formatted body to
the plain one and runs a grapheme scan over the whole message. Nothing was
remembered, so both ran again on every recomposition of every visible message.

Remember the decision against the body and the formatted body. The preview
branch moves into the same computation, so the composable wrapper around
containsOnlyEmojis, which had a single caller and existed only to read
LocalInspectionMode, is no longer needed.
2026-08-31 12:23:01 +02:00
Benoit Marty 9855224e50 Merge pull request #7600 from toger5/toger5/fix-crash-on-circular-obj-logging
Fix crash due to JSON.stringify on a cirular structure in injectes js.
2026-08-31 12:19:23 +02:00
Benoit Marty 8081682f8f Remove ExternalSession, fallback login with Element Web is gone. 2026-08-31 12:07:20 +02:00
hayyaksi b85174d35e Only track read receipts on message-like events (#7361)
Ask the SDK to track receipts on message-like events only, for the live
timeline and for the focused and threaded ones, so all three agree. The
disabled case for the media and pinned-event timelines is unchanged. 

This matches the behaviour on Element X iOS.

The SDK attaches the receipt to the preceding event rather than the next
one, which is the placement its API offers.
2026-08-31 11:44:40 +02:00
Benoit Marty 9ad21a1724 Merge pull request #7595 from element-hq/renovate/org.unifiedpush.android-connector-3.x
Update dependency org.unifiedpush.android:connector to v3.3.5
2026-08-31 10:42:33 +02:00
Benoit Marty 0d4adb175d Merge pull request #7606 from element-hq/feature/bma/releaseScriptCheckUnmergedPrs
[release] Check that there is no unmerged PR with the label "Z-NextRelease", else exit
2026-08-31 10:40:26 +02:00
Benoit Marty f97d207b16 [release] Check that there is no unmerged PR with the label "Z-NextRelease", else exit 2026-08-31 09:51:03 +02:00
Benoit Marty ffa68de260 Merge pull request #7604 from element-hq/feature/bma/fixBlankScreen
Ensure that sessionVerifiedStatus is updated when the crypto is ready.
2026-08-31 09:40:29 +02:00
Benoit Marty 8b03e4d41a Ensure that sessionVerifiedStatus is updated when the crypto is ready.
If we do not do that the FTUE state stays on WaitingForInitialState and user is stuck on a blank screen.
2026-08-31 09:14:03 +02:00
Timo K. dc2d05f9da also add try catch block 2026-08-30 13:12:57 +02:00
Timo K. 7e8efebe4d Fix crash due to JSON.stringify on a cirular structure in injectes js. 2026-08-30 12:38:04 +02:00
hayaksi1 b15125289b Merge branch 'develop' into fix/1874-empty-formatted-body 2026-08-30 02:04:57 +03:00
renovate[bot] bd81a0ce9b Update dependency org.unifiedpush.android:connector to v3.3.5 2026-08-29 13:35:28 +00:00
Benoit Marty c47446bfb9 Merge pull request #7588 from element-hq/feature/bma/avoidNullableCallback
Limit usage of nullable callback.
2026-08-28 22:09:29 +02:00
Benoit Marty c4606e59ef Fix test compilation issue. 2026-08-28 19:02:43 +02:00
Benoit Marty c204d867a2 Merge pull request #7585 from element-hq/rav/app_startup_hook
Implement a hook allowing enterprise modules to perform actions on startup
2026-08-28 16:06:25 +02:00
Benoit Marty 97126715c1 Merge pull request #7589 from element-hq/feature/bma/logGitSha
Log the SDK git Sha at application startup
2026-08-28 14:10:21 +02:00
Benoit Marty 65878c6a34 Log the SDK git Sha at application startup
Also improve the rendering of the log
2026-08-28 12:18:49 +02:00
Benoit Marty cca60ebc3a Limit usage of nullable callback. 2026-08-28 11:01:54 +02:00
Benoit Marty 4d12ec3682 Merge pull request #7536 from hayaksi1/fix/3434-mention-suggestion-ranking
Rank the mention suggestions by how well they match what was typed
2026-08-28 10:39:24 +02:00
Benoit Marty 3488757e2a Merge pull request #7574 from hayaksi1/fix/3835-direct-share-targets
Offer conversations in the system share sheet
2026-08-28 10:38:23 +02:00
renovate[bot] a52d13ab5f Update dependency org.unifiedpush.android:connector to v3.3.4 (#7586)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-28 10:08:53 +02:00
Richard van der Hoff f6da347fff Merge remote-tracking branch 'origin/develop' into rav/app_startup_hook 2026-08-27 23:06:37 +01:00
Richard van der Hoff 712fe5b217 Implement a hook allowing enterprise modules to perform actions on startup
Add an interface `io.element.android.features.enterprise.api.AppStartupHook`,
which can be implemented in the enterprise build to perform certain actions on
startup.

It's a bit of a kludge for now since it doesn't block the rest of the app from
proceeding, but it will serve our purposes for now.
2026-08-27 22:36:47 +01:00
Benoit Marty 3ec30d7c94 Merge pull request #7539 from hayaksi1/fix/5356-sender-name-fallback
Name a sender the timeline has no profile for from the room member list
2026-08-27 23:19:05 +02:00
hayaksi1 991e801fd8 Update screenshots 2026-08-27 22:28:09 +03:00
hayaksi1 74a91a7348 Give the removal confirmation the dialog the team asked for
The dialog now reads "Confirm removal" over "Are you sure you wish to remove
this message?", with the reason field marked as optional beneath it, rather
than a bare title and a labelled field. TextFieldDialog gained a supporting
text so the field can carry that note.

The pending removal moves from a nullable timeline item to redactEventAction,
an AsyncAction whose Confirming state carries the event id, so it is modelled
the same way as every other action the user has to confirm.
2026-08-27 22:21:09 +03:00
bxdxnn 84cd878069 Merge remote-tracking branch 'origin/develop' into HEAD 2026-08-27 18:40:10 +00:00
hayaksi1 e8e67dc0c5 Merge remote-tracking branch 'upstream/develop' into fix/5356-sender-name-fallback
# Conflicts:
#	features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactoryTest.kt
2026-08-27 21:19:35 +03:00
Benoit Marty 0c65dcea9d Merge pull request #7571 from hayaksi1/fix/7397-keep-thread-after-redaction
Don't hide the thread of a deleted message inside the removed messages group
2026-08-27 20:06:17 +02:00
Benoit Marty 3fecabfe3e Merge pull request #7584 from element-hq/feature/bma/fixSilentNotification
Ensure that noisy notifications are still noisy when the app has a PIN code.
2026-08-27 20:02:51 +02:00
renovate[bot] c986a2a971 Update dependency com.google.guava:guava to v33.7.1-android (#7534)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-27 20:01:45 +02:00
renovate[bot] b8981ff493 Update dependency io.github.sergio-sastre.ComposablePreviewScanner:android to v0.9.3 (#7535)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-27 20:01:26 +02:00
renovate[bot] 616ed92d00 Update dependency com.google.firebase:firebase-bom to v34.18.0 (#7581)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-27 18:47:58 +02:00
renovate[bot] f0c88eb12e Update dependency net.zetetic:sqlcipher-android to v4.18.0 (#7552)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-27 18:47:22 +02:00
Benoit Marty 586ae00cda Ensure that noisy notifications are still noisy when content is hidden when a PIN code is protecting the application.
Closes #7575
2026-08-27 16:22:28 +02:00
hayyaksi e19cfbb423 Merge branch 'develop' into fix/7397-keep-thread-after-redaction 2026-08-27 16:51:44 +03:00
Benoit Marty eb946c6daf Merge pull request #7583 from element-hq/feature/bma/releaseV2NoEditor
Do not open the editor when finishing the release
2026-08-27 15:31:54 +02:00
hayaksi1 0832b7de23 Merge remote-tracking branch 'upstream/develop' into fix/7397-keep-thread-after-redaction
# Conflicts:
#	features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt
#	features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactoryTest.kt
#	features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt
#	libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/EventItemFactoryTest.kt
2026-08-27 16:27:18 +03:00
Benoit Marty 8645260d46 Do not open the editor when finishing the release
`git flow release finish` opened the editor three times, and the content was
never edited: twice for the merge commits, where the message generated by git is
always kept, and once for the message of the annotated tag, where a placeholder
was typed by hand. The last tags carry the message "tag" for that reason.

GIT_MERGE_AUTOEDIT=no keeps the merge messages git already generates, and -m
provides the tag message. git flow appends the tag name to that message, so the
tag of the next release will read "Release v26.09.0".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 14:58:39 +02:00
Benoit Marty 4996819812 Changelog for version 26.08.4 2026-08-27 14:53:48 +02:00
Benoit Marty 743aefbe37 Merge tag 'v26.08.4' into develop
tag
2026-08-27 14:21:59 +02:00
Benoit Marty ec3286ef8f Merge branch 'release/26.08.4' 2026-08-27 14:21:20 +02:00
Benoit Marty 383519aff5 Adding fastlane file for version 26.08.4 2026-08-27 14:21:17 +02:00
Benoit Marty 2f3d197060 Setting version for the release 26.08.4 2026-08-27 14:20:36 +02:00
Benoit Marty 5e87387dac Merge pull request #7582 from element-hq/feature/bma/updateSubmoduleRef
Update submodule ref.
2026-08-27 14:14:40 +02:00
Benoit Marty 103c606968 Merge pull request #7579 from element-hq/feature/bma/improveReleaseScript
Add releaseV2.sh, a release script automating the GitHub release
2026-08-27 12:20:52 +02:00
Benoit Marty 6fbccbc897 Update submodule ref. 2026-08-27 12:17:03 +02:00
Benoit Marty 4800bbc63f Merge pull request #7546 from hayaksi1/fix/4475-element-call-docs
Document how the Element Call base URL developer option is used
2026-08-27 11:20:04 +02:00
Benoit Marty d3ab1ce43c Merge pull request #7562 from hayaksi1/fix/1548-dm-preview-sender
Drop the sender's name from the room list preview of two-person rooms
2026-08-27 11:19:32 +02:00
Benoit Marty d9c39e8f98 Merge pull request #7480 from bxdxnn/fix/deleted-messages-thread
Fix removed messages hiding its thread on deletion
2026-08-27 10:04:36 +02:00
Benoit Marty 740e3b621b Merge pull request #7344 from hayaksi1/fix/6371-save-file-name-conflicts
Keep saving files to Downloads once 32 files share the same name
2026-08-27 10:01:57 +02:00
Benoit Marty 3758aac6d3 Do not build the enterprise build, it's built elsewhere. 2026-08-27 08:52:44 +02:00
Benoit Marty c5f70de904 Add releaseV2.sh, a release script automating the GitHub release
releaseV2.sh is a copy of release.sh in which the GitHub CLI is mandatory and
does what was previously done by hand: it finds and waits for the release.yml
run, downloads and extracts its artifacts, creates the pre-release with its 6
assets and the generated release notes, and prepends those notes to CHANGES.md.
It also compares the version code and version name of the signed artifacts to
the expected values instead of asking the user to check them, and no longer
needs ELEMENT_GITHUB_TOKEN, since the CLI uses its own credentials. release.sh
is left untouched, so that the next release can be done with releaseV2.sh while
keeping a way back if something goes wrong. The installation documentation is
also fixed, as it referred to an asset name that does not exist anymore.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 22:15:54 +02:00
Benoit Marty dd7a7d57be Merge pull request #7567 from hayaksi1/fix/4099-server-unreachable
Say when the server cannot be reached instead of showing nothing
2026-08-26 16:56:40 +02:00
Benoit Marty c56001fc98 Merge pull request #7572 from hayaksi1/fix/6730-keep-caption-formatting
Keep the formatting of a caption that is being edited
2026-08-26 16:47:59 +02:00
Benoit Marty 0e86de4e58 Changelog for version 26.08.3 2026-08-26 15:35:00 +02:00
Benoit Marty b8dd1ce2d2 Merge tag 'v26.08.3' into develop
tag
2026-08-26 13:56:54 +02:00
Benoit Marty 0977ebd104 Merge branch 'release/26.08.3' 2026-08-26 13:56:44 +02:00
Benoit Marty cbc7de9982 Adding fastlane file for version 26.08.3 2026-08-26 13:56:41 +02:00
Benoit Marty 94259962a0 Setting version for the release 26.08.3 2026-08-26 13:56:12 +02:00
Benoit Marty dba8f1b276 Merge pull request #7577 from element-hq/feature/bma/explorePushRules
Add a "Push rules" entry in the developer options
2026-08-26 13:54:07 +02:00
Benoit Marty 267e48aa69 Add instructions to let the CI record the screenshot if necessary.
Also update instruction about PR size.
2026-08-26 13:50:45 +02:00
ElementBot 0e93a943a5 Update screenshots 2026-08-26 11:16:07 +00:00
Benoit Marty 2fcf806f59 Pretty print the push rules content before displaying it
The raw push rules returned by the SDK are a single long line of json, which is
hard to read on a phone. Re-encode the content with a pretty printer using a two
space indentation before handing it to the file viewer.

Parsing to a JsonElement and re-encoding preserves the order of the keys, so the
rules are still listed in the order the server returned them. Content which
cannot be parsed is passed through unchanged, so that a malformed payload remains
viewable.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 12:55:07 +02:00
Benoit Marty 373d2f1eb5 Add a Push rules entry to the developer options
Add a "Push rules" item in the Notifications section of the developer options,
above "Push history". It displays the raw push rules of the account, which were
only available as a rageshake attachment so far.

The content is read using NotificationSettingsService.getRawPushRules(), then
rendered by the new FileViewerEntryPoint, so that the screen looks like the media
viewer rendering a file, with Share and Download actions on a
push_rules<userId>.json file. The user id contains a colon, which is not a valid
file name character on all the file systems, so it is replaced by an underscore.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 12:11:16 +02:00
Benoit Marty 0801a415b5 Add a FileViewerEntryPoint to render in-memory content as a file
Introduce io.element.android.libraries.mediaviewer.api.FileViewerEntryPoint, a
reusable screen which renders content that is not a Matrix media the same way the
media viewer renders a file, with Share and Download actions.

The caller provides a filename, a mime type and the whole content as a String.
The node writes it to a temporary file in the cache directory, then reuses
LocalMediaView (so text content goes through TextFileViewer) and LocalMediaActions
for sharing and saving on disk. The node is contributed to AppScope so that the
screen can be used from any graph, since MediaViewerNode itself is bound to
RoomScope and depends on the timeline data sources.

Also route MimeTypes.Json to TextFileView in LocalMediaView, so that a json file
is rendered as text instead of falling back to the generic file card.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 12:11:04 +02:00
Benoit Marty c9da3ab490 Merge pull request #7520 from element-hq/feature/bma/updateFonts
Update fonts on the headings
2026-08-26 09:51:50 +02:00
Benoit Marty 3bdff12e6e Merge pull request #7570 from hayaksi1/fix/7509-run-database-migrations
Run the database migrations again when a database is upgraded
2026-08-26 09:38:05 +02:00
hayaksi1 6d28d91541 Offer conversations in the system share sheet
Sharing from another app listed Element X once, with no way to pick a
conversation: the Direct Share row never showed any.

The app already publishes a long-lived, conversation-categorised shortcut per
room, which is most of what the sharing shortcuts API needs. The part that was
missing is the declaration that ties those shortcuts to the share intent: the
launcher activity had no `android.app.shortcuts` meta-data, so there was no
`<share-target>` for the system to match them against.

It now ships one, matching the activity that already receives ACTION_SEND, and
the shortcuts carry the category it names.

The system offers these from Android 10 onwards, where sharing shortcuts are
handled natively. Reaching further back would additionally need the
androidx.sharetarget library, which is not added here.

Part of #3835
2026-08-26 03:18:28 +03:00
hayaksi1 4290a9c3b3 Keep the formatting of a caption that is being edited
Editing the caption of a media message dropped all of its formatting: bold,
links and the rest came back as plain text, and saving wrote that plain text
over the formatted caption.

The composer was being opened on the plain caption. Editing a message body
already starts from its HTML when rich text editing is on, and the caption
path was the only one that did not - it read `captionOrNull()` and never had
the caption's HTML to read, because the timeline content only kept the
already-parsed `formattedCaption`.

Media contents now carry the caption's HTML alongside the parsed one, the
same way text contents carry `htmlBody`, and editing a caption starts from it
when rich text editing is on. The save path already sent the composer's HTML,
so nothing changes there.

Part of #6730
2026-08-25 22:03:14 +03:00
hayaksi1 2278116579 Keep the thread of a message that has been deleted
Deleting a message that headed a thread took the thread with it: the "N
replies" summary under the message disappeared and there was no way back to
the replies, which are still there.

Every branch of the timeline content mapper passes `MsgLikeContent`'s thread
information on except the redacted one, which returned a bare
`RedactedContent` and dropped it. The thread summary lives on
`MsgLikeContent` next to `kind`, so it survives the redaction; only the app
was throwing it away. `RedactedContent` now carries the thread info like the
other contents do, and `EventTimelineItem.threadInfo()` reads it.

A deleted message that still heads a thread is also left out of the "N
removed messages" group, which would otherwise hide the summary again as soon
as three deletions ended up next to each other.

Part of #7397
2026-08-25 21:52:20 +03:00
hayaksi1 594a5612ad Register the second sync state preview with konsist 2026-08-25 21:45:59 +03:00
hayaksi1 362acd6cab Run the database migrations again when a database is upgraded
Upgrading over an older install crashed before the login screen with
`SQLiteException: no such column: SessionData.position`. The session, cache
and push databases were opened at their new schema version but their tables
were never migrated.

`SqlCipherDriverFactory` overrides `AndroidSqliteDriver.Callback.onUpgrade` so
the database key can be replaced, and never called through to the base
implementation - which is the only thing that runs `schema.migrate`. Every
SQLDelight migration in the app was skipped as a result.

The override now calls `super.onUpgrade` after the re-key, so a database that
crosses the re-key version is still re-encrypted before its tables are
migrated. It moved out of an anonymous object into a named `MigrationCallback`
so the behaviour can be tested.

Part of #7509
2026-08-25 21:41:17 +03:00
ElementBot 69ba9cf21e Update screenshots 2026-08-25 18:10:19 +00:00
hayaksi1 65c2cd8a79 Drop the sender's name from the room list preview of two-person rooms
The room list preview prefixes the sender's name unless the room is a
direct message. A room that was created as a group and later converted to
a direct message is not marked as one by the server, so its preview kept
naming the only other person in it on every message, while iOS did not.

The prefix now depends on how many people are actually in the room rather
than on the direct message flag, which is the behaviour asked for in the
issue: no name while only two people can be talking, the name back as
soon as a third joins.

Part of #1548
2026-08-25 20:55:38 +03: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 a2d3488426 Merge pull request #7550 from element-hq/feature/bma/updateSdk
Update sdk to version 26.08.25
2026-08-25 16:57:40 +02:00
Benoit Marty 7db6025a05 Merge pull request #7544 from hayaksi1/fix/2515-gallery-build-date
Show the build date in the screenshot gallery footer
2026-08-25 16:28:49 +02:00
hayaksi1 ca117ebcdd Record the screenshots for the new incoming verification step 2026-08-25 17:04:49 +03:00
Benoit Marty b704d227d1 Merge pull request #7543 from hayaksi1/fix/2812-debug-proxying-docs
Point the proxy debugging doc at the method that builds the clients
2026-08-25 15:59:51 +02:00
Benoit Marty 776cfc9c80 Add new type RoomRetention 2026-08-25 15:55:51 +02:00
Benoit Marty 16bc825c17 username has been renamed to serverNameFromUserId, but it is not necessary if we call homeserverUrl, so just remove it. 2026-08-25 15:55:29 +02:00
hayaksi1 1cd27761ad Say so when this session has no secrets to verify another one with
A session whose recovery state is INCOMPLETE has not got the secrets it
would need to sign another device, so accepting an incoming verification
request from it can only fail. The screen offered "Start verification"
anyway, and the user learnt otherwise at the end of the flow.

Show the design's alert step instead — "Verification with this device is
currently unavailable" — with only the Ignore action, and leave the
request unacknowledged in every other respect.

Part of #5344.
2026-08-25 16:44:08 +03:00
hayaksi1 c893657aa0 Document how the Element Call base URL developer option is used
The developer option passes its value through to the widget URL verbatim,
so it has to be the URL of the page that joins a room rather than the
deployment root. That is not obvious from the field, and hughns asked for
it to be written down in docs/ rather than expanded inline in the app,
because the path depends on how the deployment was hosted.

Part of #4475.
2026-08-25 16:30:49 +03:00
Benoit Marty 898baca9d5 SDK 26.08.25 2026-08-25 15:25:50 +02:00
hayaksi1 76b19a158f Show the build date in the screenshot gallery footer
The gallery gave no indication of how old the screenshots on the page were,
which is one of the remaining items on the GitHub Pages improvement list.

The generator now writes the time it ran alongside the screenshot data, and the
page renders it in the footer next to the existing links.
2026-08-25 15:56:16 +03:00
hayaksi1 23a60cf20a Point the proxy debugging doc at the method that builds the clients
The doc told the reader to add disableSslVerification() to
RustMatrixClientFactory.create(), which has not built the client for a long
time; getBaseClientBuilder() is where the chain lives, and it covers both the
login client and the restored session client.

It also only described the emulator's own proxy setting, while the app already
follows the device's global proxy through DefaultProxyProvider, which is the
only way to do this on a physical device.
2026-08-25 15:54:45 +03:00
Benoit Marty 12203aaf6d Merge pull request #7457 from hayaksi1/fix/1270-verification-request-timeout
Stop waiting on a verification request the other session never accepts
2026-08-25 14:22:39 +02:00
hayaksi1 e09d6e0a22 Name a sender the timeline has no profile for from the room member list
The timeline renders a message with the sender's raw Matrix ID whenever the
SDK has no profile for them, even though the room member list the same screen
already holds usually knows their display name and avatar.

The timeline item factory now falls back to the room member for a sender whose
profile is not resolved, both when the item is first built and when the member
list arrives afterwards, so the name and the avatar settle on the display name
instead of staying on the ID.
2026-08-25 15:22:32 +03:00
hayaksi1 1f4d8b1733 Update screenshots 2026-08-25 15:14:52 +03:00
hayaksi1 bc32cf1d4a Let the user give a reason when removing a message
The protocol carries a reason on a redaction and other clients let the sender
give one, but removing a message here sent the redaction straight away with no
reason and no confirmation.

Removing a message now opens a dialog with an optional reason, and the
redaction is only sent once it is confirmed. A blank reason is sent as none, so
confirming without typing anything behaves exactly as before. A message which
was never sent has nobody to give a reason to, so the send failure dialog's
Remove still redacts it immediately.
2026-08-25 14:52:05 +03:00
Benoit Marty af7e765599 Fixing compilation issue after merging develop 2026-08-25 13:32:11 +02:00
Benoit Marty 4b4ceebdb1 Merge branch 'develop' into fix/1270-verification-request-timeout 2026-08-25 13:30:57 +02:00
hayaksi1 88b3559c6b Rank the mention suggestions by how well they match what was typed
The member suggestions were emitted in whatever order the room member list
happened to be in, so a member who only matches somewhere in the middle of
their name could be offered above one whose name starts with the query.

Matches are now ordered by how well they match: display name prefix first,
then user ID localpart prefix, then any other match. The sort is stable, so
members of equal rank keep the order the member list gave them.

The @room suggestion was also matched case sensitively, so typing @Ro or
@ROOM dropped it while @ro kept it. It now matches the query the same way
the members do.
2026-08-25 14:30:09 +03:00
Benoit Marty 872319c8ea Merge pull request #7521 from element-hq/fix/account-provider-supporting-text-clipped
Make the account provider screen scrollable so its description is not clipped by the keyboard
2026-08-25 13:29:23 +02:00
Benoit Marty 23437e4427 Merge pull request #7533 from element-hq/feature/bma/renameRemainingEvents
Rename remaining events sealed interface
2026-08-25 12:29:35 +02:00
ElementBot 806f7c500c Update screenshots 2026-08-25 10:09:30 +00:00
Benoit Marty 0634cadc9c Ensure translation of "Chats" does not render in big font when displayed on 2 lines. They can add the end be displayed on one line using the small font. 2026-08-25 11:54:15 +02:00
Benoit Marty 0a9a25d940 Update Konsist test and fix existing issue. 2026-08-25 11:21:10 +02:00
Benoit Marty d7310b6941 Split code into several files. 2026-08-25 10:55:38 +02:00
Benoit Marty 7168959507 Fix documentation: Events -> Event 2026-08-25 10:54:01 +02:00
ElementBot c62ff733fb Update screenshots 2026-08-25 08:37:53 +00:00
Benoit Marty 34a086261f PushHistoryEvents -> PushHistoryEvent 2026-08-25 10:33:58 +02:00
Benoit Marty cfa63156e2 TroubleshootNotificationsEvents -> TroubleshootNotificationsEvent 2026-08-25 10:33:16 +02:00
Benoit Marty 217ccb75ed BatteryOptimizationEvents -> BatteryOptimizationEvent 2026-08-25 10:32:34 +02:00
Benoit Marty cb8c462bad FullScreenIntentPermissionsEvents -> FullScreenIntentPermissionsEvent 2026-08-25 10:31:51 +02:00
Benoit Marty 2b55ff097f ViewFileEvents -> ViewFileEvent 2026-08-25 10:31:09 +02:00
Benoit Marty 328d2d1260 OutgoingVerificationViewEvents -> OutgoingVerificationViewEvent 2026-08-25 10:30:19 +02:00
Benoit Marty 2dda3b807d IncomingVerificationViewEvents -> IncomingVerificationViewEvent 2026-08-25 10:29:30 +02:00
Benoit Marty 650f20d6ba UserProfileEvents -> UserProfileEvent 2026-08-25 10:28:49 +02:00
Benoit Marty 51a4704854 UserListEvents -> UserListEvent 2026-08-25 10:28:07 +02:00
Benoit Marty cf7d5ab4b4 StartChatEvents -> StartChatEvent 2026-08-25 10:27:26 +02:00
Benoit Marty 771840884a SpaceSettingsEvents -> SpaceSettingsEvent 2026-08-25 10:26:43 +02:00
Benoit Marty d07eba9a20 SpaceEvents -> SpaceEvent 2026-08-25 10:26:02 +02:00
Benoit Marty 15cdd85357 LeaveSpaceEvents -> LeaveSpaceEvent 2026-08-25 10:25:19 +02:00
Benoit Marty a2c3ddab02 SignedOutEvents -> SignedOutEvent 2026-08-25 10:24:37 +02:00
Benoit Marty 739fc452dd ShareEvents -> ShareEvent 2026-08-25 10:23:51 +02:00
Benoit Marty deb08a3a6c EditRoomAddressEvents -> EditRoomAddressEvent 2026-08-25 10:23:12 +02:00
Benoit Marty 47f4e136dd Use fontHeadingMdBold for empty space title. 2026-08-25 10:22:31 +02:00
Benoit Marty b8b6dc1df6 SecureBackupSetupEvents -> SecureBackupSetupEvent 2026-08-25 10:22:22 +02:00
Benoit Marty 28c19e38ce SecureBackupRootEvents -> SecureBackupRootEvent 2026-08-25 10:21:36 +02:00
Benoit Marty 2d611475da SecureBackupEnterRecoveryKeyEvents -> SecureBackupEnterRecoveryKeyEvent 2026-08-25 10:20:48 +02:00
Benoit Marty c8c579ed7e SecureBackupDisableEvents -> SecureBackupDisableEvent 2026-08-25 10:19:58 +02:00
Benoit Marty 4981a04519 InternalRoomMemberModerationEvents -> InternalRoomMemberModerationEvent 2026-08-25 10:19:06 +02:00
Benoit Marty 17213ac93e RoomMemberModerationEvents -> RoomMemberModerationEvent 2026-08-25 10:18:21 +02:00
Benoit Marty 4e9ec1b631 RoomDirectoryEvents -> RoomDirectoryEvent 2026-08-25 10:17:21 +02:00
Benoit Marty 15569ef3e8 RoomAliasResolverEvents -> RoomAliasResolverEvent 2026-08-25 10:16:32 +02:00
Benoit Marty 4e2d99c968 RolesAndPermissionsEvents -> RolesAndPermissionsEvent 2026-08-25 10:15:35 +02:00
Benoit Marty fc8d3b3065 ReportRoomEvents -> ReportRoomEvent 2026-08-25 10:14:32 +02:00
Benoit Marty 859b0e1b60 BugReportEvents -> BugReportEvent 2026-08-25 10:13:33 +02:00
Benoit Marty 5d8ca663df EditDefaultNotificationSettingStateEvents -> EditDefaultNotificationSettingStateEvent 2026-08-25 10:12:06 +02:00
Benoit Marty 8a02b50c98 NotificationSettingsEvents -> NotificationSettingsEvent 2026-08-25 10:11:07 +02:00
Benoit Marty a113d44558 LabsEvents -> LabsEvent 2026-08-25 10:10:11 +02:00
Benoit Marty 7952721ffe DeveloperSettingsEvents -> DeveloperSettingsEvent 2026-08-25 10:09:09 +02:00
Benoit Marty d8e7031709 BlockedUsersEvents -> BlockedUsersEvent 2026-08-25 10:08:17 +02:00
Benoit Marty e6be199d4a AdvancedSettingsEvents -> AdvancedSettingsEvent 2026-08-25 10:07:35 +02:00
Benoit Marty 87af3b85b3 PollHistoryEvents -> PollHistoryEvent 2026-08-25 10:06:58 +02:00
Benoit Marty d42a93100d ThreadsListEvents -> ThreadsListEvent 2026-08-25 10:06:20 +02:00
Benoit Marty 116897d64c LogoutEvents -> LogoutEvent 2026-08-25 10:05:38 +02:00
Benoit Marty 93efe3348f DirectLogoutEvents -> DirectLogoutEvent 2026-08-25 10:05:04 +02:00
Benoit Marty a039fcd655 QrCodeScanEvents -> QrCodeScanEvent 2026-08-25 10:04:23 +02:00
hayaksi1 6b2e83cb63 Fall back to the message body when the formatted body has nothing to render
The HTML sanitiser in toHtmlDocument() drops every tag outside its safelist,
which does not include img. A formatted body whose only content is an image —
a custom emoji, or a bot posting an image inline — therefore cleans down to an
empty document, and because that document is not null the ?: body fallbacks in
the timeline item factory, the room list preview, the notification resolver and
plainText are all dead. The message renders as an empty bubble.

Return null from toHtmlDocument() when no text survives the sanitisation, so the
existing fallbacks take the plain text body, which for these messages carries the
alt text the sender provided.
2026-08-25 11:03:42 +03:00
Benoit Marty 0bf671a305 QrCodeIntroEvents -> QrCodeIntroEvent 2026-08-25 10:03:42 +02:00
Benoit Marty b75d8bb6c1 OnBoardingEvents -> OnBoardingEvent 2026-08-25 10:03:05 +02:00
Benoit Marty 835690a1c2 LoginPasswordEvents -> LoginPasswordEvent 2026-08-25 10:02:25 +02:00
Benoit Marty 873843b669 ConfirmAccountProviderEvents -> ConfirmAccountProviderEvent 2026-08-25 10:01:42 +02:00
Benoit Marty 1fd102ffc3 ChooseAccountProviderEvents -> ChooseAccountProviderEvent 2026-08-25 10:01:04 +02:00
Benoit Marty 0ee7ed3a78 ChangeServerEvents -> ChangeServerEvent 2026-08-25 10:00:15 +02:00
Benoit Marty e07099f116 PermissionsEvents -> PermissionsEvent 2026-08-25 09:59:31 +02:00
Benoit Marty ee30d6aa41 KnockRequestsListEvents -> KnockRequestsListEvent 2026-08-25 09:58:53 +02:00
Benoit Marty 7da820464f KnockRequestsBannerEvents -> KnockRequestsBannerEvent 2026-08-25 09:58:15 +02:00
Benoit Marty 19221a8042 JoinRoomEvents -> JoinRoomEvent 2026-08-25 09:57:38 +02:00
Benoit Marty 55a7d278c9 DefaultInvitePeopleEvents -> DefaultInvitePeopleEvent 2026-08-25 09:56:59 +02:00
Benoit Marty 983d05fc13 InvitePeopleEvents -> InvitePeopleEvent 2026-08-25 09:56:23 +02:00
Benoit Marty 4c56833ada DeclineAndBlockEvents -> DeclineAndBlockEvent 2026-08-25 09:55:46 +02:00
Benoit Marty e395efe730 InternalAcceptDeclineInviteEvents -> InternalAcceptDeclineInviteEvent 2026-08-25 09:55:09 +02:00
Benoit Marty 91aa9ce24f AcceptDeclineInviteEvents -> AcceptDeclineInviteEvent 2026-08-25 09:54:32 +02:00
Benoit Marty fe57cfa991 HomeSpacesEvents -> HomeSpacesEvent 2026-08-25 09:53:54 +02:00
Benoit Marty 3ae9ba74b4 NotificationsOptInEvents -> NotificationsOptInEvent 2026-08-25 09:53:16 +02:00
Benoit Marty 17a3db246f ForwardMessagesEvents -> ForwardMessagesEvent 2026-08-25 09:52:38 +02:00
Benoit Marty e1e2718c54 AccountDeactivationEvents -> AccountDeactivationEvent 2026-08-25 09:52:00 +02:00
Benoit Marty dff9af9ce0 ConfigureRoomEvents -> ConfigureRoomEvent 2026-08-25 09:51:17 +02:00
Benoit Marty 7353e0e476 AnalyticsOptInEvents -> AnalyticsOptInEvent 2026-08-25 09:50:39 +02:00
Benoit Marty 64f1f299e6 LoggedInEvents -> LoggedInEvent 2026-08-25 09:50:04 +02:00
bxdxnn b08224fedb Merge 2026-08-24 20:09:11 +00:00
bxdxnn 3d400bad4a Merge 2026-08-24 20:04:47 +00:00
ElementBot 95866f960e Update screenshots 2026-08-24 17:16:01 +00:00
Benoit Marty 9586478039 Updates to text sizes in Chats header and Space hero
Closes #7476
2026-08-24 18:58:54 +02:00
Benoit Marty 6ab3d7393b Merge pull request #7447 from hayaksi1/fix/6518-encryption-indicator
Hide the encryption badge when key storage is set up on the server
2026-08-24 18:26:00 +02:00
Benoit Marty cc6827b827 Merge pull request #7519 from sandhose/quenting/remove-oidc-playground-ref
Remove the reference to the decommissioned OIDC playground
2026-08-24 18:15:39 +02:00
Benoit Marty b2b0927ea9 Merge branch 'develop' into fix/6371-save-file-name-conflicts 2026-08-24 18:14:03 +02:00
Benoit Marty f740595ad5 Merge pull request #7516 from element-hq/renovate/guava-monorepo
Update dependency com.google.guava:guava to v33.7.0-android
2026-08-24 18:10:32 +02:00
Benoit Marty beddb3e86e Merge pull request #7518 from element-hq/bma/renamePreviewProvider
Update Ref.
2026-08-24 18:09:05 +02:00
Benoit Marty f0fa07a551 Merge pull request #7517 from element-hq/feature/bma/removeUserStatusFeatureFlag
Enable user status feature and hide status text in the timeline
2026-08-24 18:08:10 +02:00
Jenna Vassar feb87ead8d Make the account provider screen scrollable so its description is not clipped by the keyboard 2026-08-24 08:59:11 -07:00
Quentin Gliech 2226eb5162 Remove the reference to the decommissioned OIDC playground
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 17:58:14 +02:00
Benoit Marty 300ca8ae5c Update Ref. 2026-08-24 17:40:44 +02:00
ElementBot 8fd491347a Update screenshots 2026-08-24 15:12:53 +00:00
Benoit Marty 7d6aa9eb76 Group preview for clarity and to limit the number of screenshots. 2026-08-24 16:37:36 +02:00
Benoit Marty 0a0eebd804 [UserStatus] Add missing previews. 2026-08-24 16:22:09 +02:00
Benoit Marty 951539e698 [UserStatus] Only render the emoji for senders in the timeline 2026-08-24 16:22:08 +02:00
Benoit Marty e50dee8b9c Remove UserStatus feature flag. 2026-08-24 16:22:08 +02:00
renovate[bot] 57201539bb Update dependency com.google.guava:guava to v33.7.0-android 2026-08-24 13:40:28 +00:00
Benoit Marty 0cd6e70777 Merge pull request #7468 from hayaksi1/fix/4657-preview-param-suffix
Rename PreviewParameterProvider subclasses to the PreviewParam suffix
2026-08-24 15:38:04 +02:00
Benoit Marty a3a0fdd0e9 Merge branch 'develop' into fix/4657-preview-param-suffix 2026-08-24 14:08:52 +02:00
Benoit Marty 0c21dbfedb Merge pull request #7515 from element-hq/feature/bma/fixLint
Fix lint issue, ensure that PiP methods are not called on old devices not supporting them.
2026-08-24 14:08:13 +02:00
Benoit Marty 00566ce5f6 Merge pull request #7514 from element-hq/misc/renameModule
Rename module and ensure that naming convention is followed
2026-08-24 12:54:16 +02:00
Benoit Marty 0d3d8c8456 Fix lint issue, ensure that PiP methods are not called on old devices not supporting them. 2026-08-24 12:52:39 +02:00
bxdxnn 65a827559b Merge 2026-08-24 10:30:25 +00:00
Benoit Marty 7d70bd32e2 Add missing annotation 2026-08-24 12:00:32 +02:00
Benoit Marty e6116d5840 Move all binding containers to a di package and normalize their method names
Every @BindingContainer now lives in a di subpackage, in a file named after
it. Containers that were declared inside a shared file (SimplePlayer.kt,
LockScreenConfig.kt, VoiceMessagePresenter.kt, TimelineItemPresenterFactories.kt,
MediaItemPresenterFactories.kt) are extracted into their own file.

Method names are made consistent too:
- @Binds functions are named bind<ConcreteParameterType>, fixing three that
  named something else (notably RoomAliasResolverBindingContainer, whose
  provider was still called providesJoinRoomPresenterFactory).
- @Provides functions are named after what they provide, and all use the
  provides prefix rather than a mix of provide and provides.

ANDROID_KEYSTORE moves back to the cryptography impl package, next to
KeyStoreSecretKeyRepository which uses it; it is not DI specific and would
otherwise be unreachable from there.

No behaviour change: Metro discovers containers via @ContributesTo and does
not care about method names, so the only call site to follow was in
DefaultVoiceRecorderTest.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 11:38:11 +02:00
Benoit Marty a845763f5b Update ContributesNodeProcessor so that generated classes follow naming convention 2026-08-24 11:31:22 +02:00
Benoit Marty 1018d26211 Rename all @BindingContainer types with a BindingContainer suffix
Metro's @BindingContainer replaced Dagger's @Module, but the declarations
kept the old Module suffix. Rename them (and their dedicated files) so the
type name matches the annotation, matching UserStatusBindingContainer which
already followed this convention.

No behaviour change: Metro discovers containers via @ContributesTo, so there
were only two call sites to update, both in tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 10:08:54 +02:00
hayaksi1 23005317cf Merge develop into the PreviewParam rename
The 174 commits develop gained since this branch was cut touch preview data
in three ways, and each is resolved so the flipped konsist rule still holds
across the merged tree.

Two screens the branch had renamed were deleted upstream by "Remove the
account provider search screen and homeserver resolver": the
changeaccountprovider and searchaccountprovider packages. Their deletion
wins, so ChangeAccountProviderStatePreviewParam, SearchAccountProviderStatePreviewParam
and the two views that used them go with them rather than coming back
under the new name.

Two preview classes develop added still carried the old suffix and are
renamed here, along with the `@PreviewParameter` reference in each view:
GlobalSearchStateProvider -> GlobalSearchStatePreviewParam and
SendFailureDialogStateProvider -> SendFailureDialogStatePreviewParam.

Everything else merged textually. No production class extending
PreviewParameterProvider is left with a `Provider` name, and no reference
to any of the 194 renamed names survives outside the CHANGES.md entry that
records a past pull request title.
2026-08-24 10:55:40 +03:00
Benoit Marty 16954bb66e Merge pull request #7449 from hayaksi1/fix/4112-wildcard-mime-type
Send a concrete mime type for media shared with a wildcard one
2026-08-24 09:41:18 +02:00
Benoit Marty 4c4cdd1678 Merge branch 'develop' into fix/6371-save-file-name-conflicts 2026-08-24 09:38:35 +02:00
Benoit Marty 4cde4b40c6 Merge pull request #6676 from bxdxnn/misc/reactions-pinned-messages
Show reactions in the pinned messages list
2026-08-24 09:37:51 +02:00
Benoit Marty 679c6e4786 Merge pull request #7410 from element-hq/feat/global-search-screen
Feature: global search
2026-08-24 09:34:35 +02:00
Benoit Marty e22321a4c1 Merge pull request #7512 from element-hq/renovate/com.squareup.okhttp3-okhttp-bom-5.x
Update dependency com.squareup.okhttp3:okhttp-bom to v5.5.0
2026-08-24 08:25:29 +02:00
Benoit Marty a33a84a684 Merge pull request #7500 from element-hq/feature/account-provider-full-mxid
Let users enter their full Matrix user ID on the account provider screen
2026-08-24 08:24:50 +02:00
Benoit Marty 081809d57e Merge pull request #7502 from element-hq/renovate/reconfigure
Remove workaround for Renovate.
2026-08-24 08:05:39 +02:00
renovate[bot] 9e66572970 Update dependency com.squareup.okhttp3:okhttp-bom to v5.5.0 2026-08-23 18:54:52 +00:00
Benoit Marty 477aa36640 Merge pull request #7505 from element-hq/bma/matrixModule
Remove dependency on enterprise module.
2026-08-22 11:53:18 +02:00
Benoit Marty eb8574e042 Remove dependency on shared module.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-22 10:59:20 +02:00
Benoit Marty 45d9339bfa Merge pull request #7495 from element-hq/refactor/add-client-enterprise-hooks
Add `ClientEnterpriseHook` and `ClientBuilderEnterpriseHook`
2026-08-22 08:10:50 +02:00
Benoit Marty 277dd4a2fd Fix test compilation 2026-08-21 18:53:43 +02:00
Benoit Marty e205b7a3aa Update ref. 2026-08-21 18:14:07 +02:00
Benoit Marty e271a6b4a8 Revert change on dependencies 2026-08-21 18:13:44 +02:00
Benoit Marty b59afe7561 No need for @SingleIn 2026-08-21 18:11:26 +02:00
Jenna Vassar 2182840a71 Let users enter their full Matrix user ID on the account provider screen 2026-08-21 08:53:23 -07:00
Benoit Marty 27b58705f3 Fix compilation issue. 2026-08-21 17:51:18 +02:00
Benoit Marty 3d9deed6e2 Remove workaround for Renovate.
It seems that it's not necessary anymore, thanks to https://github.com/renovatebot/renovate/pull/45350
2026-08-21 17:38:41 +02:00
Jorge Martín c56bb66307 Fix ClientBuilderEnterpriseHook not returning an updated ClientBuilder 2026-08-21 17:32:45 +02:00
Benoit Marty 82d17b67e4 Merge pull request #7435 from hayaksi1/fix/5623-share-unlisted-mime-type
Share a file whose mime type is not one of the supported ones
2026-08-21 16:53:48 +02:00
Benoit Marty eeaf0166f5 Merge pull request #7443 from hayaksi1/fix/4519-hide-verification-fallback
Do not render in-chat key verification requests in the timeline
2026-08-21 16:52:58 +02:00
Benoit Marty 8d3131b5ca Merge pull request #7501 from element-hq/feature/bma/updateCopyright
Update Enterprise Copyright in idea settings, so that new files get an expected header.
2026-08-21 16:52:11 +02:00
Jorge Martín b52ed580b1 Add more tests 2026-08-21 16:19:36 +02:00
Jorge Martín e3b72446cc Fix displaying the 'ready to search' state for message search after deleting the query 2026-08-21 16:10:44 +02:00
Jorge Martín 511ddb453e Bump enterprise again 2026-08-21 16:08:06 +02:00
Benoit Marty ac7b42db18 Update Enterprise Copyright in idea settings. 2026-08-21 16:07:51 +02:00
Benoit Marty 586805bcfb Merge pull request #7463 from hayaksi1/fix/4460-focused-timeline-pagination
Keep a focused timeline focused until the event has been rendered
2026-08-21 16:00:41 +02:00
Benoit Marty 441c88244f Merge pull request #7490 from element-hq/feature/bma/sendingMessageFailedReason
Show dialog with reason when user clicks on a message with a failure
2026-08-21 15:59:01 +02:00
Jorge Martín 0a624c1550 Fix empty state for room search 2026-08-21 15:51:39 +02:00
Benoit Marty 9935f91bc7 Fix wrong binding 2026-08-21 15:42:20 +02:00
hayaksi1 8a32e3d4bf Order the outgoing verification state machine imports
ktlint's import-ordering rule expects lexicographic order within the
first group, so TimeoutCancellationException has to precede delay.
2026-08-21 16:40:49 +03:00
Jorge Martin Espinosa feef42db5a Fix warning about deprecated Gradle keepRules.file API (#7498)
* Fix warning about deprecated `keepRules.file` API

The proguard files have been moved to `app/proguard` in several directories for `common`, `foss`, and `enterprise` rules and it's those directories that are added through `keepRules.addStaticSourceDirectory(dir)`.

* Use `findPropery` instead of the deprecated `project.properties["key"]` API for `allWarningsAsErrors`

* Remove very legacy .gitignore rule and restore proguard `.keep` rules
2026-08-21 13:23:34 +00:00
Jorge Martín c392737765 Try fixing the empty state for the room list search in global search 2026-08-21 15:16:26 +02:00
Jorge Martín 679d97cf4f Fix konsist issue about presenters not depending in other concrete presenter implementations 2026-08-21 15:15:48 +02:00
Jorge Martín e4891abf45 GlobalSearchPresenter doesn't need @ContributesBinding 2026-08-21 14:36:10 +02:00
Jorge Martín f28b5093df Fix licenses in enterprise 2026-08-21 13:22:50 +02:00
Jorge Martín 76b6e5857f Fix lint issues 2026-08-21 13:03:21 +02:00
ElementBot d05978cc16 Update screenshots 2026-08-21 11:00:17 +00:00
Jorge Martín b29a4c06d7 Improve previews 2026-08-21 12:43:09 +02:00
Jorge Martín 3a946475f7 Fix search results never displaying an empty state
Use the pagination state to understand when a search finished with no results
2026-08-21 12:37:47 +02:00
Benoit Marty 1300112edc Merge pull request #7453 from hayaksi1/fix/4639-system-theme
Apply the system theme when switching back to it from a forced light or dark theme
2026-08-21 12:18:52 +02:00
Jorge Martín 9a2d23c8a1 Remove unnecessary GlobalSearchPresenter.Factory 2026-08-21 12:07:38 +02:00
Benoit Marty 1186ad6f6e Merge pull request #7497 from bxdxnn/misc/allow-empty-desc
Allow empty problem description if the GitHub issue number is present for rageshakes
2026-08-21 12:06:23 +02:00
Jorge Martin Espinosa 614619e306 Make PIP react to camera orientation changes in the other participant's device (#6951)
* Make PIP react to camera orientation changes in the other participant's device

Adds a `controls.onPipMediaOrientationUpdate` callback for the WebView that will trigger a new `pipParamsBuilder.setAspectRatioFromOrientation(orientation)` method

* Fix warning about `PictureInPictureParams.Builder.setAspectRatio` only being available on API 26+
2026-08-21 09:47:49 +00:00
bxdxnn 7a3074af20 Allow empty description if GitHub issue is not empty 2026-08-21 08:46:11 +00:00
Benoit Marty a59997336d Suppress Large Class warning on test. 2026-08-21 10:17:45 +02:00
ElementBot df2ea586bd Update screenshots 2026-08-21 08:13:54 +00:00
Benoit Marty 2b3aeee1c6 Merge pull request #7494 from element-hq/feature/bma/loginWording
Move strings to the login module
2026-08-21 10:06:21 +02:00
Jorge Martín 4b32816b32 Add ClientEnterpriseHook and ClientBuilderEnterpriseHook
These allow us to not expose the enterprise internal when setting up custom configs for both `ClientBuilder` and `Client`
2026-08-21 10:05:00 +02:00
Benoit Marty 392d73826a Add instruction about screenshot test, so that AI does not try to record them. 2026-08-21 09:54:48 +02:00
Benoit Marty eb37c7bd55 Update test with Claude 2026-08-21 09:54:48 +02:00
Benoit Marty 25989135a8 Add error content to Localazy 2026-08-21 09:54:47 +02:00
renovate[bot] 069763ce54 Update metro to v1.4.2 (#7493)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-21 07:00:30 +00:00
ElementBot babed8eb1f Update screenshots 2026-08-20 18:01:33 +00:00
renovate[bot] 9aeb4c4a9f Update roborazzi to v1.72.0 (#7489)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-20 19:45:02 +02:00
Benoit Marty 3f2a1d95fc Fix issues and add missing text. 2026-08-20 19:44:05 +02:00
Benoit Marty 27d3f3dabc Add preview for the new dialog 2026-08-20 19:06:07 +02:00
Benoit Marty c10e84923b Let user see an error dialog in the case a message fails to be sent.
Closes #7470
2026-08-20 19:02:02 +02:00
Benoit Marty a890db969f Merge pull request #7488 from element-hq/feature/bma/fixColdStartDeepLink
Fix cold start with a mobile deep link
2026-08-20 17:24:30 +02:00
renovate[bot] d710b23513 Update dependency io.element.android:element-call-embedded to v0.24.0 (#7474)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-20 16:25:14 +02:00
Benoit Marty 1209d7e283 Move the test constants to the top of RootFlowNodeTest
Declaring them before the test class makes them easier to find.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 16:02:35 +02:00
Benoit Marty a6a09a8be0 Move FakeParentNode to the testutils module
The fake was declared in RootFlowNodeTest, so other tests could not use
it. Move it to the testutils module, in the node package, and make its
documentation generic.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 16:01:31 +02:00
Benoit Marty 8e4b2145d5 Move FakeNodeFactoriesBindings to the testutils module
The fake was declared in RootFlowNodeTest, so other tests could not use
it. Move it to the testutils module, in a new node package.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 15:56:39 +02:00
Benoit Marty e85c5041a4 Move NotUsedPresenter to the testutils module
The fake presenter was declared in RootFlowNodeTest, so other tests could
not use it. Move it to the testutils module, in a new presenter package,
along with MutablePresenter.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 15:51:30 +02:00
Benoit Marty d71f691d75 Move the session related classes to a session package
MatrixSessionCache and SyncOrchestrator were in the di package, which
does not describe what they do. Move them, along with their tests, to a
session package, and extract FakeSyncOrchestratorFactory from
RootFlowNodeTest so that other tests can use it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 15:41:30 +02:00
Benoit Marty be124610f0 Reuse the existing FakeOAuthActionFlow in RootFlowNodeTest
RootFlowNodeTest was declaring its own no-op fake, while the oauth test
module already provides one. Use it, and move it out of the customtab
package, since it is not specific to the custom tabs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 15:28:22 +02:00
Benoit Marty 5bdaea3f6e Move FakeAccountProviderAccessControl to the login test module
The fake was declared in RootFlowNodeTest, so other tests could not use
it. Move it next to the API it fakes, in a package mirroring the one of
the API, and let the result be provided by the caller, so that the case
where the account provider is not allowed can be tested too.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 15:20:57 +02:00
Benoit Marty 67500edafa Move FakeCacheService to the preferences test module
The fake was declared in RootFlowNodeTest, so other tests could not use
it. Move it next to the API it fakes, and let the cleared cache event
flow be provided by the caller, so that a test can emit events.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 15:17:18 +02:00
Benoit Marty f0e575e25e Move the fake entry points of RootFlowNodeTest to the test modules
The fakes were declared in RootFlowNodeTest, so other tests could not use
them. Move each of them next to the API it fakes, following the existing
convention of a createNodeResult lambda defaulting to lambdaError():

- FakeLoginEntryPoint in features/login/test
- FakeBugReportEntryPoint in features/rageshake/test
- FakeSignedOutEntryPoint in features/signedout/test (new module)
- FakeAccountSelectEntryPoint in libraries/accountselect/test (new module)

The login and rageshake test modules did not have Appyx on their
classpath, so add the architecture module to their dependencies.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 15:11:00 +02:00
Benoit Marty b3f53da7d9 Add exception for Konsist test. 2026-08-20 14:55:03 +02:00
Benoit Marty 8d4c7c868e Cleanup test 2026-08-20 14:21:53 +02:00
ElementBot f854fbd70a Update screenshots 2026-08-20 14:18:15 +02:00
Jorge Martín 8bdce7cfd6 Add tests 2026-08-20 14:18:06 +02:00
Benoit Marty d471241da8 Fix account provisioning deep link being ignored when it cold starts the app
At cold start two writers set the root nav target of RootFlowNode: the
intent path (handleIntent -> onLoginLink -> NotLoggedInFlow(params)) and
the first nav state emission (NotLoggedIn -> NotLoggedInFlow(null)).
safeRoot() only dedupes on strict equality, so the two targets do not
cancel each other out and the last writer wins, silently dropping the
login params.

Keep the params coming from the intent as pending state on the node, and
let the nav state observer take them into account, so that the root is
derived from both the logged in state and the pending login params, and
both orderings converge on the same target. The pending params are
cleared when leaving the not logged in flow, so that a later sign out
does not resurrect a stale provisioning link.

Also log when switching to the not logged in flow, since the drop was
silent. The login hint can contain an email address, so only its
presence is logged.

Closes #7482

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 13:58:03 +02:00
Jorge Martín 3ca5081f33 Integrate the new screen with the existing home screen, display it only when the search feature flag is enabled 2026-08-20 13:56:29 +02:00
Jorge Martín be2909bca2 Add a global search screen, which reads from both the RoomListSearchDataSource for the rooms search and MessageSearchService for the message search
Note the designs and texts of this screen are temporary.
2026-08-20 13:55:45 +02:00
Jorge Martín 9d36c8c4ed Export missing MessageSearchService to SessionScope 2026-08-20 13:53:49 +02:00
hayaksi1 c239190994 Merge develop into the wildcard mime type branch
Develop gained resolveImageMimeType, which reads the image header so a
share that declares a broad type is encoded as the format it really is.
Resolving the wildcard first would hand that helper a concrete image/jpeg
and make it return early, so a transparent PNG shared as image/* would be
flattened and an animated GIF would be recompressed. The header is now
read from the type the sender declared and the default subtype is applied
to whatever comes back, so the sniffing still runs and an undecodable file
no longer keeps a wildcard.

A non-image shared as image/* is therefore sent as image/jpeg rather than
image/*, which is the whole point of this branch, so the test covering
that case now expects the default subtype.
2026-08-20 14:26:57 +03:00
Benoit Marty a078851b78 Add small script to force stop an application. 2026-08-20 13:23:21 +02:00
Jorge Martin Espinosa f2883ef9df Fix flaky test in DefaultVoiceMessageComposerPresenterTest (#7487) 2026-08-20 11:49:59 +02:00
renovate[bot] ca86711ecb Update dependency io.sentry:sentry-android to v8.53.0 (#7481)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-20 11:49:21 +02:00
renovate[bot] e98d37cbcb Update dependency androidx.webkit:webkit to v1.17.0 (#7484)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-20 11:49:07 +02:00
renovate[bot] 85226228e9 Update dependency org.maplibre.gl:android-sdk to v13.5.0 (#7486)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-20 11:48:50 +02:00
ElementBot 4fa9576029 Update Gradle Wrapper from 9.7.0 to 9.7.1 (#7485)
Signed-off-by: gradle-update-robot <gradle-update-robot@regolo.cc>
Co-authored-by: gradle-update-robot <gradle-update-robot@regolo.cc>
2026-08-20 11:44:15 +02:00
hayyaksi 2525019ca1 Show the right image when opening one that has not finished sending (#7359)
* Show the right image when opening one that has not finished sending

* Reuse the timeline media gallery for events that have no event id
2026-08-20 11:16:13 +02:00
Benoit Marty 7ba29d8a5e Changelog for version 26.08.2 2026-08-20 10:40:43 +02:00
Benoit Marty 0d48b17363 Merge tag 'v26.08.2' into develop
tag
2026-08-20 10:13:06 +02:00
Benoit Marty 3ea7541628 Merge branch 'release/26.08.2' 2026-08-20 10:12:55 +02:00
Benoit Marty 9c867c9fc4 Adding fastlane file for version 26.08.2 2026-08-20 10:12:51 +02:00
Benoit Marty b774e6a280 Setting version for the release 26.08.2 2026-08-20 10:12:25 +02:00
hayyaksi 918015b8bb Stop waiting for a room alias that the remote server never resolves (#7446)
Tapping a room alias whose server is down left the resolver screen on a
spinner for as long as the screen stayed open, with no way forward other
than going back.

Bound the resolution with the ten second timeout already used when
joining a room by address, so the retry dialog is shown instead. The fake
client's resolve lambda becomes suspending so a server that never answers
can be simulated.
2026-08-20 09:34:15 +02:00
hayyaksi 48f26ae33e Do not open the room again when the app is resumed from the recents list (#7436)
MainActivity is a singleTask activity that declares the elementx://open deep
link, so tapping a notification makes that deep link the base intent of the
task. When the user later brings the app back from the recents list, the
system replays that base intent with FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY, the
resolver turns it into a navigation again and the user is pushed back into the
room they had already left.

Ignore any intent the system replayed from the history, the same way the
launcher intent is already ignored.
2026-08-20 09:31:25 +02:00
renovate[bot] b366515d11 Update dependency androidx.compose:compose-bom to v2026.08.00 (#7483)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-20 08:59:00 +02:00
hayyaksi c362bca527 Keep the send button when the composer is emptied while editing a message (#7456)
* Keep the send button when the composer is emptied while editing

rememberEndButtonParams checked whether the composer had sendable text
before it checked whether the user was editing a message, so clearing
the text of a message being edited replaced the send button with the
voice recorder button. Starting a recording from there abandons the
edit.

Check the editing branch first, as the formatting composer already does,
and let it render a disabled send button when there is nothing to send.

* Update screenshots
2026-08-20 08:55:50 +02:00
hayyaksi 2432b0ccc6 Keep playing a voice message when another app plays a short sound (#7454)
* Request media audio focus when playing a voice message

Voice message playback asked for audio focus with
USAGE_VOICE_COMMUNICATION, the same bucket as Element Call and voice
message recording, while the player itself renders on the media stream.
Audio policy layers act on the declared usage, so a car head unit
switches to its call audio context and routes the playback away while
the player keeps running.

Declare USAGE_MEDIA (and STREAM_MUSIC on the legacy path) for playback,
which is what the player actually renders. Recording and Element Call
keep asking for voice communication focus.

* Keep playing a voice message through a short sound from another app

A notification, a navigation prompt or any other app taking focus for a
moment sends AUDIOFOCUS_LOSS_TRANSIENT or AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK,
and DefaultMediaPlayer pauses on it. The voice message stops part way
through and the user has to find it again and restart it.

Recording already ignores those two, because the capture path is not
affected by focus. Give voice message playback the same treatment, moving
the requester test into a named function rather than growing the inline
condition. A permanent AUDIOFOCUS_LOSS, a real phone call, still pauses,
and the media viewer is untouched.
2026-08-20 08:55:03 +02:00
bxdxnn 2ff6a56e7d Use the new SDK thread info for redacted messages 2026-08-19 16:12:15 +00:00
Benoit Marty 42f9e15641 Move strings to the login module 2026-08-19 17:18:16 +02:00
Benoit Marty 1c94e89ee8 Merge pull request #7394 from element-hq/feature/account-provider-selection
Simplify choosing the account provider during sign in, with autocomplete of previously used ones
2026-08-19 17:06:47 +02:00
Benoit Marty 92595e1532 Merge pull request #7427 from element-hq/renovate/media3
Update media3 to v1.11.0
2026-08-19 16:26:29 +02:00
ElementBot f2114d94f1 Update screenshots 2026-08-19 14:05:29 +00:00
Benoit Marty bbaea49114 Merge pull request #7479 from element-hq/renovate/org.matrix.rustcomponents-sdk-android-26.x
Update dependency org.matrix.rustcomponents:sdk-android to v26.08.19
2026-08-19 15:19:33 +02:00
Benoit Marty fb614cb90a Merge pull request #7478 from element-hq/feature/bma/configurePusherAppId
Make the PUSHER_APP_ID configurable at build time.
2026-08-19 15:07:19 +02:00
renovate[bot] a70cd18563 Update dependency org.matrix.rustcomponents:sdk-android to v26.08.19 2026-08-19 12:21:52 +00:00
Benoit Marty 7adbd3941a Make the PUSHER_APP_ID configurable at build time. 2026-08-19 14:20:31 +02:00
hayyaksi 7341feeed8 Model the fake voice recorder on a cancellable recording job (#7466)
* Model the fake voice recorder on a cancellable recording job

FakeVoiceRecorder pushed a whole recording synchronously from a single
suspending call, which made it both weaker and stricter than
DefaultVoiceRecorder.

Every level emission carried the same elapsed time and the emissions were
coalesced by Molecule, so no intermediate recording state was observable and
the level-limit test had to skip half the emissions to compensate. Each level
now advances the test time source and suspends for its share of the recording
duration, so the states arrive one at a time and that magic number is gone.

Call verification moved to the constructor lambdas the rest of the repo uses,
in place of hand-rolled counters and assertCalls. The counters discarded the
argument, so nothing pinned that cancelling a recording stops it with
cancelled = true; that is now asserted. Truth is no longer needed by the
fixture module.

A second startRecord() no longer raises an error where production only warns
and returns, and stopping or deleting terminates an in-flight emission loop
the way cancelling the real recording job does, so a stale recording state can
no longer arrive after the recording has finished.

* Keep each recording's level loop to its own recording

The level loop checked a single isRecording flag, which cannot tell "this
recording was cancelled" from "a later recording has started". After a cancel
followed by a new start, the first loop resumed, saw the flag set again, and
advanced the shared test time source, so the new recording reported the elapsed
time of both. Each recording now carries an id and its loop stops as soon as
that id is no longer the active one.
2026-08-19 13:44:11 +02:00
ganfra cf2ce2bd65 Use Profiles sliding sync extension check for own profile subscription (#7403)
* Use Profiles sliding sync extension check for own profile subscription

---------

Co-authored-by: Jorge Martin Espinosa <jorgem@element.io>
2026-08-19 13:00:27 +02:00
hayyaksi 6bd841d838 Close the invite screen once the invite has been declined (#7440)
Declining an invite from the full screen invite left the user on that
same screen, so they had to press back themselves to get to the room
list. Both decline paths are affected: the plain decline handled by
AcceptDeclineInviteView, and the decline and block sub screen, which
navigated up into the join room flow instead of out of it.

Navigate up from the join room flow on a successful decline, and give
DeclineInviteAndBlockEntryPoint a callback so the sub screen can report
a successful decline separately from a back press. The room list flow
supplies the same callback, popping the sub screen once the invite has
been declined.
2026-08-19 12:52:11 +02:00
Benoit Marty 03c69be722 Merge pull request #7477 from element-hq/feature/bma/normalizeUserAgent
Normalize the applicationName used in user agent
2026-08-19 12:35:56 +02:00
hayyaksi ee1348e19e Give the room name in the timeline top bar a 48dp tap target (#7450)
* Give the room name in the timeline top bar a 48dp tap target

The row carrying the room avatar and name is the control that opens the
room details, but it is only as tall as its content, which is under the
48dp minimum recommended for a touch target. Constrain it to a minimum
height of 48dp. The children stay centred inside the app bar, so nothing
moves visually.

* Update screenshots

The accessibility snapshot of the timeline is the only one the change
affects: the node which opens the room details now reports a 48dp tall
bounding box. The room name, the avatar and the icons stay where they
were.
2026-08-19 12:29:24 +02:00
Benoit Marty ff89302cf5 Normalize the applicationName used in user agent, as HTTP spec does not allow UTF-8 chars in headers. 2026-08-19 11:56:34 +02:00
Jorge Martin Espinosa a1e86ad499 Use the right base URL for the ESS config/well known endpoints (#7469)
* Rename `UrlContentFetcher` to `ClientUrlContentFetcher` and add missing base URL data (server name, homeserver URL)

* Avoid passing the `homeserverUrl/serverName` around.

Calculate the URL in `DefaultRemoteEnterpriseConfigRetriever` using the newly added `homserver` and `server` values as the possible base URLs, depending on the remote config source

* Make sure we don't perform blocking requests to fetch the remote config when restoring a session

Add a `needsRefresh` parameter to `RemoteEnterpriseConfigResult.NotFound` to handle this, and store a `no-config` value in the store to indicate an endpoint has been checked and no config was found

* Bump enterprise module
2026-08-19 09:24:22 +02:00
Jenna Vassar 891c077786 Merge remote-tracking branch 'origin/develop' into feature/account-provider-selection 2026-08-18 17:49:31 -07:00
Jenna Vassar 2e2bf71ad5 Make ensureProtocol case-insensitive 2026-08-18 17:03:40 -07:00
Jenna Vassar 1b2d5b9c92 Complete account providers without the https scheme and include the enterprise allow-list 2026-08-18 17:03:39 -07:00
Jorge Martin Espinosa c31d7e6cce Don't restart the sync service if the client is shutting down (#7472)
* Don't restart the sync service if the client is shutting down

This was causing issues when clearing the cache:

1. The client would ask the SDK to clear the caches.
2. The SDK would stop the sync service, clear the caches and set the `pos` value of sliding sync to 0 so the HS returns all the required state the next time the client is used.
3. However, between the sync service being stopped with the `pos=0` change and the client being destroyed, there was enough time for the `SyncOrchestrator` to decide the sync service was stopped for no good reason since there is network connectivity and the app is on foreground, so it restarted the sync, with `pos=0`.
4. This value was persisted, and the next time the new client did an 'initial sync' it had a `pos != 0`, which the HS uses to avoid returning some required state it already fetched.
5. The room list now lacks state events for lots of rooms and room names, avatars, members, etc. are now missing.

---------

Co-authored-by: Benoit Marty <benoitm@element.io>
2026-08-18 16:50:57 +00:00
Benoit Marty 32416581d4 Merge pull request #7473 from element-hq/feature/bma/improveBuildTimeConfig
Improve build time config and update the Firebase configuration
2026-08-18 18:21:57 +02:00
Benoit Marty 1fefbc9943 Firebase configuration location has changed. 2026-08-18 17:39:21 +02:00
Benoit Marty 07983221e6 Move google_app_id to resource files. 2026-08-18 16:25:57 +02:00
Benoit Marty ccebb55ca7 Improve documentation 2026-08-18 15:46:24 +02:00
Benoit Marty 815b2a895e Reorder items and specify type explicitly so that diff is minimal when comparing to a generated file for a variant build. 2026-08-18 11:18:18 +02:00
Benoit Marty dfbc75a9e1 Merge pull request #7428 from element-hq/feature/bma/timeoutInReadyToScanScreen
[LinkNewDevice] Ensure the notice screen is also protected by the timeout.
2026-08-18 10:07:36 +02:00
Benoit Marty 3c21af2a57 Merge pull request #7437 from hayaksi1/fix/4594-leave-room-not-found
Show an error when the room to leave cannot be found
2026-08-18 10:06:01 +02:00
Benoit Marty c3442d3a03 Merge pull request #7439 from hayaksi1/fix/5153-sanitize-carriage-return
Exclude carriage returns from the login and password fields
2026-08-18 10:05:40 +02:00
Benoit Marty 378fbfa1d8 Merge pull request #7441 from hayaksi1/fix/5978-caption-markdown
Convert markdown to HTML when a caption is edited
2026-08-18 10:05:19 +02:00
Benoit Marty a54b624cd7 Merge pull request #7442 from hayaksi1/fix/6429-remove-inplace-fix
Remove the F-Droid dex patching step from the release script
2026-08-18 10:04:56 +02:00
Benoit Marty 25094cbd06 Merge pull request #7445 from hayaksi1/fix/2617-user-id-with-slash
Open the user profile of a user id that contains a slash
2026-08-18 10:04:27 +02:00
Benoit Marty 3ea36d54b4 Merge pull request #7451 from hayaksi1/fix/7059-relax-image-compression
Relax image compression so photos and screenshots keep more detail
2026-08-18 10:03:36 +02:00
Benoit Marty a36ccbaae2 Merge pull request #7455 from hayaksi1/fix/4582-autoscroll-focused-field
Keep the focused login field visible above the keyboard
2026-08-18 10:03:22 +02:00
hayaksi1 0aa283729c Cover every branch of the forward pagination gate
Codecov reported 55.55% patch coverage on the previous commit: four of the nine
measured lines were only partially covered. No test paginates forwards in a
thread timeline, and the focus states Requested, Loading and Failure were never
live when a forward pagination arrived, so only the Success branch of
isPending() was ever evaluated both ways.

Add a test per uncovered state: a forward pagination is deferred while the focus
request is still being debounced and while the focused timeline is being
created, it is performed again once the request has failed, and it is dropped in
a thread timeline while a backward one still goes through.

The pagination recorder now also sits on the live timeline, and the presenter is
built on that same timeline. Before the detached timeline exists, a pagination
that escaped the gate would go to the live one, so without this the new guards
would pass against the unfixed presenter. The deferred cases end on the emitted
state rather than on cancelAndIgnoreRemainingEvents for the same reason: the
recorder throws from inside the presenter coroutine and cancelling swallows it.

Verified by reverting the gate: the three deferred-pagination tests then fail
with "This lambda should never be called."
2026-08-18 00:49:03 +03:00
hayyaksi cb6ac751cc Refresh the room members when a mention is started (#7438)
* Refresh the room members when a mention is started

The mention suggestions read room.membersStateFlow, which is a cache filled
only by an explicit updateMembers() call. Nothing calls it while a room is
open, so a member who joined after the room was opened never appears in the
suggestion list until the room is left and entered again.

Call updateMembers() when a mention search starts. The trigger is
distinctUntilChanged on "the current suggestion is a mention", so it fires
once per mention rather than on every keystroke, and fetchRoomMembers already
no-ops while a fetch is in flight.
2026-08-17 21:10:08 +02:00
hayyaksi 75607c73ec Keep transparency when an image is shared into the app with an imprecise mime type (#7452)
* Resolve the real image format before choosing the encoder

The upload pre-processor picks the encoder from the mime type it is
given. When an image is shared from another app that type is often
broader than the image really is, so a transparent PNG shared as a
wildcard image type is re-encoded as JPEG and its alpha channel is
flattened to black.

Read the format from the image header when the declared type is not
already a concrete PNG or JPEG, and use that to decide both whether the
image can be compressed at all and which encoder to use.

* Cover the mime type fallbacks the wildcard resolution introduced

resolveImageMimeType now decides whether an image is compressible, so two of
its outcomes had no test: a GIF shared with a wildcard type has to be
recognised and left uncompressed rather than recompressed into a still, and a
file the decoder cannot read at all has to fall back to the type it was shared
with.

The isMimeTypeImage() guard on the decoded type is dropped with them. The
BitmapFactory decoders only ever report an image type in outMimeType, so the
guard could not fire and there was no way to exercise it.
2026-08-17 21:08:48 +02:00
renovate[bot] e6e93c9bd0 Update dependency org.matrix.rustcomponents:sdk-android to v26.08.17 (#7460)
* Update dependency org.matrix.rustcomponents:sdk-android to v26.08.17

* Fix API breaks:

- `ClientBuildException.WellKnownLookupDisabled` should be handled now.
- `Client.sendReply` returns a `Result<SendHandle>` now. We can ignore it like we do for `Client.send`.

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jorge Martín <jorgem@element.io>
2026-08-17 21:06:28 +02:00
Jenna Vassar 7c48e737c4 Merge remote-tracking branch 'origin/develop' into feature/account-provider-selection 2026-08-17 11:18:46 -07:00
Jenna Vassar a6c7042482 Configure the account provider homeserver only once when signing in 2026-08-17 10:37:27 -07:00
Jenna Vassar bbd6ce134b Fix account provider dropped on first Continue and address review comments 2026-08-17 10:31:17 -07:00
Benoit Marty ad502c07ef Merge branch 'develop' into fix/1270-verification-request-timeout 2026-08-17 18:49:02 +02:00
hayaksi1 03c330ee07 Rename PreviewParameterProvider subclasses to the PreviewParam suffix
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.
2026-08-17 17:40:33 +03:00
Benoit Marty 1f7635f0e3 Fix API break 2026-08-17 15:56:59 +02:00
hayaksi1 8e02f03543 Cover the branches left untested by the uri short circuit
An intent carrying uris is now accepted before the mime type list is
reached, so the image check that every uri share used to exercise is only
covered by an intent which has nothing to send. Add one case per listed
mime type without a uri.

Cover the rest of the handler while it is under test: an intent with no
resolvable type, a send multiple without a stream, an action which is
neither send nor send multiple, an empty caption, a plain text intent
without text, the mime type reported by the content resolver for the uri,
and the explicit intent built for the application which will read it, both
when the read permission can be granted and when it cannot.
2026-08-17 16:35:31 +03:00
Jorge Martin Espinosa 8b2c207728 Move the :libraries:wellknown logic into enterprise modules (#7429)
* Extract `IsEnterpriseBuild` use case

Having it inside `EnterpriseService` made having DI cycles with the `BuildMeta` provider too easy

* Rename `EnterpriseService.defaultHomeserverList()` to `homeserverAllowList()`

This seems more aligned with the way this list works

* Move most of the logic from `:libraries:wellknown` to `:enterprise:feature:enterprise`, both `shared` and `impl` modules

Ideally I would have added everything into `impl`, but some of the classes needed to be shared between the different `:enterprise:features` modules, and those are in the `shared` module, lacking a better name/strategy

* Add `EnterpriseService.isElementProEnforced`

With this, we don't need to provide the `RemoteEnterpriseConfig` anymore to any consumers, everything goes through `EnterpriseService` or other providers

* Remove `EnterpriseService.overriddenElementWellKnown`

This leaks the remote enterprise config/element well known contents to the app in a public interface.

* Create `CustomMapTilerConfigProvider` to avoid leaking the `RemoteEnterpriseConfig` in public APIs

This commit also starts moving some of the well-known related values to the enterprise modules

* Add missing docs to `EnterpriseService` and `SessionEnterpriseService`

* Fix tests

* Fix lint issues

* Bump enterprise module
2026-08-17 13:21:47 +00:00
hayaksi1 11e3cb9b19 Keep a focused timeline focused until the event has been rendered
Opening a room from a notification asks the timeline to focus the notified
event. When that event is outside the loaded live window the controller
creates a detached timeline for it, and a detached timeline always carries a
forward loading indicator as its last chronological item. The items list is
reversed for display, so that indicator is the bottom-most row and is composed
on first layout, which unconditionally emits a forward pagination request
nobody asked for. Because a notified event sits close to the live end, that one
page reaches the end, and the controller responds by closing the detached
timeline. The live window replaces the item list while it is still anchored at
the bottom, so the user lands on the newest message and the focus request is
discarded.

Defer automatic forward pagination while a focus request is still pending. Once
the focused event has been rendered the viewport has moved off the indicator
row, so only a real scroll back to the live end switches to the live timeline,
which is what the controller's existing behaviour is for. Backward pagination
and the explicit jump-to-live action are untouched.
2026-08-17 16:20:43 +03:00
Cipherd f94566e44f Fix outgoing session verification stuck with incomplete recovery setup (#7433) 2026-08-17 15:14:45 +02:00
hayyaksi 12791406ca Explain a rate limiting push gateway in the notification troubleshooter (#7448)
* Explain a rate limiting push gateway in the notification troubleshooter

The push loop back test printed whatever HTTP status the gateway
answered, so a user throttled by the public ntfy.sh gateway was told
"Error: HTTP 507", which reads as a server fault rather than as a quota
they can wait out or move away from.

Map the two statuses that mean the notification was throttled onto a
typed failure and give the troubleshooter a message that says what to do.

* Import strings from localazy, remove `temporary.xml` in `:libraries:push:impl`

---------

Co-authored-by: Jorge Martín <jorgem@element.io>
2026-08-17 15:14:15 +02:00
hayaksi1 98ef737b1d Rename withDefaultSubtype to ensureDefaultSubtype
The old name read as though the default subtype always replaced whatever the
receiver carried, when a mime type that already has a concrete subtype is
returned untouched.
2026-08-17 15:52:46 +03:00
Benoit Marty 98c3067514 Merge pull request #7432 from element-hq/renovate/org.maplibre.compose-maplibre-compose-0.x
Update dependency org.maplibre.compose:maplibre-compose to v0.14.0
2026-08-17 14:50:27 +02:00
hayaksi1 5bfb614bdb Restore the UnusedImports suppression on the outgoing verification test
ktlint reports the TestScope import as unused because the type only appears in
a context parameter, which the rule does not look at. The file carried a
file-level suppression for exactly that before this branch dropped it.
2026-08-17 15:37:11 +03:00
Benoit Marty 5a71cc31e8 Merge pull request #7461 from element-hq/feature/bma/syncStrings
Sync strings - New language supported: Arabic
2026-08-17 14:30:02 +02:00
Benoit Marty 054d592391 Sync again 2026-08-17 12:10:39 +02:00
Benoit Marty 5ac1fe2409 Remove temporary string. 2026-08-17 12:10:18 +02:00
Benoit Marty a740423343 Sync strings. 2026-08-17 11:34:52 +02:00
hayaksi1 932a4d013c Relax image compression so photos keep more detail
Images are downscaled against a 640px reference, which makes inSampleSize
halve the source one step further than the documented intent: the KDoc
says images should be sent at up to 1280x1280, but a 12000x3916 photo is
sent as 3006x979. Text in screenshots and detail in photos is lost at
that size.

Raise the reference to 1280. inSampleSize is a power of two, so this
moves each image one step up rather than to an exact size.
2026-08-17 11:33:41 +03:00
hayaksi1 9a4daa19b2 Keep the focused login field visible above the keyboard
On shorter screens the software keyboard can cover the field the user is
typing into on the login and account provider screens, because nothing
scrolls the content back into the reduced viewport once the keyboard is
shown.

Apply the existing bringIntoViewOnImeVisible modifier to those fields,
the same way the recovery key screen already does.
2026-08-17 11:33:10 +03:00
hayaksi1 fcecc7acba Give up on a verification request the other session never accepts
The outgoing verification flow had no client side timeout: if the other
session never answered, the screen waited on "Waiting to accept request"
for as long as the user left it open, with no indication that the
request could no longer succeed.

Bound the wait to the ten minutes the Matrix specification gives a
verification request. Past that the other session can no longer accept
it, so cancel the request and move to the canceled state, which already
offers to start again.
2026-08-17 11:33:10 +03:00
hayaksi1 ddd89f46df Resolve the System theme from the application configuration
Selecting a Light or Dark theme makes AppCompatDelegate rewrite the
activity configuration to the forced night mode. isSystemInDarkTheme
reads that same configuration, so switching back to System kept showing
the previously forced theme.

Read the night mode from the application configuration instead, which
AppCompat leaves untouched and itself uses to expand
MODE_NIGHT_FOLLOW_SYSTEM. The activity configuration is still used as
the recomposition key so a system-wide theme change is picked up.
2026-08-17 11:33:09 +03:00
Benoit Marty 2ac11ebe14 Clarify the comments 2026-08-17 10:30:57 +02:00
Benoit Marty d0a1011638 Add documentation on LinkNewDesktopHandler and LinkNewMobileHandler 2026-08-17 10:06:26 +02:00
Benoit Marty aa6f70530a [LinkNewDevice] Move the timeout management when linking a new desktop device to the LinkNewDeviceFlowNode so that it can also be used for the DesktopNotice screen. 2026-08-17 10:06:25 +02:00
renovate[bot] 1a3b89de12 Update dependency io.github.sergio-sastre.ComposablePreviewScanner:android to v0.9.2 (#7431)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-17 07:33:18 +00:00
hayaksi1 4c5baf8d60 Send a concrete mime type for media shared with a wildcard one
Some apps share an image with the mime type image/* rather than a
concrete one. That value was carried straight into the uploaded event, so
the message reached the room with an unusable media type and bridges
rejected it as unsupported.

Resolve a wildcard subtype to a default for its top level type before
processing the upload, reusing the mapping the download path already
applies so the SDK can pick the right extension.
2026-08-17 04:01:05 +03:00
hayaksi1 7dcd55ee08 Hide the encryption badge when key storage is set up on the server
The settings badge was shown for every unknown backup state, so a session
whose key backup lives on the server but has not been reported by the SDK
kept a red dot next to Encryption while the Encryption screen itself
showed nothing to do.

Resolve an unknown backup state the way the Encryption screen already
does, by asking whether a backup exists on the server, and only badge the
entry when there is none.
2026-08-17 03:55:09 +03:00
hayaksi1 c5a878249b Open the user profile of a user id that contains a slash
A slash is valid in the local part of a user id, but in a permalink it
also separates the identifier from an event id, so the SDK could not tell
the two apart and the link fell back to opening matrix.to in a browser.

Only a user id can start with an @, so recover it before handing the URI
to the SDK, and only when the identifier really contains a slash so every
other permalink keeps taking the same path.
2026-08-17 03:48:44 +03:00
hayaksi1 39afaa1fe4 Do not render in-chat key verification requests in the timeline
A m.key.verification.request sent from a client that supports in-chat
verification carries a plain text body for clients that do not. Element X
has no msgtype of its own for it, so it fell through to OtherMessageType
and the timeline printed that body, telling the user their client does
not support in-chat key verification right after a verification they had
just completed.

Drop the event when building the timeline items, as Element X iOS does.
2026-08-17 03:44:53 +03:00
hayaksi1 999bee2420 Remove the F-Droid dex patching step from the release script
fix-pg-map-id.py rewrites the pg_map_id of a dex file, but the dex
format changed and its regex no longer matches anything, so the step is
a no-op and the F-Droid build is reproducible without it. inplace-fix.py
only existed to drive it; none of its other commands are used here.

The removed call also re-ran zipalign -P 16 on each APK. AGP aligns the
release APKs at packaging time and apksigner is already invoked with
--alignment-preserved, so the signed output stays 16 KB aligned.
2026-08-17 03:40:53 +03:00
hayaksi1 537842bb40 Convert markdown to HTML when a caption is edited
Adding or editing a media caption from the plain composer sent the
caption as text only: the composer has no HTML to pass, and the media
caption edit was built by hand with a null formatted body, so the
markdown was never rendered. Sending a caption with the same markdown
does render it, because the SDK builds the formatted body there.

Build the edit with the SDK's createCaptionEdit, which runs the caption
through the same markdown conversion the send path uses.
2026-08-17 03:40:11 +03:00
hayaksi1 c901b902b1 Exclude carriage returns from the login and password fields
The sanitize() helper applied to the login, password and account deactivation
password fields promises to remove "any new line characters" but only removes
LF. A value pasted from a source with CRLF endings keeps its CR, which is
invisible in the field and is sent to the homeserver as part of the password,
so the login is rejected while typing the same characters by hand works.

Drop every line separator instead of LF only, in both copies of the helper.
Whitespace that can legitimately be part of a password, such as spaces and
tabs, is left untouched.
2026-08-16 22:44:48 +03:00
hayaksi1 04dcc14eaa Show an error when the room to leave cannot be found
showLeaveRoomAlert ran its whole body inside client.getRoom(roomId)?.use { }.
When getRoom returned null, which happens for a room the client can no longer
resolve, the body was skipped without writing any state: leaveAction stayed
Uninitialized, so no confirmation dialog and no error was displayed and
Leave room appeared to do nothing at all.

Handle the missing room explicitly and surface it as a failure, which
AsyncActionView already renders with the generic error dialog. leaveRoom used
a !! for the same lookup, so it reported the miss as a NullPointerException;
it now throws the same named exception.
2026-08-16 22:44:29 +03:00
hayaksi1 2c0da1cadf Share a file whose mime type is not one of the supported ones
handleIncomingShareIntent only accepted an intent whose resolved mime type
started with image/, video/, audio/, application/, file/, text/ or */. An
ACTION_SEND carrying EXTRA_STREAM with any other top level type, such as the
message/rfc822 that some apps use when attaching a file, fell through to null
and the share was dropped before the room picker ever appeared, even though
the manifest filter accepts */*.

The uris are extracted before that check, so their presence is enough to know
the intent carries something to send. Accept the intent when it has uris and
keep the mime type list for the case where it does not.
2026-08-16 22:44:11 +03:00
renovate[bot] 45057b35a5 Update dependency org.maplibre.compose:maplibre-compose to v0.14.0 2026-08-15 09:07:16 +00:00
Benoit Marty 23b75c3423 Fix outdated documentation. (#7430) 2026-08-15 11:05:43 +02:00
ElementBot 2c4cf090cb Update screenshots 2026-08-14 18:27:41 +00:00
bxdxnn 0b0124708d Add the new test to the list 2026-08-14 18:05:32 +00:00
Jenna Vassar f488da890b Update the maestro sign-in flow for the consolidated account provider screen 2026-08-14 08:57:37 -07:00
Benoit Marty e1adc938fd Merge pull request #7424 from element-hq/revert-7378-fix/6853-remember-revealed-images
Revert "Keep manually revealed media visible when re-entering a room"
2026-08-14 17:37:31 +02:00
Jenna Vassar 54ba43a8ec Use the correct license header on the new account provider history files 2026-08-14 08:12:51 -07:00
Jenna Vassar 89e2abaeeb Merge develop into feature/account-provider-selection 2026-08-14 07:51:17 -07:00
bxdxnn 67b48ae184 Fix build failure 2026-08-14 12:55:53 +00:00
renovate[bot] ea076ecc4b Update media3 to v1.11.0 2026-08-14 11:48:17 +00:00
Benoit Marty 9e32801d02 Merge pull request #7426 from element-hq/feature/bma/documentation
Add KDoc to all the interfaces of the api modules
2026-08-14 13:46:28 +02:00
Benoit Marty 7d9f4b9af4 Fix outdated documentation. 2026-08-14 12:22:37 +02:00
Benoit Marty 1ab401482a Fix formatting issues 2026-08-14 12:20:49 +02:00
renovate[bot] 2c94cee575 Update dependency org.matrix.rustcomponents:sdk-android to v26.08.13 (#7420)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-14 12:18:43 +02:00
Benoit Marty b74b146917 Add KDoc to the single-interface library and service API modules
Cover the last remaining interfaces across libraries/audio, libraries/cachestore,
libraries/indicator, libraries/slashcommands, libraries/voiceplayer and
services/apperror, with their members and parameters.

This completes the effort: all 222 in-scope interfaces and their 820 members in
the 60 api modules that hold any now carry documentation.

Notable points: CacheStore applies no expiry of its own and only records when each
entry was written, leaving staleness to the caller; SlashCommandService.parse
never throws, returning NotACommand for ordinary text and an error case for a
malformed command.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 12:04:40 +02:00
Benoit Marty 9822b3d4f7 Add KDoc to the single-interface feature API modules
Cover the last remaining interfaces across features/announcement, cachecleaner,
call, contentscanner, createroom, knockrequests, lockscreen, rolesandpermissions,
startchat, userprofile and viewfolder, with their members and parameters.

Grouped into one commit rather than eleven near-empty ones, per the AGENTS.md rule
about tiny commits.

This completes every features/* api module.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 12:00:42 +02:00
Benoit Marty 1b4d6c07fa Add KDoc to the authentication, logout, preferences and share API interfaces
Cover the remaining interfaces of features/login, features/logout,
features/preferences and features/share, along with their members and parameters.

Points recorded here: AccountProviderAccessControl returns true on a build with no
enterprise restriction, ExtraDeveloperOptionsRenderer is bound to a no-op in the
standard build, DirectLogoutView draws nothing until a logout event is sent, and
CacheService exists because clearing a cache destroys the client and the app has
to react.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 11:53:37 +02:00
Benoit Marty fa123d941b Add KDoc to the features/invite API interfaces
Document AcceptDeclineInviteEvents, AcceptDeclineInviteView and the
SeenInvitesStore interface.

The view doc explains why it is a shared component rather than part of one screen:
it is embedded everywhere an invitation can be acted on, so the confirmation
dialogs behave identically in the invite list and in a room preview.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 11:51:32 +02:00
Benoit Marty d713017afe Add KDoc to the libraries/cryptography API interfaces
Document the members of SecretKeyRepository and EncryptionDecryptionService.

The caveat worth having in the API doc comes from KeyStoreSecretKeyRepository:
requiresUserAuthentication only takes effect when the key is created, so an
existing key keeps whatever protection it was created with.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 11:51:32 +02:00
Benoit Marty b683c62d8f Add KDoc to the services/appnavstate API interfaces
Document the members of AppNavigationStateService and AppForegroundStateService
that had none, and add the missing @param entries.

The recurring `owner` parameter is now explained, from
DefaultAppNavigationStateService: the navigation state is a stack, pushing a level
without its parent logs an error and does nothing, and a leave call whose owner
does not match the current level is silently ignored so that a node which has
already been replaced cannot pop someone else's state.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 11:50:13 +02:00
Benoit Marty c4c7dac9cf Add KDoc to the features/rageshake API interfaces
Document RageshakeFeatureAvailability, LogFilesRemover and the BugReporter
interface, and complete the docs of its remaining members.

The BugReporter doc records that progress and the outcome arrive through the
listener passed to sendBugReport rather than through a return value or an
exception.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 11:48:59 +02:00
Benoit Marty 57658640e1 Add KDoc to the features/location API interfaces
Document LocationService, StaticMapUrlBuilder, TileServerStyleUriBuilder and the
members of ActiveLiveLocationShareManager that had none.

The two URL builders now say what happens when no map provider is configured for
the build, which is the case LocationService.isServiceAvailable exists to guard.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 11:48:58 +02:00
Benoit Marty 2284cff25a Add KDoc to six small library API modules
Cover the remaining interfaces of libraries/troubleshoot, libraries/matrixmedia,
libraries/dateformatter, libraries/featureflag, libraries/workmanager and
libraries/mediaviewer, along with their members and parameters.

Behaviours worth recording: NotificationTroubleshootTest reports its outcome
through its state flow rather than by returning, and its quickFix throws unless
the test offers one; DateFormatter.format turns a null timestamp into an empty
string so callers need no null check; ImageLoaderHolder keeps one loader per
session because decrypting room media needs that session's client.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 11:43:40 +02:00
Benoit Marty d80aaf6be4 Add KDoc to the libraries/mediapickers API interfaces
Document PickerProvider and each of its register methods.

The interface doc carries the two rules that apply to all of them: they must be
called during composition rather than from a click handler, and a cancelled pick
arrives as a null uri or an empty list rather than as an error.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 11:24:05 +02:00
Benoit Marty 13aad55af8 Add KDoc to the libraries/pushproviders API interfaces
Document PusherSubscriber and PushHandler at the interface level, document the
PushProvider members that had none, and add the missing @param entries.

Notable points: getCurrentDistributor also returns null when the stored
distributor is no longer installed, rotateToken throws unless canRotateToken is
true, and handleInvalid records the payload in the push history for diagnosis.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 11:24:04 +02:00
Benoit Marty 012d43665f Add KDoc to the libraries/session-storage API interfaces
Document SessionStore, SessionObserver and SessionListener at the interface level
and add the missing @param entries.

The store doc records that sessions are ordered by last usage, which is what makes
"the latest session" meaningful across the rest of the interface, and the listener
doc explains that wasLastSession is the signal to clean up app-wide data too.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 11:24:04 +02:00
Benoit Marty c32f81f696 Revert "Keep manually revealed media visible when re-entering a room" 2026-08-14 10:49:24 +02:00
Benoit Marty 4a5d0d2540 Add KDoc to the services/analyticsproviders API interfaces
Document AnalyticsProvider, AnalyticsTransaction, AnalyticsTracker and
ErrorTracker at the interface level, and add the missing @param entries.

The provider doc records that providers are driven by the analytics service and
only receive data once the user has consented, and that startTransaction returns
null on a provider with no performance support.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 10:31:34 +02:00
Benoit Marty aa7dc4fd0d Add KDoc to the services/toolbox API interfaces
Add the interface level docs that StringProvider, SystemClock and
BuildVersionSdkIntProvider were missing, document ExternalIntentLauncher.launch,
and add the remaining @param entries.

Each doc says why the abstraction exists rather than restating the signature:
these wrap Context, Build.VERSION and the clock so that presenters can be tested
without them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 10:31:33 +02:00
Benoit Marty c561674221 Add KDoc to the libraries/oauth API interfaces
Document OAuthIntentResolver and OAuthActionFlow along with their members.

OAuthActionFlow now explains why it exists rather than the result simply being
returned: the activity that receives the redirect and the login screen waiting for
it are not in the same navigation subtree.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 10:28:56 +02:00
Benoit Marty 1f3c73d3c6 Add KDoc to the libraries/usersearch API interfaces
Document UserRepository and UserListDataSource along with their members.

Two behaviours from MatrixUserRepository and MatrixUserListDataSource are recorded
because callers rely on them: the data source swallows a failed directory request
into an empty list, and a query that is a well-formed user id other than the
current user's yields that user even when the directory does not know them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 10:28:56 +02:00
Benoit Marty 6446b30255 Add KDoc to the libraries/eventformatter API interfaces
Document RoomLatestEventFormatter, TimelineEventFormatter and
PinnedMessagesBannerFormatter along with their members.

The formatters return a nullable CharSequence, so each one now states what null
means: the event is not rendered as text at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 10:28:48 +02:00
Benoit Marty c0848cee9d Add KDoc to the libraries/deeplink API interfaces
Document DeeplinkParser, DeepLinkCreator and InviteFriendsUseCase, including that
DeepLinkCreator.create silently ignores the deeper arguments once an outer one is
null.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 10:28:47 +02:00
Benoit Marty 055fe408a6 Add KDoc to the features/roommembermoderation API interfaces
Document RoomMemberModerationState, RoomMemberModerationEvents and
RoomMemberModerationRenderer along with their members and parameters.

The renderer doc states that it draws nothing until the host screen asks for a
member's actions, and that onSelectAction exists so the host can navigate for the
actions that need a screen of their own.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 10:06:53 +02:00
Benoit Marty 50214fe01a Add KDoc to the features/poll API interfaces
Document PollContentStateFactory, SendPollResponseAction and EndPollAction along
with their members and parameters.

The two action interfaces exist to wrap a plain timeline call, so their docs say
what they add: both capture an analytics event on success, which is the reason
callers should go through them rather than calling the timeline directly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 10:06:47 +02:00
Benoit Marty 76411b07fb Add KDoc to the features/leaveroom API interfaces
Document LeaveRoomState, LeaveRoomEvent and LeaveRoomRenderer.

The renderer doc records the two things a host screen needs to know, both taken
from LeaveRoomPresenter and LeaveRoomView: it draws nothing until a leave event is
sent, and onSelectNewOwners fires specifically when the user is the last owner of
a room that still has other members.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 10:05:31 +02:00
Benoit Marty 49f1284116 Merge pull request #7414 from element-hq/renovate/io.sentry-sentry-android-8.x
Update dependency io.sentry:sentry-android to v8.52.0
2026-08-14 10:05:20 +02:00
Benoit Marty bc6f237507 Add KDoc to the features/invitepeople API interfaces
Document InvitePeopleState and each of its properties, InvitePeopleEvents,
InvitePeopleRenderer and the presenter factory.

The state properties now say what drives them, in particular that
createRoomFromDmAction covers the case of inviting a third person into an
existing direct message, and that the factory accepts a null room for that flow.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 10:04:31 +02:00
Benoit Marty d52e8398ba Merge pull request #7423 from element-hq/renovate/roborazzi
Update roborazzi to v1.71.0
2026-08-14 10:03:33 +02:00
Benoit Marty 186d5b65b9 Add KDoc to the features/messages API interfaces
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>
2026-08-14 10:02:48 +02:00
Benoit Marty 3db3f31b03 Merge pull request #7419 from element-hq/rav/feature_flags
Add hooks to allow providing additional feature flags in EX pro
2026-08-14 10:02:48 +02:00
Benoit Marty 7f3d896a3e Add KDoc to the features/enterprise API interfaces
Document EnterpriseService, SessionEnterpriseService and
RemoteEnterpriseConfigProvider along with all their members and parameters.

Two points that callers get wrong are stated explicitly: these services return
permissive defaults on a standard build, so there is no need to branch on the
build type, and SessionEnterpriseService only answers meaningfully once init has
run. The many nullable getters now say what null means in each case, which is
generally "no override, use the app default".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 10:01:14 +02:00
Benoit Marty 0a47aefdf7 Add KDoc to the libraries/permissions API interfaces
Document PermissionsStore, PermissionStateProvider, LocalNetworkPermissionAdvisor
and the PermissionsPresenter factory, along with their members and parameters.

The store doc explains why the module exists: Android does not let an app
distinguish "never asked" from "asked and denied", so the app has to remember
that itself in order to decide whether it may show a rationale.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 09:57:38 +02:00
Benoit Marty a90801bcec Add KDoc to the libraries/pushstore API interfaces
Document UserPushStore, UserPushStoreFactory, PushClientSecret,
PushClientSecretStore and PushClientSecretFactory along with all their members.

The client secret interfaces now explain why they exist at all: a push gateway
delivers notifications without saying which account they belong to, so the secret
is what lets an incoming push be attributed back to a session.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 09:56:28 +02:00
Benoit Marty c6af196bf0 Add KDoc to the libraries/wellknown API interfaces
Document WellknownRetriever, ElementWellknownStore, ElementWellKnownParser and
their factories, along with every member.

The caching semantics are the part callers need and cannot see in the signatures,
so they are recorded here: the store reports entries older than a day as outdated
rather than missing, the retriever returns such a value immediately and refreshes
in the background, an enterprise build with a hardcoded configuration
short-circuits entirely, and the parser ignores unknown JSON fields.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 09:50:08 +02:00
Benoit Marty 23a346907c Add KDoc to the libraries/mediaupload API interfaces
Document MediaSender and all its members, its two factories, MediaPreProcessor,
MaxUploadSizeProvider and MediaOptimizationConfigProvider.

The MediaSender doc explains the two-step shape of the API, which the individual
signatures do not convey: pre-processing and sending are separate so a preview
can be shown in between, and the temporary files that pre-processing creates are
only released by cleanUp, whether or not anything was ultimately sent.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 09:44:42 +02:00
Benoit Marty 414d2b5dd0 Add KDoc to the libraries/emoji API interfaces
Document AddRecentEmoji, GetRecentEmojis.invoke, the EmojiPickerRenderer
interface and the EmojiPickerPresenter factory, pointing at the no-op renderer
and the empty recent-emoji source so callers know how to opt out of each.

EmojiPickerPresenter itself is a Presenter and therefore out of scope.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 09:39:36 +02:00
Benoit Marty 7462c5f366 Add KDoc to the services/analytics API interfaces
Document AnalyticsService and ScreenTracker at the interface level, document the
members of the cold start and room list watchers, and add the missing @param
entries across the analytics interfaces.

The AnalyticsService doc states the rule that applies to the whole interface:
nothing reaches the providers until the user has given their consent.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 09:38:36 +02:00
Benoit Marty 82ff94672b Add KDoc to the libraries/preferences API interfaces
Document AppPreferencesStore, SessionPreferencesStore, their two factories, and
each of their 44 members.

For a preference store the useful thing to document is the fallback value, so
each getter states the default it uses when the preference has never been set,
taken from DefaultAppPreferencesStore and DefaultSessionPreferencesStore:
developer mode follows the build type, live location updates default to 10
metres, the log packs to an empty set, and the session toggles are all on by
default except session verification skipping.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 09:35:57 +02:00
Benoit Marty f01abdc00d Add KDoc to the libraries/push API interfaces
Document PushService, GetCurrentPushProvider, NotificationCleaner and
NotificationBitmapLoader along with their members, and add the missing @param
entries across the notification interfaces.

The NotificationCleaner docs record the behaviour shared by all its methods,
which is not visible in the signatures: each one also removes the grouping
summary notification once it has no children left.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 09:33:16 +02:00
Benoit Marty 69e466a8e1 Add KDoc to the remaining matrix API interfaces
Document the permalink, widget, tracing, core, platform, server and analytics
interfaces along with their members, which completes libraries/matrix/api: all
69 in-scope interfaces and their 398 members now carry documentation.

Notable points recorded here: PermalinkParser reports an unrecognised uri as a
fallback link rather than failing, MatrixWidgetDriver.run is a no-op while it is
already running and send is silently ignored once closed, and every member of
CallAnalyticCredentialsProvider is null when the service is unconfigured or the
user has not consented to analytics.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 09:27:01 +02:00
Benoit Marty 99bbd0e29e Add KDoc to matrix API notification and pusher interfaces
Document NotificationSettingsService and PushersService along with all their
members, and complete the NotificationService documentation.

Fix the documentation of notificationSettingsChangeFlow, which described a state
flow of room notification settings when the member is a SharedFlow<Unit> that
only signals that the push rules changed. That was the sole reference to
RoomNotificationSettingsState in the file, so its now unused import is removed.

The docs also explain why so many of these methods take isEncrypted and
isOneToOne: Matrix keeps a separate default push rule per combination.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 09:16:53 +02:00
Jorge Martin Espinosa 22195a47f2 Move the custom tile server URL config to the Element wellknown content (#7416)
* Move the custom tile server URL config to the Element wellknown content

* Iteration: use whole `MapTilerConfig` in the remote config
2026-08-14 08:20:37 +02:00
renovate[bot] 58f09422e3 Update roborazzi to v1.71.0 2026-08-14 06:17:33 +00:00
renovate[bot] ddbc6a2a33 Update metro to v1.4.1 2026-08-14 08:14:47 +02:00
Benoit Marty 68e5ea5a74 Add KDoc to matrix API media, mxc and scanner interfaces
Add the interface level docs that MediaPreviewService, MatrixMediaLoader and
MxcTools were missing, document MediaFile.path and the nested
ContentScannerUrlProvider.Factory, and add the missing @param entries.

Most members in these packages already had accurate documentation and are left
untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 19:30:24 +02:00
Benoit Marty 1e19f5b607 Add KDoc to matrix API room list, directory and spaces interfaces
Document RoomDirectoryService, RoomDirectoryList, SpaceService, SpaceRoomList
and LeaveSpaceHandle along with their members, add the missing DynamicRoomList
page size doc and the missing @param entries, and give MessageSearchService the
interface level doc it was lacking.

The docs make the ownership rules explicit, since they differ across these
types: a SpaceRoomList and a LeaveSpaceHandle are owned by the caller and must be
destroyed or closed, while a RoomDirectoryList and a MessageSearch are released
when the scope they were created with completes.

RoomList, RoomListService and the search interfaces were already accurate and are
left untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 19:28:15 +02:00
Benoit Marty 0b2d6eafc3 Add KDoc to matrix API authentication and device linking interfaces
Document MatrixAuthenticationService, OAuthRedirectUrlProvider, the two QR code
login data interfaces, and the four device linking interfaces, along with their
members and parameters.

The docs record the ordering constraints a caller has to respect: setHomeserver
must precede any login attempt, the link handlers report their outcome through
their step flow rather than through a return value, and ContinuationMessageSender
expects exactly one of cancel or confirm.

The oauth package holds only a sealed interface, so it is out of scope here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 19:14:00 +02:00
Benoit Marty fa01166197 Add KDoc to matrix API encryption and verification interfaces
Document EncryptionService, SessionVerificationService and
SessionVerificationServiceListener along with their members, and add the missing
@param entries.

From RustEncryptionService: the backup and recovery state flows report a
waiting-for-sync value for as long as the session is not syncing, and
isLastDevice and hasDevicesToVerifyAgainst are polled rather than observed.
PasswordStrengthEstimator was already accurate and is left untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 19:10:58 +02:00
Benoit Marty 54780f0f70 Add KDoc to matrix API timeline interfaces
Document Timeline and all of its members, TimelineProvider, and the three
lazy providers carried by EventTimelineItem.

Behaviours taken from RustTimeline: collecting membershipChangeEventReceived or
onSyncedEventReceived is what keeps the timeline subscribed to the SDK, paginate
fails with CannotPaginate when the matching status forbids it, the initial
hasMoreToLoad of each pagination status depends on the timeline mode,
timelineItems also carries virtual items such as date separators, and the media
send functions return a handler the caller must await.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 19:07:55 +02:00
Benoit Marty 8a88638667 Add KDoc to matrix API knock, threads and permissions interfaces
Document KnockRequest and ThreadsListService along with all their members, and
add the missing @param entries to the already documented RoomPermissions.

For ThreadsListService the docs record what the implementation does with the
subscriptions: subscribeToItemUpdates starts a shared subscription on the first
call and re-emits the whole list, subscribeToPaginationUpdates replays the
current state immediately, and destroy leaves the service unusable.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 19:03:48 +02:00
Benoit Marty 0bd4dc39cd Add KDoc to matrix API room interfaces
Document BaseRoom, JoinedRoom, NotJoinedRoom, RoomAliasHelper and JoinRoom,
along with their members, and add the missing @param entries.

Fix the documentation of BaseRoom.getUpdatedIsEncrypted, which described the
room directory visibility instead of the encryption state it actually returns.

Behaviours taken from the implementations: BaseRoom.predecessorRoom swallows
errors into null, updateMembers only reads the cache on its first call,
JoinedRoom.syncUpdateFlow is an opaque change counter, notification settings
start in an Unknown state until updateRoomNotificationSettings is called, and
JoinRoom maps a Forbidden answer to Failures.UnauthorizedJoin.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 19:02:12 +02:00
Benoit Marty 5d6384e175 Add KDoc to MatrixClient
Document the interface itself and all of its 60+ properties and functions.

Notable behaviours taken from RustMatrixClient that callers cannot guess from
the signatures: getRoom and getJoinedRoom build a new closeable instance on
every call, createRoom/joinRoom/knockRoom wait for the membership to come back
through sync and report a null RoomInfo rather than a failure when that wait
times out, getCacheSize excludes the crypto database, and clearCache destroys
the client while keeping the session.

Also fix the stale wording and the malformed KDoc block on resolveRoomAlias,
and add the missing @param entries on the functions that already had a summary.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 18:49:45 +02:00
Richard van der Hoff f7d24050f8 fix unit test 2026-08-13 17:38:59 +01:00
Benoit Marty 42d3893755 Add KDoc to matrix API session and sync interfaces
Document MatrixClientProvider, SdkMetadata, HomeserverCapabilitiesProvider,
TemporaryMatrixClientFactory and SyncService, along with each of their members.

Behaviour described here was checked against the implementations
(MatrixSessionCache, RustSdkMetadata, RustHomeserverCapabilitiesProvider,
RustTemporaryMatrixClientFactory and RustSyncService), so the docs mention the
caching, idempotence and error-wrapping that callers cannot infer from the
signatures alone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 18:34:30 +02:00
Benoit Marty 188e2c1b42 Merge pull request #7418 from element-hq/feature/bma/license
Remove license check on Pro modules.
2026-08-13 17:54:55 +02:00
Benoit Marty 1c34e51651 Merge branch 'develop' into misc/reactions-pinned-messages 2026-08-13 17:34:10 +02:00
Richard van der Hoff 5b728d911b Add hooks to allow providing additional feature flags in EX pro
We modify the existing `FeaturesProvider` interface so that there can be more
than one of them, and the Features provided by *all* of the instances are
shown. We can then make another instance in element-android-enterprise, which
exposes enterprise-only features.
2026-08-13 15:57:57 +01:00
Benoit Marty 776182234e Remove license check on Pro modules. 2026-08-13 16:40:56 +02:00
hayaksi1 59b26173a7 Create the fake MediaStore directory without java.nio.file
Files.createTempDirectory needs API 26 and the fake lives in the main source
set of tests:testutils, so lint failed it against the minimum of 24.
File.createTempFile has been available since API 1 and still gives each
provider instance its own directory.
2026-08-13 17:36:51 +03:00
Jorge Martín 913b566270 Add missing tests for RustTemporaryMatrixClient 2026-08-13 12:46:46 +02:00
Jorge Martín 07e90ff704 Fix MarkdownTextInputTest using the previous onSendMessage signature 2026-08-13 12:10:19 +02:00
Benoit Marty d017a84b2b Remove useless let statement. 2026-08-13 11:47:22 +02:00
Jorge Martín 590e33de71 Add explicit dependency of sonarResolver task to processResources
Otherwise `:codegen:sonarResolver` fails
2026-08-13 10:26:40 +02:00
renovate[bot] 690ffa54ce Update plugin sonarqube to v7.4.0.8496 2026-08-13 10:26:40 +02:00
Benoit Marty 76d547e988 Merge pull request #7347 from hayaksi1/fix/2614-linkify-ipv6-urls
Linkify URLs whose host is a bracketed IPv6 address
2026-08-13 09:49:40 +02:00
Benoit Marty ddbea895a1 Merge branch 'develop' into misc/reactions-pinned-messages 2026-08-13 09:32:25 +02:00
Benoit Marty 8bf5b9dd6f Merge pull request #7349 from hayaksi1/fix/4757-timeline-grouping-timeout
Do not group timeline messages sent more than five minutes apart
2026-08-13 09:23:01 +02:00
Benoit Marty 51eb7d69fb Merge pull request #7396 from element-hq/renovate/kotlin
Update kotlin to v2.3.11
2026-08-13 09:21:58 +02:00
Benoit Marty 8c8d232eac Make onSendMessage parameter not nullable 2026-08-13 09:21:31 +02:00
Benoit Marty fb82291f0a Merge pull request #7353 from hayaksi1/fix/860-enter-sends-message
Send a message with Enter on a physical keyboard, Shift+Enter for a new line
2026-08-13 09:19:07 +02:00
Benoit Marty 1abe26f4d8 Merge pull request #7354 from hayaksi1/fix/5319-linkify-matrix-uri
Turn a matrix: URI in a message into a pill
2026-08-13 09:17:32 +02:00
Benoit Marty 2aea4782e3 Merge pull request #7370 from hayaksi1/fix/6253-duplicate-notification-messages
Stop repeating messages that are already shown in a room notification
2026-08-13 09:13:15 +02:00
Benoit Marty 1a18da8ead Merge pull request #7395 from hayaksi1/fix/7393-bug-report-submission
Fix bug reports failing to send after the app has crashed
2026-08-13 09:12:44 +02:00
Benoit Marty 530de1f85d Merge pull request #7345 from hayaksi1/fix/6426-unifiedpush-account-name
Show the account a UnifiedPush registration belongs to in the distributor
2026-08-13 09:09:57 +02:00
Benoit Marty a3e7d9cc1a Merge pull request #7413 from element-hq/feature/bma/fixThreadPlaceholder
Fix wrong TextComposer placeholder displayed when in a thread.
2026-08-13 09:01:24 +02:00
renovate[bot] e2aa3071a9 Update dependency io.sentry:sentry-android to v8.52.0 2026-08-12 23:03:33 +00:00
Benoit Marty ae5c4161c7 Remove redundant parenthesis 2026-08-12 18:14:36 +02:00
Benoit Marty 00e9a8ab71 Fix wrong TextComposer placeholder displayed when in a thread.
We cannot only use MessageComposerMode since use can edit a message in a thread, so this is becoming handly.
2026-08-12 17:05:08 +02:00
Jorge Martín 7c648b34b7 Remove the legacy web-based account creation for matrix.org
This was added as a temporary measure to allow matrix.org users to use Element X before it supported MAS, but it hasn't been needed for a very long time
2026-08-12 16:48:53 +02:00
hayaksi1 d4863db03e Group the GitHub issue body limit constant with an underscore
The GITHUB_MAX_ISSUE_BODY_SIZE constant added with the description truncation
is a five-digit literal, above detekt's acceptableLength of 4, so
:features:rageshake:impl:detekt failed. Write it as 65_536, matching
MAX_LOG_LINES_SIZE in RageshakeConfig.
2026-08-12 16:06:02 +03:00
Richard van der Hoff 76092fcba6 fix lint 2026-08-12 14:51:57 +02:00
Richard van der Hoff 6badc9634c build-rust-sdk: print the command we run
It's helpful to know what exactly is being run.

Also some tweaks to reduce the amount of directory changing that needs to
happen.
2026-08-12 14:51:57 +02:00
Benoit Marty 999ee9737e Merge pull request #7355 from hayaksi1/fix/822-record-screenshots-no-change
Do not fail the screenshot recording job when there is nothing to commit
2026-08-12 13:51:20 +02:00
Benoit Marty 3029218dde Merge branch 'develop' into fix/2614-linkify-ipv6-urls 2026-08-12 13:48:36 +02:00
Benoit Marty 539437da22 Merge pull request #7343 from hayaksi1/fix/7190-forward-live-location
Hide the forward action on live location shares
2026-08-12 13:47:36 +02:00
Benoit Marty 6cc581d2d3 Merge pull request #7379 from hayaksi1/fix/7314-invalid-pinned-events
Do not display pinned events that the client cannot render
2026-08-12 13:44:48 +02:00
Benoit Marty c8bfabcaf5 Changelog for version 26.08.1 2026-08-12 13:41:43 +02:00
Benoit Marty e289372724 Merge tag 'v26.08.1' into develop
tag
2026-08-12 12:35:06 +02:00
Benoit Marty cfc1e8e5e9 Merge branch 'release/26.08.1' 2026-08-12 12:34:54 +02:00
Benoit Marty 2805fc6cc1 Adding fastlane file for version 26.08.1 2026-08-12 12:34:50 +02:00
Benoit Marty de05495d51 Setting version for the release 26.08.1 2026-08-12 12:34:30 +02:00
Benoit Marty a021ec70cf Merge pull request #7409 from element-hq/feature/bma/fixes
UI fixes
2026-08-12 12:13:32 +02:00
Benoit Marty 7b0745c5f1 Fix layout issues. 2026-08-12 11:00:55 +02:00
Benoit Marty 7c4e81679f Animate visibility of the developer option entry point.
Related to #6703
2026-08-12 09:49:24 +02:00
Benoit Marty fae7df16f9 Merge pull request #7338 from element-hq/feature/bma/linkNewDeviceTimeout
[Link new device] Add timeout
2026-08-11 17:51:39 +02:00
Benoit Marty 2566bd15eb Merge pull request #7207 from element-hq/matthew/knock-push-notifications-pr
Display push notifications for knocks
2026-08-11 17:44:26 +02:00
Benoit Marty 7d99bf0d52 Merge branch 'develop' into matthew/knock-push-notifications-pr 2026-08-11 17:21:34 +02:00
Benoit Marty b2269a9f64 Merge pull request #7351 from hayaksi1/fix/4191-video-bitrate-clamp
Never re-encode a video at a higher bitrate than the source
2026-08-11 17:18:50 +02:00
Benoit Marty c0fbdd78ca Merge pull request #7369 from hayaksi1/fix/6130-space-left-room-joinable
Hide space children you have left that can only be joined by invitation
2026-08-11 17:17:55 +02:00
Benoit Marty 18e76b569f Merge pull request #7340 from hayaksi1/fix/7289-custom-map-style-url
Do not append the application MapTiler key to a custom map style url
2026-08-11 16:53:53 +02:00
hayaksi1 055fea8d25 Hide the forward action on live location shares
The timeline action sheet offered Forward on a live location share, but
forwarding one always failed: Mode.Live maps to the SDK's
MsgLikeKind.LiveLocation, which RoomContentForwarder rejects outright. So
the action was shown for exactly the set of events on which it could
never work.

canBeForwarded() now returns true for a location event only when its mode
is Static. Ended live shares keep Mode.Live and fail for the same reason,
so they are excluded too. ActionListPresenter needs no change: its check
is the function's only call site in the repo, so the single gate covers
the timeline sheet, the threaded timeline and the pinned messages list.

Hiding the action mirrors Element Web and the sticker exclusion already
present in the same function. Converting a live share into a static
m.location on forward would be a product decision and is out of scope.

Relates to https://github.com/element-hq/element-x-android/issues/7190
2026-08-11 17:48:45 +03:00
Benoit Marty 223973805f Create function to limit code duplication. 2026-08-11 16:30:11 +02:00
Benoit Marty 749ecb4576 No need for a companion object. 2026-08-11 16:28:07 +02:00
Benoit Marty 6a124c59ac Merge pull request #7348 from hayaksi1/fix/3338-rtl-room-list-preview
Stop a right to left sender name from reordering a room list preview
2026-08-11 16:25:59 +02:00
Benoit Marty 9116b8a776 Merge pull request #7342 from hayaksi1/fix/5383-incompatible-room-list-filters
Ignore a room list filter that is incompatible with an already selected one
2026-08-11 16:21:51 +02:00
Benoit Marty 433f5d1d04 Merge pull request #7350 from hayaksi1/fix/5766-redaction-unknown-target
Do not notify about a redaction whose target event is unknown
2026-08-11 16:21:28 +02:00
Benoit Marty bea89fee41 Merge pull request #7352 from hayaksi1/fix/6189-no-call-in-empty-dm
Do not offer to start a call in a DM with no other member
2026-08-11 16:21:02 +02:00
Benoit Marty 2e7f3f1371 Merge pull request #7346 from hayaksi1/fix/5780-voice-recorder-double-start
Ignore a second voice recording start while one is already running
2026-08-11 16:20:20 +02:00
Benoit Marty fbe2b3574f Merge pull request #7363 from hayaksi1/fix/2419-empty-waveform-seek
Let a voice message without a waveform be seeked
2026-08-11 16:00:59 +02:00
Benoit Marty 0eb73af1fd Merge pull request #7365 from hayaksi1/fix/7046-voice-message-screen-timeout
Keep playing a voice message when the screen turns off
2026-08-11 16:00:23 +02:00
Benoit Marty 15addc522b Merge pull request #7367 from hayaksi1/fix/2925-room-notification-mute-string
Use a dedicated string for the Mute room notification mode
2026-08-11 15:59:55 +02:00
Benoit Marty 12f4330724 Merge pull request #7371 from hayaksi1/fix/5428-send-to-room-subtitle
Show the alias or the direct message user's Matrix ID in the room picker
2026-08-11 15:59:29 +02:00
Benoit Marty 3811d957ed Merge pull request #7402 from element-hq/renovate/org.matrix.rustcomponents-sdk-android-26.x
Update dependency org.matrix.rustcomponents:sdk-android to v26.08.11
2026-08-11 15:56:00 +02:00
renovate[bot] b21c239a9d Update kotlin to v2.3.11 2026-08-11 13:39:26 +00:00
renovate[bot] f9834c0b03 Update dependency org.matrix.rustcomponents:sdk-android to v26.08.11 2026-08-11 13:39:16 +00:00
Benoit Marty 44caa94f3d Merge pull request #7373 from hayaksi1/fix/5794-account-data-api
Expose global account data get and set on MatrixClient
2026-08-11 14:51:57 +02:00
Benoit Marty f7508cb29a Merge pull request #7356 from hayaksi1/fix/4008-fediverse-handle-not-email
Do not linkify the server part of a fediverse handle as an email address
2026-08-11 14:48:40 +02:00
Benoit Marty ad39144a99 Merge pull request #7362 from hayaksi1/fix/4973-media-gallery-error
Keep the opened image on screen when the media gallery fails to load
2026-08-11 14:15:01 +02:00
Benoit Marty e2a1342efa Merge pull request #7360 from hayaksi1/fix/7253-skip-other-account-notifications
Do not notify about an event sent by another account on the same device
2026-08-11 14:14:36 +02:00
Benoit Marty 4ac913e7c0 Merge pull request #7366 from hayaksi1/fix/3493-media-progress-insets
Do not draw the media loading progress bar under the status bar
2026-08-11 14:05:05 +02:00
Benoit Marty 005cbf6579 Merge pull request #7372 from hayaksi1/fix/4893-copy-blocked-user-id
Copy a blocked user's Matrix ID by long pressing them
2026-08-11 14:04:19 +02:00
Benoit Marty 90a2a258d1 Merge branch 'develop' into fix/5794-account-data-api 2026-08-11 14:03:39 +02:00
Jorge Martín 6c7f4d3b24 Add some missing tests to improve coverage 2026-08-11 13:18:49 +02:00
Jorge Martín 4f5855bfa5 Bump enterprise module 2026-08-11 13:18:49 +02:00
Jorge Martín e371b46c68 Rename GetUrlResolver to UrlContentFetcher and TemporaryMatrixClientFactory.createTemporaryMatrixClient to just create 2026-08-11 13:18:49 +02:00
Jorge Martín dbc28df3eb Remove unnecessary call to fetchElementWellKnown when the value was up to date in the store in DefaultWellknownRetriever 2026-08-11 13:18:49 +02:00
Jorge Martín e92016c68e Improve host extraction in DefaultWellknownRetriever 2026-08-11 13:18:49 +02:00
Jorge Martín e24468c74f Fix and add tests 2026-08-11 13:18:49 +02:00
Jorge Martín e3290707d9 Remove no longer needed SessionWellknownRetriever 2026-08-11 13:18:49 +02:00
Jorge Martín 89908c2511 Add WellknownRetrieverResul.upToDateDataOrNull helper function for the enterprise module 2026-08-11 13:18:49 +02:00
Jorge Martín b9afbf0692 Add a session-scoped WellknownRetriever provider so we don't have to build it every time there 2026-08-11 13:18:49 +02:00
Jorge Martín ec0819ad07 Fixup geturlresolver matrix client 2026-08-11 13:18:49 +02:00
Jorge Martín 90d4fbe0bd Apply the GetUrlResolver pattern using unauthenticated matrix clients where possible 2026-08-11 13:18:49 +02:00
Jorge Martín 25eca29cd2 Rework WellknownRetriever so it uses the GetUrlResolver
Also, allow instantiating it using a factory: with this we can always decide where to take the config from and how to do it

Do the same for `ElementWellknownStore` so we can store and restore the values for different sources (endpoints) independently
2026-08-11 13:18:49 +02:00
Jorge Martín 3f19100427 Add TemporaryMatrixClient interface
This is an abstraction over an actual unauthenticated `Client` from the SDK that implements `GetUrlResolver`, and is supposed to be short-lived.

With this, we can use the SDK network stack to perform arbitrary GET url operations.

The `AutoCloseable` implementation makes it easier to clean up any leftover local config after using this temporary client.
2026-08-11 13:18:49 +02:00
Jorge Martín d48f5bd8b9 Have MatrixClient implement GetUrlResolver 2026-08-11 13:18:49 +02:00
Jorge Martín fd3c671301 Create RemoteEnterpriseConfigProvider wrapper
For Pro, this will check different remote config sources and return the result. For FOSS, this will always return a `NotFound` result
2026-08-11 13:18:49 +02:00
Jorge Martín c8e43bdad6 Create GetUrlResolver interface
Implementations should just perform a GET request and return the raw body as bytes
2026-08-11 13:18:49 +02:00
Jorge Martín 49abaf5a5c Create EnterpriseRemoteConfigSource enum
This allows us to decide where to get the remote enterprise config from.
2026-08-11 13:18:49 +02:00
Jorge Martín 59758a20a5 Add RemoteEnterpriseConfig typealias for ElementWellKnown
In the future, we want to move the enterprise config to the private Element Pro repo, this is a first step
2026-08-11 13:18:49 +02:00
renovate[bot] 7438a0d378 Update plugin dependencycheck to v13 (#7386)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-11 11:52:27 +02:00
renovate[bot] 9a5aab2d73 Merge pull request #7384 from element-hq/renovate/gradle-actions-6.x
Update gradle/actions action to v6.3.0
2026-08-11 11:52:02 +02:00
ganfra 6d6ca97899 Merge pull request #7328 from element-hq/feature/fga/user_status_last_pieces
User status last pieces
2026-08-11 11:11:12 +02:00
Benoit Marty 91e533ec56 Merge pull request #7389 from element-hq/sync-localazy
Sync Strings
2026-08-11 10:27:41 +02:00
ElementBot 77ae52513c Update screenshots 2026-08-11 08:18:52 +00:00
ganfra 0de78ed956 Merge branch 'develop' into feature/fga/user_status_last_pieces 2026-08-11 10:04:18 +02:00
ElementBot 612b4a6dbe Update screenshots 2026-08-11 07:41:59 +00:00
bmarty 17c5d7db59 Sync Strings from Localazy 2026-08-11 09:27:29 +02:00
hayaksi1 84b38e291a Fix bug reports failing to send after the app has crashed
The whole crash call stack was appended to the bug report description, and the
bug report server copies that description verbatim into the GitHub issue it
opens. GitHub refuses an issue body over 65536 characters, so the server's save
step failed and answered "Could not save report". The stored crash data is only
cleared after a successful send, so every retry failed the same way.

Truncate the description before sending it. The user's own text and the GitHub
issue link come first, so only the tail of the crash stack is dropped. The
uncaught exception handler already writes the full stack through Timber, so it
stays available in the attached log files.

The 40 KB budget leaves room for the other reported fields, mainly the list of
omitted log file names, which is not otherwise bounded.
2026-08-10 22:53:50 +03:00
Benoit Marty a95f02497e Merge pull request #7378 from hayaksi1/fix/6853-remember-revealed-images
Keep manually revealed media visible when re-entering a room
2026-08-10 19:23:39 +02:00
Jenna Vassar 749c96e409 Raise the account provider history cap to 20 entries 2026-08-10 09:42:44 -07:00
Jenna Vassar bbb3a68087 Save the selected account provider to history even if the flow default is reset 2026-08-10 09:28:32 -07:00
Jenna Vassar f8816b2449 Submit the exact account provider field text on continue 2026-08-10 09:28:31 -07:00
Benoit Marty b4189f5ee0 Merge pull request #7375 from hayaksi1/fix/3444-login-with-full-matrix-id
Let users enter their full Matrix user ID when searching for a homeserver
2026-08-10 18:17:53 +02:00
Benoit Marty 37c643dcdd Merge pull request #7374 from hayaksi1/fix/4666-feature-module-template
Fix the feature module template writing the impl build script into the api module
2026-08-10 16:53:09 +02:00
hayyaksi 348c7bcea8 Expand the emoji picker to full height when the keyboard is shown (#7382)
* Expand the emoji picker to full height when the keyboard is shown

The picker content is rendered with fillMaxSize, so the sheet measures itself to
the full window height. That makes the partially expanded anchor available, and
the modal sheet auto-shows at that anchor, half way down the screen.

The sheet reserves the IME inset through its content window insets, but applies
that padding at its own bottom edge, which is off-screen while it rests half way
down. The band that is both on-screen and above the keyboard is then only about
a tenth of the screen, which is why the matching emoji end up behind it.

Expand the sheet while the keyboard is visible so its bottom edge returns to the
screen edge and the inset it already reserves lands on top of the keyboard.
Modifier.imePadding on the sheet content is not an option: the sheet applies the
insets with windowInsetsPadding, which consumes them, so a nested imePadding
reads zero.

* Cover the keyboard driven sheet expansion with a test

The IME reaction moves into ExpandSheetWhenImeIsVisible so a Robolectric test can
drive it: dispatching window insets to the composition flips
WindowInsets.isImeVisible, and the sheet state is observable from the test. Both
the shown and the hidden case are asserted, which is the patch coverage this
change was missing.

* Do not leave a blank strip below the expanded emoji search

The 10dp of bottom padding on the search bar separates the collapsed
field from the category tabs underneath it. It stayed applied once the
search bar expanded to fill the picker, so the picker stopped 10dp short
of its own bottom edge.

That strip used to sit below the visible part of a half height sheet.
Now that the sheet expands against the keyboard it reads as a gap
between the results and the keyboard, so only pad while collapsed.
2026-08-10 16:35:38 +02:00
Jorge Martin Espinosa cb3b9e7946 Add default OAUTH_CLIENT_URL_PATH alternative (#7385)
* Add default `OAUTH_CLIENT_URL_PATH` alternative

Previously this was `null`, but we want to use `apps/android` instead to identify Element X Android. Element Pro will use a different value, and other custom apps based on EP will use `null` or their own custom value, if they provided one

* Suppress lint check so the pro app can be built
2026-08-10 16:16:14 +02:00
renovate[bot] 481407329f Update actions/setup-java action to v5.7.0 (#7339)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-10 16:03:45 +02:00
hayaksi1 629dc15a86 Only show a Matrix ID in the room picker for a direct message
heroes is populated for any room whose name is derived from its members,
not only for direct messages, so a two person room that is not a DM
showed a member's Matrix ID as its subtitle.

Carry isDm on SelectRoomInfo and read the hero only when it is set. The
canonical alias still takes precedence, so nothing changes for a room
that has one.
2026-08-10 16:46:58 +03:00
Jorge Martin Espinosa e101963814 Display 'add reactions' menu in the threads screen (#7388) 2026-08-10 15:38:57 +02:00
hayaksi1 1b833df727 Expose the revealed event ids as a SnapshotStateSet
Restore the snapshot backed set the store started with and declare
allowedEventIds as SnapshotStateSet so the interface says out loud that
reads of it are observable.

TimelineProtectionPresenter reads the set inside derivedStateOf, so a
plain set silently stops the timeline from re-rendering when an event is
revealed. Naming the type keeps that requirement from being mistaken for
an implementation detail.
2026-08-10 16:36:36 +03:00
Benoit Marty 30986d54f0 Merge pull request #7387 from element-hq/feature/bma/complementFor7376
Update in reply to message when message is not found.
2026-08-10 15:33:11 +02:00
ElementBot f7936b92d0 Update screenshots 2026-08-10 10:31:57 +00:00
Benoit Marty 1693d325c1 Update UI according to https://www.figma.com/design/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?node-id=5781-5862 2026-08-10 12:12:55 +02:00
Benoit Marty 650b38429c Show a Message not found placeholder when a replied to message cannot be loaded (#7376)
* Show a Message not found placeholder when a replied to message cannot be loaded

When the replied to event could not be fetched, the reply preview rendered the
raw SDK error string in critical styling, and tapping it emitted a focus
request that could never succeed, so the user got a dead end dialog.

Render the localised error_message_not_found string in the neutral italic style
already used for redacted and undecryptable replies, and drop the click handler
for that case. The existing key is reused rather than adding a new one; a "you
do not have access" wording was rejected because this state covers any failure
to load the event, not only a permission problem.

* Update screenshots

* Trigger CI after recording screenshots

---------

Co-authored-by: ElementBot <android@element.io>
2026-08-10 11:59:37 +02:00
hayaksi1 4558590492 Cover both MediaStore save paths with a fake provider
DefaultFileSave had no test at all and AndroidLocalMediaActions only exercised the
failing path, so neither the uniquifying retry nor the new IOException on a null
insert was ever executed.

FakeMediaStoreContentProvider stands in for the media authority: it records the
display names an insert is attempted with, keeps whatever is copied into it, and can
either collide a name the way MediaStore does past 32 files or refuse to create
anything. Both call sites now go through it for the plain save, the retry and the
rejected insert.

Its name cannot follow the Fake<Interface> convention because it extends the
framework ContentProvider, so it joins the existing exceptions in
KonsistClassNameTest next to FakeFileSystem and FakeKeyStore.
2026-08-10 11:49:15 +03:00
renovate[bot] 829dbb1563 Update zizmorcore/zizmor-action action to v0.6.2 (#7383)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-10 08:16:20 +00:00
hayaksi1 09efb04a8f Hold the revealed event ids in a plain set
The store no longer needs a mutableStateSetOf: an ordinary Set kept in a
mutableStateOf does the same job, and matches DefaultMediaPreviewConfigStateStore.

The Compose state itself has to stay. TimelineProtectionPresenter derives
ProtectionState from the store inside a derivedStateOf, so the set must be a
snapshot object for a reveal to invalidate it and reach the timeline. Backing it
with a plain mutableSetOf leaves the presenter with nothing to observe and it
stops emitting after ShowContent.
2026-08-10 10:59:22 +03:00
Eklavya Jandial 19c6526841 [Task] Show active call participants in the people list #7199 (#7300) 2026-08-10 09:40:12 +02:00
hayaksi1 aa7b9b8721 Trigger CI after recording screenshots 2026-08-09 15:47:29 +03:00
hayaksi1 acb9e0e082 Trigger CI after recording screenshots 2026-08-09 15:47:26 +03:00
hayaksi1 e5337b42e0 Validate the bracketed authority with a URI parser instead of a regex
Review feedback: the previous pattern hardcoded http, https and ftp and
guessed at the address grammar. Candidates are now found by scheme and
handed to java.net.URI, which accepts any scheme and rejects an authority
that is not a real IPv6 literal, an unclosed bracket, or array syntax
such as values[0]:1. Also drops the explanatory comments.
2026-08-09 15:44:14 +03:00
ElementBot 7989361114 Update screenshots 2026-08-09 12:41:41 +00:00
ElementBot 4c1d660cf3 Update screenshots 2026-08-09 12:41:30 +00:00
hayaksi1 fbdb99019e Drop the MediaStore explanation from the save helper
Review feedback: the retry itself is short enough to read, and the
platform limit it works around belongs in the commit, not the source.
The contract of the name helper keeps its one-line KDoc.
2026-08-09 15:40:20 +03:00
hayaksi1 f0e791001f Drop the comments from the matrix URI pillification
Review feedback: the regex name and the test names carry the meaning.
2026-08-09 15:39:18 +03:00
hayaksi1 4b5429395d Drop the comments from the Enter key handling
Review feedback: the callback name and the device-id check read on their
own, and the test names carry the soft versus physical keyboard rule.
2026-08-09 15:37:18 +03:00
hayaksi1 7f56c15fb8 Drop the comments from the empty DM call guard
Review feedback: the member-count condition reads on its own.
2026-08-09 15:33:51 +03:00
hayaksi1 750db0c777 Drop the comments from the bitrate clamp
Review feedback: the clamp expression and the test name state the rule.
2026-08-09 15:33:37 +03:00
hayaksi1 fec71aaf4d Drop the comments from the redaction resolution
Review feedback: the filtered-out result and the test name already state
why an unmappable redaction must not notify.
2026-08-09 15:31:19 +03:00
hayaksi1 b725d37b0e Do not display pinned events that the client cannot render
The pinned events timeline is built with an exclusion filter rather than an
allowlist, so an event of a type the app has no renderer for reaches both
pinned surfaces. The banner falls through to the formatter catch-all and reads
"Unsupported event", and the list renders an unknown content tile.

Drop unknown content and message-like or state events that failed to parse from
both surfaces, along with any day divider left without an event to introduce,
and show the empty state when nothing renderable remains. The spec makes this
the client's call. Such an event can still be unpinned from the room timeline,
where it is still rendered.

Undecryptable and redacted events are untouched: they carry information the
user needs.
2026-08-09 15:20:13 +03:00
hayaksi1 6533a10ab9 Keep manually revealed media visible when re-entering a room
The set of revealed event ids lived on TimelineProtectionPresenter, which is
scoped to the room graph. That graph is rebuilt every time the room is opened,
so the set was empty on re-entry and every revealed image was hidden again.

Move it into a session scoped TimelineProtectionStore. The store is in memory
rather than persisted: revealing media is a privacy control and there is no
hide action today, so keeping the reveals for the session leaves restarting the
app as a way to undo one, and the set is dropped on logout.
2026-08-09 15:19:46 +03:00
hayaksi1 05528dbd3d Let users enter their full Matrix user ID when searching for a homeserver
The account provider search treated its whole input as a host, so a full user
ID was probed as https://@alice:example.org, which never resolves, and the
permissive URL fallback then offered that invalid address as a provider.

Recognise a complete user ID and resolve the homeserver from its server name,
preserving a port when there is one. Input that starts with @ but is not yet a
complete user ID resolves nothing, so no candidate is produced while the user
is still typing. Detection reuses MatrixPatterns.isUserId and UserId.domainName
rather than a new pattern.
2026-08-09 15:19:45 +03:00
hayaksi1 38398c24b2 Show a Message not found placeholder when a replied to message cannot be loaded
When the replied to event could not be fetched, the reply preview rendered the
raw SDK error string in critical styling, and tapping it emitted a focus
request that could never succeed, so the user got a dead end dialog.

Render the localised error_message_not_found string in the neutral italic style
already used for redacted and undecryptable replies, and drop the click handler
for that case. The existing key is reused rather than adding a new one; a "you
do not have access" wording was rejected because this state covers any failure
to load the event, not only a permission problem.
2026-08-09 15:19:45 +03:00
hayaksi1 33bdf968c5 Expose global account data get and set on MatrixClient
MatrixClient had no way to read or write global account data although the
underlying client supports it.

Add getAccountData and setAccountData to the interface and implement them in
RustMatrixClient using the same withContext/runCatchingExceptions idiom as the
neighbouring methods. The content is the raw JSON string the SDK returns and
accepts: event types are open ended, so a typed wrapper belongs to whichever
caller needs one. Scope is limited to global account data; the room level
variant and the change notification APIs can follow when a feature needs them.
2026-08-09 15:10:00 +03:00
hayaksi1 5140b7c3c8 Fix the feature module template writing the impl build script into the api module
Generating a feature with the Generate Module from Template plugin produced a
module that could not build: features/<name>/api/build.gradle.kts was written
from the impl template, giving the api module a dependency on itself and an
impl namespace.

FeatureModule.json declared BUILD_GRADLE_API and BUILD_GRADLE_IMPL, both
defaulting to build.gradle.kts, and used them as fileTemplates keys. The plugin
resolves keys to filenames before matching, so both collapsed to one key and
the second overwrote the first. The same matching code falls back to a suffix
match on the path, so DefaultXEntryPoint also matched the key XEntryPoint and
was generated from the api template.

Key both by directory, which the plugin supports for exactly this case, and
drop the two now-unused placeholders. The file templates themselves were
correct and are unchanged. The onboarding doc gains a note that an already
imported template must be removed before re-importing, since the plugin keeps
its own copy.
2026-08-09 15:10:00 +03:00
hayaksi1 cbe555009b Stop repeating messages that are already shown in a room notification
A room notification is rebuilt from the one currently on screen: its messaging
style is extracted and the incoming events are appended. Every event was
appended unconditionally, so an event re-delivered while its notification was
still showing was added again, and the notification re-alerted each time.

Skip events whose id is already present in the existing messaging style, and
set onlyAlertOnce when the batch adds nothing new. Smart reply failures are
exempt, because that path deliberately reuses the same synthesised event id to
append the "message not sent" line. Filtering is per event rather than per
message so an image and its caption stay a unit.

This fixes the visible duplication, not the re-delivery that triggers it, and
does not help once the notification has been dismissed.
2026-08-09 15:09:59 +03:00
hayaksi1 d04779c8fd Copy a blocked user's Matrix ID by long pressing them
The blocked users list displayed each Matrix ID but offered no way to get it
out of the app, which is what someone who blocked a user for abuse needs in
order to report them onwards.

Long press now copies the Matrix ID and confirms with a snackbar, reusing the
ClipboardHelper and SnackbarDispatcher idiom from the user profile screen. A
short tap still opens the unblock confirmation. The row declares a long click
label so the action is announced by a screen reader. No new string is needed.
2026-08-09 15:09:59 +03:00
hayaksi1 ed82505946 Show the alias or the direct message user's Matrix ID in the room picker
The forward and share room picker showed only a display name, so several
direct messages or rooms sharing a name were indistinguishable.

Render a secondary line: the canonical alias when the room has one, otherwise
the other person's Matrix ID for a direct message. No model change was needed,
because heroes is populated only for a direct message with exactly one hero,
so heroes.singleOrNull() is the peer.
2026-08-09 15:09:59 +03:00
hayaksi1 1299b5c897 Hide space children you have left that can only be joined by invitation
After leaving a room listed in a space, the room stayed in the child list with
a Join button, even though it can only be entered with a new invitation. It
disappeared only after leaving and reopening the space, because the server
omits it from the refetched hierarchy.

Apply the same rule locally in the presenter. The filter keys on the LEFT
membership combined with an invite join rule rather than on the derived
visibility, which collapses knock and unknown join rules into private and
would wrongly remove the Join button from knockable rooms.
2026-08-09 15:09:34 +03:00
hayaksi1 8272523b5b Keep playing a voice message when the screen turns off
The ExoPlayer backing voice messages was built without a wake mode, so it
defaulted to C.WAKE_MODE_NONE and held no wake lock. The CPU was free to
suspend once the screen turned off, cutting playback short.

Build it with C.WAKE_MODE_LOCAL instead. The media is a local file, so no
WifiLock is needed, and android.permission.WAKE_LOCK is already in the merged
manifest. The media viewer players are left alone: they already pause on
ON_PAUSE, so a wake lock there would keep the CPU awake for a player the user
has intentionally backgrounded.
2026-08-09 15:09:33 +03:00
hayaksi1 aff71cf1b9 Use a dedicated string for the Mute room notification mode
Two of the three modes on the room notification settings screen had
screen-scoped keys; the third reused common_mute, which also labels the Mute
action button on the room details screen. A translator could not fix one
occurrence without breaking the other.

Add screen_room_notification_settings_mode_mute and use it at both places on
that screen. The English value is unchanged, so nothing renders differently.
The action button keeps common_mute, which is the one context where the
generic verb is correct.
2026-08-09 15:09:33 +03:00
hayaksi1 1b7631b84e Do not draw the media loading progress bar under the status bar
The media viewer is edge to edge and the loading progress indicator was the
top-most child of a container that only applied navigation bar padding, so it
was laid out at y=0, behind the status bar and any display cutout.

Give it safeDrawing horizontal and top padding, reusing the idiom already used
by MediaViewerBottomBar in the same file. The inset deliberately does not come
from the scaffold content padding, which collapses to zero when the user taps
to hide the overlay.
2026-08-09 15:09:33 +03:00
hayaksi1 72cc8f848d Drop the comments from the grouping timeout
Review feedback: the constant name and the test names carry the rule.
2026-08-09 14:51:30 +03:00
hayaksi1 c3a281815b Drop the comments around the bidi isolation
Review feedback: the helper name says what it does and the test names say
why the preview needs it.
2026-08-09 14:49:59 +03:00
ElementBot 73f26c3d74 Update screenshots 2026-08-09 11:48:57 +00:00
ElementBot 813122eebd Update screenshots 2026-08-09 11:48:53 +00:00
hayaksi1 b32d48f7f6 Drop the comments from the double start guard
Review feedback: the guard, the warning it logs and the test names carry
the meaning on their own.
2026-08-09 14:48:14 +03:00
hayaksi1 8943c4e724 Drop the comments from the filter selection guard
Review feedback: the guard and the test name already say what the check
is for, so the narration was redundant.
2026-08-09 14:46:13 +03:00
hayaksi1 7ebcb86ee1 Drop the comment on the custom map style branch
Review feedback: the branch already reads as "use the custom url as it
is", so the explanation belongs in the commit rather than the source.
2026-08-09 14:44:30 +03:00
hayaksi1 bf958e0008 Drop the comment above the UnifiedPush registration call
Review feedback: the named `messageForDistributor` argument already says
what the session id is doing there, so the comment only added noise.
2026-08-09 14:42:04 +03:00
hayaksi1 bdf7f329c1 Build the bidi isolate characters from their code points
Android lint rejects the literal U+2068 and U+2069 characters embedded in the string template with BidiSpoofing, "String contains misleading Unicode bidirectional text", which fails the lint task for the module.

Naming the two code points and building the characters from them produces the same string and leaves no invisible characters in the source.
2026-08-09 14:34:39 +03:00
hayaksi1 baf28f85cf Keep the opened image on screen when the media gallery fails to load
Opening an image from the timeline replaces it with a full-screen page
reading "error". Two things go wrong. The viewer already holds the item
that was tapped, but building the surrounding media timeline can fail and
the failure overwrites that cached item, so the picture the user asked for
disappears. And the failure page renders the exception message straight
from the SDK, which is developer text with nothing actionable in it.

Emit the failure only when there is nothing cached, so a viewer opened on
a specific event keeps showing that event and stays usable while the rest
of the gallery is unavailable. Where there really is nothing to show, the
page now uses the same alert icon, title and message as the other error
states in this screen, built from strings that already exist.

The trade-off is that the timeline error is no longer surfaced in the
cache case; it is still logged. Swapping it for a snackbar would need a
new surface in the viewer and is left out.
2026-08-09 13:52:55 +03:00
hayaksi1 d45ffd8181 Let a voice message without a waveform be seeked
A voice message that arrives without amplitude data — bridged messages and
those sent by clients that omit it — is unusable. The waveform is empty, so
its measured width is zero: the progress overlay never draws, the cursor is
pinned to the left edge, and the touch handler rejects every event because
no x coordinate can fall inside a zero-width range. The message plays, but
there is no way to see where it is or to move it.

Fall back to a flat run of zero amplitudes sized to the canvas when there
is no waveform. Width, progress, cursor and seeking then all work exactly
as they do for a real waveform, and the existing minimum amplitude renders
those zeros as the same flat track a genuinely silent recording already
draws, so no new drawing code is needed.

Nothing changes for a message that has a waveform, and the model still
carries the empty list, which is the honest value. Computing a real
waveform from the audio is a separate piece of work.
2026-08-09 13:47:46 +03:00
hayaksi1 32f7a9c836 Do not notify about an event sent by another account on the same device
With several accounts logged in, sending a message from one of them raises
a notification on every other account that shares the room. The device
notifies its owner about something the owner has just done, and the more
accounts share a room the more copies arrive.

Resolve the set of logged-in user ids once per push batch and drop an event
whose sender is one of the other accounts, using the filtered-out result
the notification pipeline already understands, so it is recorded in push
history rather than turned into a fallback notification.

The receiving session is removed from that set, which keeps the change a
strict no-op for a single account and protects the local echo that the
reply action pushes back through the same path. Filtering happens at the
one point that sees every kind of push, so messages, polls, call invites,
ringing calls and room invites are all covered.

Filtering is unconditional and has no setting; multiple accounts sit
behind an experimental flag that is off by default, so nothing changes for
a stock build.
2026-08-09 13:38:28 +03:00
hayaksi1 f5a2d5bf88 Do not linkify the server part of a fediverse handle as an email address
A fediverse handle such as `@name@server.tld` contains a substring that
`Linkify.EMAIL_ADDRESSES` recognises as a valid address, so the timeline
renders `name@server.tld` as a tappable `mailto:` link. Tapping it opens a
mail composer addressed to an account that does not accept mail.

Drop a newly added `mailto:` span when the character immediately before it
is `@`. This mirrors what Android already does for web URLs, where
`Linkify.sUrlMatchFilter` rejects a match preceded by `@` — which is why
the handle currently produces exactly one stray mailto span and no partial
link is left behind once it is removed.

Author-supplied links are unaffected: spans that were already present
before linkification are excluded from the check and restored as before. A
genuine address written directly after an `@` — "mail me @support@element.io"
— is no longer linkified, which is the same trade-off Android already makes
for web URLs.
2026-08-09 13:16:04 +03:00
hayaksi1 aa64f07ff0 Do not fail the screenshot recording job when there is nothing to commit
recordScreenshots.sh runs under `set -e`, and the `git commit -m "Update
screenshots"` at the end of it is unconditional. When a recording run
produces no diff — the common case once the snapshots are already up to
date — `git commit` exits 1 with "nothing to commit, working tree clean"
and takes the whole workflow down with it, reporting a red job for a
successful run.

Stage as before, then return early when the staged diff is empty. `set -e`
is left in place: it was added deliberately so that a failed Gradle run
cannot push a commit that deletes every snapshot. Using `git diff --cached
--quiet` as an `if` condition is `set -e`-safe, because its non-zero "there
are changes" status is consumed by the condition. The early return also
skips the push, which had nothing to send.
2026-08-09 13:11:16 +03:00
hayaksi1 bc20e64c61 Do not notify about a redaction whose target event is unknown
A redaction push whose redacts field the SDK could not map was reported as
an unknown error. The result processor only suppresses filtered-out and
redacted events; every other exception becomes a visible "you have N
notifications" fallback and also raises the battery optimisation banner.

So a deleted message could produce a notification about itself, which is
the opposite of what a redaction means, plus an unrelated banner.

The branch now throws EventFilteredOut, which the processor already drops
silently, and logs at warning level so the SDK gap still shows up in push
history.

Relates to https://github.com/element-hq/element-x-android/issues/5766
2026-08-09 04:09:35 +03:00
hayaksi1 4b85e2d31e Turn a matrix: URI in a message into a pill
A permalink written as a matrix: URI stayed plain text, while the same
link in matrix.to form became a pill. The pillifier only scanned
Patterns.WEB_URL, which cannot match a URI with no web scheme, even though
the permalink parser already understands matrix: URIs.

Those URIs are now scanned for as well, before the web pass, since
Patterns.WEB_URL otherwise claims the bare domain inside the URI and
whichever pass runs first wins the range. Each match also gets a URLSpan:
the timeline only reports URLSpans as clicks, so without one the pill
would look right and do nothing.

The composer path is untouched, since it pillifies without permalinks.

Relates to https://github.com/element-hq/element-x-android/issues/5319
2026-08-09 04:09:32 +03:00
hayaksi1 1024323661 Send a message with Enter on a physical keyboard, Shift+Enter for a new line
With a hardware keyboard attached, Enter inserted a newline and the only
way to send was to reach for the send button, which is the opposite of
what every desktop chat client does.

MarkdownEditText gains an optional Enter listener and consumes Enter when
it comes from a physical keyboard without Shift. Shift+Enter keeps
inserting a newline, and a key event from the on-screen keyboard is
identified by its VIRTUAL_KEYBOARD device id and left alone, so nothing
changes for touch users. The key release is consumed as well, so no
newline leaks out on key repeat.

TextComposer passes its existing send lambda through, guarded by the same
canSendTextMessage check the send button already uses, so Enter cannot
send an empty message.

Relates to https://github.com/element-hq/element-x-android/issues/860
2026-08-09 04:09:30 +03:00
hayaksi1 717efe46c5 Do not offer to start a call in a DM with no other member
A DM whose other member has left still offered the call button. Starting
that call put the user alone in a room nobody could join, with no way out
of the call screen.

The presenter now clears canStartCall for a DM with no other active
member. activeMembersCount counts joined and invited members, so a fresh
DM whose peer has been invited but has not joined yet can still start a
call. Joining an already ongoing call is untouched, so a call started
before the other member left stays reachable.

Relates to https://github.com/element-hq/element-x-android/issues/6189
2026-08-09 04:09:27 +03:00
hayaksi1 1be87994b1 Never re-encode a video at a higher bitrate than the source
The compression config computed a target bitrate purely from the output
resolution and frame rate, ignoring the bitrate already extracted from the
source. A clip that is small because it was efficiently encoded, rather
than because it is short, was therefore re-encoded upwards: the upload got
slower and the resulting file was larger than the original.

The target is now clamped to the source bitrate when that is known.
METADATA_KEY_BITRATE is the container total, video plus audio, so it is a
safe upper bound for the video track alone. An unknown or zero bitrate
still falls back to the calculated value.

Relates to https://github.com/element-hq/element-x-android/issues/4191
2026-08-09 04:09:24 +03:00
hayaksi1 0ccb9b23cc Linkify URLs whose host is a bracketed IPv6 address
Patterns.WEB_URL, which LinkifyCompat uses to find links, has no
production for a bracketed IPv6 authority, so a URL such as
http://[2001:db8::1]:8008/path was never turned into a link. Worse, the
phone number matcher claimed the digit runs inside the address, leaving a
tel: link over part of it.

LinkifyHelper now runs its own pass for those URLs. It requires both a
scheme and the brackets, so array syntax like values[0]:1 is not matched,
and it runs after LinkifyCompat.addLinks, which strips any span set before
it, but before the pre-existing spans are restored, so real anchors and
pill spans keep their precedence. Any span Linkify left inside the address
is evicted, and the same trailing punctuation trimming as the other links
is applied.

Relates to https://github.com/element-hq/element-x-android/issues/2614
2026-08-09 03:51:49 +03:00
hayaksi1 75ab17af57 Stop a right to left sender name from reordering a room list preview
A room list preview is one paragraph, and its direction is resolved from
the first strong character. A sender whose display name is Arabic or
Hebrew therefore flipped the whole line: the message body moved to the
wrong side of the name and the separating colon jumped to the front.

The sender's display name is now wrapped in Unicode isolate characters
before it is used as a prefix, so its own direction cannot escape it. Both
places that interpolate the name are covered: the prefix path and the
emote path, which bypasses it.

prefixWith itself is deliberately left alone, since the pinned messages
banner shares it and only ever passes localised nouns. The "You" prefix is
localised rather than user-provided and is not isolated.

Relates to https://github.com/element-hq/element-x-android/issues/3338
2026-08-09 03:51:49 +03:00
hayaksi1 b5e95e27d4 Do not group timeline messages sent more than five minutes apart
Consecutive messages from the same sender were grouped into one bubble
block no matter how far apart they were sent, so a reply hours later
looked like part of the earlier burst and lost its own timestamp header.

computeGroupPosition now folds a five minute limit into the two
groupability flags it already computes. The delta is symmetric between a
pair of neighbours, so both items always agree on where the block splits
and every branch of the existing decision table keeps working unchanged.

Breaking a group on reactions, which the iOS client also does, is a
separate divergence and is not part of this.

Relates to https://github.com/element-hq/element-x-android/issues/4757
2026-08-09 03:51:48 +03:00
hayaksi1 e63c897ec2 Ignore a second voice recording start while one is already running
The composer emits Start whenever the voice message state is Idle, and the
state only leaves Idle once the recorder has produced its first audio
buffer. A second tap inside that window started a second recording, which
overwrote the output file, re-initialised the encoder while the first one
was still encoding, and orphaned the first job together with its
AudioReader.

The orphan kept the microphone open and kept emitting Recording for the
rest of the session, so Stop and Delete appeared to do nothing until the
room was left and re-entered, and re-initialising the encoder under an
active encode could crash natively.

DefaultVoiceRecorder.startRecord now runs under the mutex it already owns
and returns early when a recording job exists, so the second start is a
no-op: no file, no encoder re-init and no second AudioReader.

Relates to https://github.com/element-hq/element-x-android/issues/5780
2026-08-09 03:51:48 +03:00
hayaksi1 de0e628512 Show the account a UnifiedPush registration belongs to in the distributor
A distributor such as ntfy or DAVx5 lists every registration it holds, but
Element registered each one with no message for the distributor, so an
account added from a second session was indistinguishable from the first.

RegisterUnifiedPushUseCase.execute now takes the session id and passes it
as messageForDistributor, which the connector has accepted since 3.x. The
value is the raw Matrix ID, which is not personal data by the project's
own logging rules, and no new string resource is needed.

UnifiedPushProvider already has the MatrixClient in hand at the only call
site, so the PushProvider interface is unchanged and nothing outside the
module moves.

Relates to https://github.com/element-hq/element-x-android/issues/6426
2026-08-09 03:51:48 +03:00
hayaksi1 17365a50d1 Keep saving files to Downloads once 32 files share the same name
MediaStore de-duplicates the display name of a new file itself, but its
uniquifier gives up after 32 collisions and throws IllegalStateException.
Saving then failed with "Sorry, an error occurred" and wrote nothing, and
because image.png is the name most screenshots arrive with, a user who
saves media regularly could reach a state where that name never saved
again without deleting files from Downloads by hand.

A new saveWithUniqueFileName helper in androidutils retries the insert
once with a suffixed name when the platform reports it cannot build a
unique one; a second failure propagates so genuine errors still surface.
withFileNameSuffix builds that name by inserting the suffix before the
extension, and handles names without one.

Both call sites are fixed: AndroidLocalMediaActions and DefaultFileSave
carried byte-identical logic and therefore the identical defect. While in
AndroidLocalMediaActions, a null output uri from ContentResolver.insert
now throws instead of reporting success without writing anything.

The pre-Android-Q path is untouched: it overwrites rather than
uniquifying, so it never reaches this limit.

Relates to https://github.com/element-hq/element-x-android/issues/6371
2026-08-09 01:59:25 +03:00
hayaksi1 e8ca16b74f Ignore a room list filter that is incompatible with an already selected one
Some room list filters are mutually exclusive, and the UI expresses that
by hiding the incompatible chips rather than disabling them. Nothing
enforced the rule on the write path, so two chips tapped in the same
frame, before the list could recompose, both applied. The room list then
matched nothing while the offending chip was hidden, leaving no way to
see or undo what was filtering the rooms.

DefaultFilterSelectionStrategy.select() now ignores a filter that is
incompatible with one already selected, so the same table buildFilters()
uses to hide chips also governs the selection itself. The currently
applied filters win and the late tap is dropped, which matches the
existing design where an incompatible chip is already on its way out of
the UI when it is tapped.

toggle(), deselect(), clear() and the FilterSelectionStrategy interface
are unchanged.

Relates to https://github.com/element-hq/element-x-android/issues/5383
2026-08-09 01:59:25 +03:00
hayaksi1 c7d8973507 Do not append the application MapTiler key to a custom map style url
When a homeserver publishes a map_style_url in its .well-known file, the
app appended its own MapTiler API key to it, producing a URL with two
query strings that the tile server rejects. The map then failed to load
for every user of that homeserver, with no way for the operator to opt
out.

MapTilerTileServerStyleUriBuilder.build() now appends ?key= only to the
URL it composes itself from the bundled base url and map ids. A non
blank customMapStyleUrl is returned verbatim, so the operator's own
credentials survive. This matches Element Web and the self-hosted tile
server documentation, both of which treat map_style_url as complete.

The two existing tests that pinned the old behaviour are updated, and
cases are added for a custom URL that already carries a key, one that
carries an unrelated query string, and a blank url falling back to the
built-in style.

Relates to https://github.com/element-hq/element-x-android/issues/7289
2026-08-09 01:59:25 +03:00
Jenna Vassar d7e6b64265 Dismiss the keyboard on continue and restore it after an error 2026-08-07 12:46:11 -07:00
Jenna Vassar 8c2cf9d95e Reference the Figma design on the account provider screen 2026-08-07 12:04:01 -07:00
Benoit Marty e66efcfc43 [LinkNewDevice] Disable QrCode rotation
#7303
2026-08-07 17:46:47 +02:00
Benoit Marty 2b2d86a804 Use Kotlin Duration. 2026-08-07 17:46:46 +02:00
Benoit Marty 4485c8a4c0 [LinkNewDevice] Fix second and next attempts to unlock using biometric failures.
`cipher.doFinal` cannot be called several times.
2026-08-07 17:46:46 +02:00
Benoit Marty 33979b6574 [LinkNewDevice] Add a 2 minutes timer to scan a QrCode
#7303
2026-08-07 17:46:46 +02:00
Benoit Marty 044737a3a7 Merge pull request #6719 from element-hq/feature/bma/qrCodeLoginIteration
QR code login iteration
2026-08-07 17:31:50 +02:00
Benoit Marty 668243f658 Use value instead of first(). 2026-08-07 16:43:52 +02:00
Benoit Marty 8f8c3af8c5 [LinkNewDevice] Log continuation / cancellation failure. 2026-08-07 16:42:49 +02:00
ganfra 06862ff3b2 Test synchronizes on profile persistence instead of advanceUntilIdle 2026-08-07 16:30:03 +02:00
bxdxnn d36daca242 Fix 2026-08-07 13:44:43 +00:00
bxdxnn 221f13269e Fix 2026-08-07 13:42:16 +00:00
bxdxnn a04078aa9d Address review 2026-08-07 13:38:20 +00:00
ganfra 7d5877c659 Merge pull request #7330 from element-hq/renovate/constraintlayout.compose
Update dependency androidx.constraintlayout:constraintlayout-compose to v1.1.2
2026-08-07 14:44:47 +02:00
ganfra 9b39843e69 Add withProfilesExtension override to FakeFfiSyncServiceBuilder 2026-08-07 14:22:52 +02:00
renovate[bot] df925c29e4 Update dependency org.jsoup:jsoup to v1.23.1 (#7332)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Benoit Marty <benoitm@element.io>
2026-08-07 14:06:22 +02:00
Benoit Marty 64ba29f91a Merge branch 'develop' into feat/polls-max-selections 2026-08-07 14:03:17 +02:00
Benoit Marty 07ca468847 Merge pull request #7136 from element-hq/fix/a11y-invite-people
Announce checked/unchecked state for user rows in invite screen
2026-08-07 14:00:01 +02:00
ElementBot b7c0374c7c Update screenshots 2026-08-07 09:53:47 +00:00
ganfra 14c0944d22 Merge branch 'develop' into feature/fga/user_status_last_pieces 2026-08-07 11:35:19 +02:00
Benoit Marty 293faec03b Merge pull request #7175 from element-hq/renovate/androidx.compose.material3-material3-1.x
Update dependency androidx.compose.material3:material3 to v1.5.0-alpha23
2026-08-07 10:45:22 +02:00
ganfra 8d2686d1a9 Merge branch 'develop' into feature/fga/user_status_last_pieces 2026-08-07 10:12:32 +02:00
renovate[bot] e1daec6728 Update dependency androidx.constraintlayout:constraintlayout-compose to v1.1.2 2026-08-07 08:06:16 +00:00
renovate[bot] 8a164228bd Update dependency com.google.firebase:firebase-bom to v34.17.0 (#7333)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Benoit Marty <benoitm@element.io>
2026-08-07 10:02:28 +02:00
Benoit Marty b5e9f42058 Fix compilation issue 2026-08-07 09:52:54 +02:00
ElementBot 0e1cb2df5e Update screenshots 2026-08-07 07:20:49 +00:00
ElementBot ed44cd776c Update Gradle Wrapper from 9.6.1 to 9.7.0 (#7335)
Signed-off-by: gradle-update-robot <gradle-update-robot@regolo.cc>
Co-authored-by: gradle-update-robot <gradle-update-robot@regolo.cc>
2026-08-07 09:12:21 +02:00
renovate[bot] e3ce557576 Update metro to v1.4.0 (#7334)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-07 09:08:52 +02:00
Benoit Marty 701cf4b2f9 Merge branch 'develop' into renovate/androidx.compose.material3-material3-1.x 2026-08-07 09:06:19 +02:00
Jenna Vassar d04a3853b4 Add inline autocomplete for the account provider field 2026-08-06 12:11:18 -07:00
Jenna Vassar c2b3b0fe0f Remove the account provider search screen and homeserver resolver 2026-08-06 11:31:32 -07:00
Jenna Vassar df6ccd9dc3 Consolidate account provider selection into a single editable screen 2026-08-06 11:31:25 -07:00
renovate[bot] d3f35cb90c Update dependency io.element.android:element-call-embedded to v0.23.0 (#7323)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-06 18:39:06 +02:00
renovate[bot] 73119273c2 Update dependency org.maplibre.gl:android-sdk to v13.4.1 (#7305)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-06 18:38:49 +02:00
renovate[bot] 87b56861e6 Update dependency io.sentry:sentry-android to v8.51.0 (#7301)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-06 18:38:21 +02:00
Benoit Marty e568f1ec7a Fix deprecation warning on androidx.compose.material3.ListItem 2026-08-06 18:37:25 +02:00
Benoit Marty c8c8971889 Fix detekt false positive 2026-08-06 18:35:18 +02:00
Benoit Marty d1cf1aa09f Fix deprecation warning on androidx.compose.material3.ListItem 2026-08-06 18:35:17 +02:00
renovate[bot] d2b4a85453 Update dependency androidx.compose.material3:material3 to v1.5.0-alpha23 2026-08-06 18:35:17 +02:00
renovate[bot] b1b52da38e Update reactivecircus/android-emulator-runner action to v2.38.0 (#7189)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-06 18:32:08 +02:00
renovate[bot] f8ce3e9b70 Merge pull request #7329 from element-hq/renovate/constraintlayout
Update dependency androidx.constraintlayout:constraintlayout to v2.2.2
2026-08-06 18:31:36 +02:00
Benoit Marty dfe0dedf37 Merge pull request #7331 from element-hq/feature/bma/fixOverlap
Fix overlap issue.
2026-08-06 18:29:38 +02:00
ganfra 0e563332ce Clean up formatting/detekt 2026-08-06 17:28:10 +02:00
Benoit Marty 9c66cd331f [Link new device] Handle split steps in the SDK 2026-08-06 17:22:10 +02:00
Benoit Marty 6cd9ab0f4e Merge pull request #7241 from element-hq/feature/bma/about
Move deviceId and git details to developer settings
2026-08-06 16:36:18 +02:00
Benoit Marty 7adaeca7c7 Fix overlap issue.
Fixes #7327
2026-08-06 16:17:17 +02:00
ganfra a7d2b86fe0 Show loading and failure feedback when updating user status 2026-08-06 16:15:44 +02:00
Benoit Marty 670ddd35f4 Fix compilation warning 2026-08-06 15:10:25 +02:00
Benoit Marty beccb1ddfa Fix tests 2026-08-06 15:03:13 +02:00
ElementBot 9d27ab4caf Update screenshots 2026-08-06 14:46:29 +02:00
Benoit Marty 10f24e2b8d Send gitSha in the bug report content.
See https://github.com/element-hq/element-x-android-rageshakes/issues/14270
2026-08-06 14:46:28 +02:00
Benoit Marty 4dffd7ddef Move deviceId, gitBranch and gitSha to the developer screens. 2026-08-06 14:46:28 +02:00
ganfra c35a0a3864 Sync automatic call status with user status flag and server support 2026-08-06 11:09:49 +02:00
ganfra 93ca5374d6 Add status emoji badge to current user avatar in home top bar 2026-08-05 16:14:59 +02:00
ganfra 863cece4fb Subscribe to own profile updates and gate on server support 2026-08-05 16:14:59 +02:00
ganfra 494dc026fa Create fun ProfileDetails.toMatrixUser method 2026-08-05 16:14:59 +02:00
ganfra 489ef4666a Map displayed status for room members and timeline events 2026-08-05 16:14:59 +02:00
ganfra ac6cacb415 Add MatrixClient.isUserStatusSupported 2026-08-05 16:14:59 +02:00
ganfra 38102747ad Wire user status through the Rust SDK and refresh profile after change
# Conflicts:
#	libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt
2026-08-05 16:14:59 +02:00
ganfra 98c686dd5b Merge pull request #7324 from element-hq/renovate/org.matrix.rustcomponents-sdk-android-26.x
Update dependency org.matrix.rustcomponents:sdk-android to v26.08.05
2026-08-05 16:14:35 +02:00
renovate[bot] b327b435ab Update dependency org.matrix.rustcomponents:sdk-android to v26.08.05 2026-08-05 13:55:15 +00:00
ganfra 08f205eab0 Merge pull request #7322 from element-hq/renovate/io.github.zxing-cpp-android-3.x
Update dependency io.github.zxing-cpp:android to v3.1.1
2026-08-05 15:52:26 +02:00
renovate[bot] 6c6f596b1a Update dependency io.github.zxing-cpp:android to v3.1.1 2026-08-05 09:20:04 +00:00
ganfra cd89f755a0 Merge pull request #7317 from element-hq/renovate/actions-stale-11.x
Update actions/stale action to v11
2026-08-05 11:19:10 +02:00
ganfra bb785676bf Merge pull request #7318 from element-hq/renovate/dependencyanalysis
Update dependencyAnalysis to v3.18.0
2026-08-05 11:18:38 +02:00
ganfra e64b0dcc43 Merge pull request #7311 from element-hq/renovate/com.posthog-posthog-android-3.x
Update dependency com.posthog:posthog-android to v3.56.4
2026-08-05 11:18:16 +02:00
renovate[bot] cdc4ac73fc Update actions/stale action to v11 2026-08-04 20:49:57 +00:00
renovate[bot] 6f1906300c Update dependency com.posthog:posthog-android to v3.56.4 2026-08-04 20:49:39 +00:00
renovate[bot] 28b90a91d0 Update dependencyAnalysis to v3.18.0 2026-08-04 00:53:09 +00:00
ganfra d572a78612 Merge pull request #7203 from element-hq/matthew/lls-push-notifications
Render push notifications for live location share starts (MSC4505)
2026-08-03 21:43:09 +02:00
Matthew Hodgson 0a577ffbb8 Render push notifications for live location share starts 2026-08-03 21:01:56 +02:00
Jenna Vassar 3c09fb37b9 Save the account provider to history on successful sign-in 2026-08-03 11:18:05 -07:00
Jenna Vassar 4714127cc0 Default the account provider to the most recently used one 2026-08-03 10:54:45 -07:00
Jenna Vassar 21471e07fb Add persistent account provider history to app preferences 2026-08-03 09:07:48 -07:00
Jenna Vassar 50ff04ac88 Bump enterprise module to the latest commit (#7309) 2026-07-31 18:52:22 +02:00
Jorge Martin Espinosa 54a8770f31 Bump enterprise module to the latest commit (#7307) 2026-07-31 09:46:40 +02:00
renovate[bot] c70bd32f59 Update android.gradle.plugin to v9.3.1 (#7304)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-31 09:05:56 +02:00
renovate[bot] 1a07fdfed0 Update zizmorcore/zizmor-action action to v0.6.1 (#7306)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-31 09:03:28 +02:00
Jorge Martin Espinosa 52d146a59d CI: split unit tests, screenshots and coverage in tests.yml (#7296)
* Check if splitting the steps for testing and generating the reports improves performance and keeps the coverage stats

* Exclude Konsist tests and include `:app` ones

* Remove the extra dependency of `koverVerifyAll` on `:app:koverVerify{variant}`
2026-07-30 11:51:18 +02:00
hayyaksi e6d4472c0c Index messages locally in an encrypted on-device store so encrypted rooms can be searched at all (#7249)
* Add a message search abstraction over the Rust SDK's local index

Wraps the SDK's search service so the rest of the app never sees MatrixRustSDK
types, following the existing convention in libraries/matrix.

- MessageSearch, MessageSearchService, MessageSearchResult and
  MessageSearchPaginationState describe the surface in the api module.
- RustMessageSearch and RustMessageSearchService implement it, with
  MessageSearchResultMapper and MessageSearchResultsProcessor turning SDK results
  into Kotlin data classes.
- TantivyQueryEscaper escapes the query so a user typing ":)" or "AND" searches
  for that literal text rather than tripping the tantivy parser, and joins every
  word as a Must clause.
- Fakes and unit tests cover the mapping, the processor and the escaper, the last
  checked against a real tantivy 0.26.1 parser.

* Attach the encrypted search index behind a disabled feature flag

Gives the client an on-device tantivy index encrypted with the session's client
secret, so encrypted rooms can be searched at all.

- The index store is attached at ClientBuilder time via withSearchIndexStore, in
  a search-index directory alongside the session, keyed on the client secret. A
  session without one gets no index.
- MatrixClient exposes isMessageSearchAvailable so callers gate on the real
  indexing capability rather than on the raw flag, and getCacheSize() counts the
  index directory so it stays visible in the settings figures.
- FeatureFlags.MessageSearch (feature.message_search) is off by default and
  isFinished = false. It is read once at ClientBuilder time, so develop stays
  releasable and nothing reaches a user.

* Simplify search index wiring following review

Tie search availability to the MessageSearch feature flag alone and let
the index be created without a client secret, encrypting it only when
the session has one, matching the SDK's SQLite stores. Make
getBaseClientBuilder take the flag as a required parameter and return
ClientBuilder directly again, dropping the BaseClientBuilder wrapper
and the mid-restore snapshot parameter. Check the flag through
FeatureFlagService in RustMatrixAuthenticationService instead of
caching it in a field, and revert the extra matrix_sdk_search tracing
target, which belongs in the SDK's defaults.
2026-07-30 11:17:40 +02:00
Jorge Martin Espinosa af9db20f4f Add extra vertical padding to ReplyToInvalidContent and ReplyToNotFoundContent (#7288)
* Add extra vertical padding to `ReplyToInvalidContent` and `ReplyToNotFoundContent`

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
2026-07-30 10:30:22 +02:00
Jorge Martin Espinosa 9ab679566d Don't check enterprise previews in KonsistPreviewTest (#7297)
Don't check enterprise previews in `KonsistPreviewTest`
2026-07-30 10:29:43 +02:00
renovate[bot] bac77b7645 Update dependency io.sentry:sentry-android to v8.50.0 (#7298)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-30 09:43:45 +02:00
renovate[bot] e46d36ec64 Update android.gradle.plugin to v9.3.0 (#7248)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-30 09:40:09 +02:00
ganfra 2a1fd60ad7 Merge pull request #7291 from element-hq/renovate/org.maplibre.compose-maplibre-compose-0.x
Update dependency org.maplibre.compose:maplibre-compose to v0.13.1
2026-07-29 19:56:34 +02:00
ganfra 67b5bddf4e Changelog for version 26.08.0 2026-07-29 18:31:11 +02:00
ganfra a598ff263f Merge branch 'release/26.08.0'
# Conflicts:
#	tools/manifest/gplay/release/aaptDump.txt
2026-07-29 17:46:31 +02:00
ganfra 0770ff04c7 Adding fastlane file for version 26.08.0 2026-07-29 17:44:30 +02:00
ganfra ae74546ee4 Setting version for the release 26.08.0 2026-07-29 17:44:12 +02:00
ganfra 2e480f93ed Merge pull request #7293 from element-hq/ci/run-konsist-tests-sequentially
Run `Konsist` tests sequentially
2026-07-29 13:58:09 +02:00
Jorge Martín d711922325 Remove test for detecting m prefix for fields
I'm not sure anyone will actually use `m` prefixes on Kotlin anymore
2026-07-29 13:28:57 +02:00
Jorge Martín ddc972a365 Run Konsist tests sequentially
It's sadly faster that way:

Every time we run a test class in parallel a new JVM is started, and the `KoScope` contents can't be shared between them.
This scope can take a few minutes to load, so having several instances of this happening at the same time is a terrible idea.
It's actually faster to run the tests sequentially, at least locally.
2026-07-29 13:28:22 +02:00
Jorge Martin Espinosa 49692794f9 Allow adding a path suffix to the OAuth client URI (#7286)
* Allow adding a path suffix to the OAuth client URI
2026-07-29 09:29:44 +00:00
Jorge Martin Espinosa 674354b1a6 Enforce HS disabling E2EE in DM creation too (Element Pro) (#7292)
This part was missing from the implementation of this Element Pro feature, which incorrectly only applied to rooms.
2026-07-29 09:17:41 +00:00
renovate[bot] eb18770378 Update dependency org.maplibre.compose:maplibre-compose to v0.13.1 2026-07-29 07:55:56 +00:00
renovate[bot] 00dcf48f28 Update dependency org.maplibre.gl:android-sdk to v13.4.0 (#7290)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-29 09:53:27 +02:00
ganfra cc75e9386c Merge pull request #7287 from element-hq/renovate/org.matrix.rustcomponents-sdk-android-26.x
Update dependency org.matrix.rustcomponents:sdk-android to v26.07.28
2026-07-29 02:03:53 +02:00
ganfra 8b970fb2f1 Fix license header on FakeFfiContinuationMessageSender 2026-07-29 01:21:05 +02:00
ganfra 3fc8972464 Fix unit test compilation for new continuationSender param on WaitingForAuth 2026-07-28 18:08:10 +02:00
ganfra 3933d5ab8c Merge pull request #7277 from element-hq/sync-localazy
Sync Strings
2026-07-28 16:44:21 +02:00
ganfra bd4d0cb02b Fix compilation for QR login continuation error variants added in SDK 26.07.28 2026-07-28 16:44:07 +02:00
renovate[bot] 154d79d4b5 Update dependency org.matrix.rustcomponents:sdk-android to v26.07.28 2026-07-28 13:45:07 +00:00
Jenna Vassar c2e8676773 Update keyboard visibility to track hasFocus (#7284) 2026-07-28 15:35:27 +02:00
ElementBot f5b0195852 Update screenshots 2026-07-28 13:17:45 +00:00
ganfra ea3788abd1 Sync Strings from Localazy 2026-07-28 14:51:37 +02:00
Jenna Vassar fe24aeb2ce Add a password strength estimator wrapping the SDK zxcvbn API (#7186)
* Add a password strength estimator wrapping the SDK zxcvbn API

* Add tests for the password strength estimator SDK mapping

---------

Co-authored-by: ElementBot <android@element.io>
2026-07-28 10:30:06 +00:00
Jorge Martin Espinosa 0cd163a680 Import Compound tokens from v10.2.3 (#7272)
* Import Compound tokens from `v10.2.3`

* Remove manually added `bgSubtleTertiary` semantic color: it's in Compound now

* Bump enterprise module to a compatible commit
2026-07-28 09:07:24 +00:00
Jorge Martin Espinosa adc1b50492 Fix insets in several screens (#7285)
* Fix insets in several screens

UI components in the room list, room member list, etc. now have better insets based on `WindowInsets.safeDrawing`

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
2026-07-28 10:29:42 +02:00
ganfra c685c356ce Merge pull request #7282 from element-hq/renovate/actions-checkout-7.x
Update actions/checkout action to v7.0.1
2026-07-28 10:02:56 +02:00
Matt Van Horn 9042b4be4f Make settings section separators visible in black theme (#7039)
* Make settings section separators visible in black theme

In the Black (OLED) theme the canvas is forced to pure black while the
section-separator divider keeps its standard-dark colour (outlineVariant,
~10% alpha white), leaving dividers in Settings and related screens almost
invisible. Add an OLED-aware listSectionDivider alias that lightens the
divider only when the black theme is active, and use it for the
ListSectionHeader divider. Light and standard-dark themes are unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>

* Set separatorSecondary for Black theme instead of a custom divider alias

Following review feedback: now that the global HorizontalDivider uses
separatorSecondary (#7054), give the Black/OLED theme a visible
separatorSecondary value in ElementTheme instead of carrying a bespoke
listSectionDivider alias. This fixes all separators on pure black, not
just section headers. The exact color is pending design confirmation.

* chore(theme): remove in-code design discussion comment

Move design discussion out of the code per review; the divider color
decision lives in the PR thread.

* Update screenshots

Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>

---------

Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: ElementBot <android@element.io>
Co-authored-by: Jorge Martin Espinosa <jorgem@element.io>
2026-07-28 09:51:15 +02:00
Jorge Martin Espinosa cc3c68411e Try making HeaderFooterView more robust (#7267)
* Try making `HeaderFooterView` more robust

Create `HeaderFooterLayout` to properly calculate the content size when the component is scrollable so it's at least the size of the available space.

* Make `DesktopNoticeView` scrollable

For this, `NumberedListOrganism` can't be a `LazyColumn`, since that's a lazy layout with no intrinsic height and Compose doesn't know how to size it.

* Fix the case where no header or content composables are supplied

* Fix insets in non-scrollable UI

* Remove `movableContentOf`, suppress the warning instead since the `isScrollable` parameter should never toggle this behavior

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
2026-07-28 09:38:56 +02:00
renovate[bot] 03639caa75 Update actions/checkout action to v7.0.1 2026-07-27 16:18:01 +00:00
Jorge Martin Espinosa 77edfec61a Use the right insets in the home screen UI (#7271)
* Use the right insets in the home screen UI

That's `WindowInsets.statusBar` for the `Scaffold` component, then `WindowInsets.navigationBar` for the `LazyColum.contentPadding`

* Apply it to other screens with long lists of items too

* Apply the insets to `PreferencePage` too

* Apply inset changes to other screens with scrollable content

* Add helper insets/padding values based on review suggestions.

* Use the newly added insets/padding values in several scaffold and scrollable columns

---------

Co-authored-by: ElementBot <android@element.io>
2026-07-27 18:17:14 +02:00
Jorge Martin Espinosa 0442fbd307 Fix media viewer for medias not related to events displaying invalid state (#7280)
* When no URL is provided to `ContentValidationState.collectMediaState`, assume it's valid

That way we can do:

`if (validationState.isValid() && state.collectMediaState(possiblyNullUrl).isValid()) ...`

* Remove the `eventId` param from `ContentScannerService.scan`

It's not really used, since we then provide a state that can be mutated, and fetching that is what the previous parameter was used for

* Make sure to always validate the medias in the viewer, both the actual media and the thumbnail

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
2026-07-27 18:16:35 +02:00
Jorge Martin Espinosa d8b01cf3e7 Fix ContentErrorView not calculating its text size synchronously (#7281)
It was using a `LaunchedEffect` instead of relying on the `Text(onTextLayout)` parameter.
2026-07-27 18:15:33 +02:00
Jorge Martin Espinosa bbb83c2ff0 Localazy: improve search, parallelize checks (#7261)
* Remove no longer temporary string

These are now in localazy and the sync strings CI flow was failing because we have 2 conflicting values in `temporary.xml` and `localazy.xml`

* Use the right placeholder value in `screen_local_network_opt_in_subtitle`

* Localazy: improve search, parallelize checks

The search for XML files has been narrowed down and it's several times faster now, and processing the files is now done in parallel

* Improve script to also find and delete previous files

Also add the option of having a `-v|--verbose` flag to print which files we're modifying

* Use `LocalBuildMeta` to get the app name inside `LocalNetworkPermissionDialogView`

* Allow finding all the forbidden terms in the strings and translations and then return an error exit code instead of a success one

---------

Co-authored-by: ElementBot <android@element.io>
2026-07-27 15:12:53 +02:00
renovate[bot] 0e3d479490 Update roborazzi to v1.70.0 (#7274)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-27 10:56:30 +02:00
renovate[bot] 4d9bbd67c3 Update actions/setup-python action to v7 (#7278)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-27 10:56:07 +02:00
renovate[bot] 91ec818522 Update dependency org.jetbrains.kotlinx:kover-gradle-plugin to v0.9.9 (#7273)
* Update dependency org.jetbrains.kotlinx:kover-gradle-plugin to v0.9.9

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: ElementBot <android@element.io>
2026-07-27 09:23:13 +02:00
bmarty 892b384f07 Sync Strings from Localazy 2026-07-27 01:07:51 +00:00
ganfra 66f6852f8a Merge pull request #7268 from element-hq/renovate/actions-setup-java-5.x
Update actions/setup-java action to v5.6.0
2026-07-24 18:25:09 +02:00
ganfra e798eed816 Merge pull request #7051 from element-hq/feature/valere/timeline_active_call_rendering
feat(call): Active call timeline rendering
2026-07-24 13:43:43 +02:00
ganfra bfb2cc4678 Fix CallNotifyContent constructor call in EventItemFactoryTest 2026-07-24 11:35:34 +02:00
ganfra 1e2085260c Merge branch 'develop' into renovate/actions-setup-java-5.x 2026-07-24 11:05:18 +02:00
Jorge Martin Espinosa 1595830541 Remove no longer temporary strings (#7260)
* Remove no longer temporary string

These are now in localazy and the sync strings CI flow was failing because we have 2 conflicting values in `temporary.xml` and `localazy.xml`

* Use the right placeholder value in `screen_local_network_opt_in_subtitle`

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
2026-07-24 09:00:45 +00:00
ganfra 61ea9cf2b3 Merge pull request #7266 from element-hq/feature/fga/searchbar-result-state-refactor
Refactor SearchBarResultState.Initial/NoResultsFound to data objects
2026-07-24 09:58:15 +02:00
renovate[bot] 5ee8bc2fa7 Update dependencyAnalysis to v3.17.0 (#7265)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-24 09:04:48 +02:00
renovate[bot] bce559dc1b Update actions/setup-java action to v5.6.0 2026-07-23 16:52:37 +00:00
ElementBot f5b05f93c1 Update screenshots 2026-07-23 16:07:28 +00:00
ganfra 29d9e7c1ba Merge branch 'develop' into feature/valere/timeline_active_call_rendering
# Conflicts:
#	tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.customreaction_EmojiItemWithPopup_Night_0_en.png
2026-07-23 16:24:11 +02:00
ganfra 9d427077f0 Refactor SearchBarResultState.Initial and NoResultsFound to data objects 2026-07-23 16:15:02 +02:00
ElementBot 44f027514e Update screenshots 2026-07-23 13:48:37 +00:00
Jorge Martin Espinosa 72c47f7c41 Add warning to the 'Scan QR code' screen (#7264)
* Add warning to the 'Scan QR code' screen

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
2026-07-23 15:38:33 +02:00
ganfra 907579d59f Merge pull request #7232 from element-hq/feature/fga/display_user_status
WIP : display user status
2026-07-23 15:10:19 +02:00
Valere 0f43b9b79a fix: test compilation 2026-07-23 14:57:12 +02:00
ganfra b83996d70d Merge pull request #7257 from element-hq/feature/fga/user-status-emoji-picker
Plug emoji picker for custom user status
2026-07-23 14:39:55 +02:00
ganfra b839d4a2e1 Merge pull request #7263 from element-hq/renovate/io.sentry-sentry-android-8.x
Update dependency io.sentry:sentry-android to v8.49.0
2026-07-23 14:27:10 +02:00
ganfra 93786cdcbb Use roomMember.getBestName() in SuggestionPickerView 2026-07-23 14:23:05 +02:00
ganfra eaf987877f Merge pull request #7262 from element-hq/renovate/org.matrix.rustcomponents-sdk-android-26.x
Update dependency org.matrix.rustcomponents:sdk-android to v26.07.23
2026-07-23 14:02:28 +02:00
ganfra 075cca2d21 Sync emoji picker translations from Localazy 2026-07-23 13:56:30 +02:00
renovate[bot] ff7f54c416 Update dependency io.sentry:sentry-android to v8.49.0 2026-07-23 11:41:57 +00:00
renovate[bot] 0d6c35a4a2 Update dependency org.matrix.rustcomponents:sdk-android to v26.07.23 2026-07-23 11:41:48 +00:00
Valere f22e4b3314 Merge branch 'develop' into feature/valere/timeline_active_call_rendering 2026-07-23 13:40:38 +02:00
Valere 19aabd4211 review cleanup 2026-07-23 13:40:08 +02:00
ganfra 14feeca2a5 Add some comments after PR review 2026-07-23 11:25:40 +02:00
ElementBot 2f09585b81 Update screenshots 2026-07-22 19:22:11 +00:00
ganfra e4f8139791 Sync emoji picker translations from Localazy 2026-07-22 21:00:16 +02:00
ganfra fa537cce28 Merge pull request #7240 from element-hq/gradlew-update-9.6.1
Update Gradle Wrapper from 9.5.1 to 9.6.1
2026-07-22 18:37:50 +02:00
ganfra 471b80af18 Fix duplicate string resources after rebase 2026-07-22 18:32:35 +02:00
renovate[bot] 8d9bec57d7 Update zizmorcore/zizmor-action action to v0.6.0 (#7256)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-22 17:58:57 +02:00
ganfra b9831734a0 Take DisplayedStatus directly in DisplayNameWithStatus and always show emoji 2026-07-22 17:28:30 +02:00
ganfra 9c3fe5ad23 Drop mutating setter for MentionSpan trailingText 2026-07-22 17:28:30 +02:00
ganfra 7935dc2ac7 Restore disabled-state color on UserRow AnnotatedString overload 2026-07-22 17:28:30 +02:00
ganfra 3fdd6be832 Extract dmUserStatus helper for DM user status resolution 2026-07-22 17:28:30 +02:00
ganfra 2152552daa Add displayedStatus parameter to test fixtures for user status 2026-07-22 17:28:30 +02:00
ElementBot 140074f23c Update screenshots 2026-07-22 17:28:30 +02:00
ganfra 2a86c69b9d Sync user status strings with Localazy and tidy formatting
# Conflicts:
#	libraries/ui-strings/src/main/res/values/temporary.xml
2026-07-22 17:28:29 +02:00
renovate[bot] f21eb287c7 Update dependency org.matrix.rustcomponents:sdk-android to v26.07.22 (#7258)
* Update dependency org.matrix.rustcomponents:sdk-android to v26.07.22

* Fix test fixture for rust's `RoomHero`

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jorge Martín <jorgem@element.io>
2026-07-22 15:27:37 +00:00
ganfra 06a903718b Display user status next to display names across rooms and profiles 2026-07-22 17:27:21 +02:00
Jorge Martin Espinosa 18034e02a2 Fix banned mime type errors in DefaultContentScannerService (#7255)
The content scanner allows disabling certain mime types for files in the set up HS, but it turns out for the scan operation instead of returning an `"is_valid": false` result in that case, it returns an error with a `MCS_MIME_TYPE_FORBIDDEN` reason. We need to handle that case as if it had returned the false `isValid` value.
2026-07-22 16:48:44 +02:00
ganfra 19c38f7e7f Fix lint, detekt and konsist checks 2026-07-22 16:46:58 +02:00
ElementBot e24e15a0c4 Update screenshots 2026-07-22 14:25:10 +00:00
ganfra 4dadb3c8c8 Wire the emoji picker into the user-status feature 2026-07-22 15:48:55 +02:00
ganfra 89deff8a53 Add libraries/emoji/test module with picker fakes 2026-07-22 15:48:54 +02:00
ganfra 93d6d5c84c Migrate EmojiPickerPresenter to assisted-factory pattern with EmptyGetRecentEmojis 2026-07-22 15:48:54 +02:00
ganfra 5d8290b240 Extract EmojiPicker into libraries/emoji with Renderer/Presenter pattern 2026-07-22 15:48:54 +02:00
ganfra e492f06386 Migrate libraries/recentemojis into libraries/emoji 2026-07-22 15:48:54 +02:00
Jorge Martin Espinosa 05917bd65f Fix padding for captions of media in 'not found' state (#7247)
* Fix padding for captions of media in 'not found' state

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
2026-07-22 13:36:21 +02:00
ganfra 53f977805c Merge branch 'develop' into gradlew-update-9.6.1 2026-07-21 15:34:15 +02:00
ganfra c5d50bc859 Merge pull request #7243 from element-hq/renovate/metro
Update metro to v1.3.2
2026-07-21 15:20:57 +02:00
ganfra 97b42f0bad Merge pull request #7245 from element-hq/renovate/roborazzi
Update roborazzi to v1.68.0
2026-07-21 15:20:22 +02:00
renovate[bot] 8267901260 Update kotlin to v2.4.10 (#7244)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-21 09:42:51 +00:00
renovate[bot] 5a7c5e6fe7 Update dependency io.element.android:element-call-embedded to v0.22.0 (#7161)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-21 11:20:35 +02:00
renovate[bot] fbffcb5c68 Update roborazzi to v1.68.0 2026-07-21 09:04:41 +00:00
ganfra b18c1f892b Merge pull request #7214 from element-hq/renovate/actions-setup-java-5.x
Update actions/setup-java action to v5.5.0
2026-07-21 11:00:52 +02:00
ganfra 13c7275531 Merge pull request #7212 from element-hq/renovate/dependencyanalysis
Update dependencyAnalysis to v3.16.1
2026-07-21 11:00:15 +02:00
renovate[bot] eddac3eaaa Update actions/setup-java action to v5.5.0 2026-07-21 08:21:05 +00:00
renovate[bot] 5fc212fa26 Update metro to v1.3.2 2026-07-21 08:20:52 +00:00
renovate[bot] 5ddb56572a Update dependencyAnalysis to v3.16.1 2026-07-21 08:20:43 +00:00
Jorge Martin Espinosa 925376b6af Allow adding extra elements to the developer settings from external modules (#7230)
* Add `ExtraDeveloperOptionsRenderer` to display extra UI components in the developer settings screen. It defaults to not displaying anything, using `NoopExtraDeveloperOptionsRenderer`

* Add `EnterpriseService.overriddenElementWellKnown` method to fetch the current overridden element well known value, if any

* Modify the well known retrievers to use the overridden value if it's there. Also extract the JSON parsing to its own `ElementWellKnownParser` component.

* Bump enterprise repo reference.
2026-07-21 10:18:56 +02:00
ganfra 10161a686f Merge pull request #7237 from element-hq/renovate/com.google.firebase-firebase-bom-34.x
Update dependency com.google.firebase:firebase-bom to v34.16.0
2026-07-20 15:42:49 +02:00
Jürgen Wischer 3cef8c2796 Announce expanded/collapsed state on the timeline group header (#7226)
* Announce expanded/collapsed state on the timeline group header

GroupHeaderView (the accordion-style header used for grouped state
events in the timeline) uses toggleable() combined with
clearAndSetSemantics { contentDescription = text }. The
clearAndSetSemantics block discards the state information that
toggleable() would otherwise expose, so TalkBack never announces
whether the group is currently expanded or collapsed.

Add an explicit stateDescription (two new a11y_state_expanded /
a11y_state_collapsed strings in temporary.xml) alongside the existing
contentDescription so the expanded/collapsed state is communicated to
assistive technology.

Internal note: this single-file/single-root-cause fix addresses three
separate BITV findings raised in the same accessibility audit against
this component's toggle semantics.

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
2026-07-20 08:50:51 +02:00
Benoit Marty 968d271f2c Fix checkDependencies.py, looks like gradle 9.6.1 surrounds the dependencies with ' 2026-07-17 16:38:41 +02:00
Jorge Martin Espinosa 4a9fd056eb CI: fix 'Update Gradle Wrapper' (#7238)
The action needs a JDK 21, but the step to install it wasn't running for us because an incorrect check was added.
2026-07-17 14:29:43 +00:00
gradle-update-robot 7f27f4d697 Update Gradle Wrapper from 9.5.1 to 9.6.1
Signed-off-by: gradle-update-robot <gradle-update-robot@regolo.cc>
2026-07-17 13:59:33 +00:00
Valere e9fa6435fb Merge branch 'develop' into feature/valere/timeline_active_call_rendering 2026-07-17 15:33:33 +02:00
Valere 8109290650 code review 2026-07-17 15:33:28 +02:00
renovate[bot] 01823852fd Update dependency com.google.firebase:firebase-bom to v34.16.0 2026-07-17 13:28:32 +00:00
renovate[bot] c2d90ed870 Update dependency net.zetetic:sqlcipher-android to v4.17.0 (#7218)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-17 15:26:24 +02:00
Jorge Martin Espinosa 2705efad15 Add content scanner support to media gallery and viewer (Element Pro) (#7233)
* Add content scanner support to media gallery and viewer

* Improve UI and previews

* Remove default content validation state in media content views

* Disable click for invalid media items in the gallery events

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
2026-07-17 15:26:13 +02:00
Benoit Marty 19724aaa78 Merge pull request #7236 from element-hq/manuroe/element-pro-release-notes-category
Group Element Pro changes into a dedicated release notes section
2026-07-17 14:59:10 +02:00
manuroe 584e5097fd Group Element Pro changes into a dedicated release notes section
Pro-only PRs (labelled pr-element-pro) leaked into Element X release
notes, causing confusion. They now group into a single Element Pro
section instead of scattering through Features/Bugfixes. Granularity is
dropped on purpose for these changes.
2026-07-17 14:21:11 +02:00
renovate[bot] 87890ea30b Update kotlin to v2.3.10 (#7234)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-17 11:42:14 +02:00
renovate[bot] 9c99b020de Update dependency com.google.crypto.tink:tink-android to v1.23.0 (#7229)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-17 10:52:40 +02:00
Benoit Marty 2ceff75e65 Merge pull request #7227 from unclejay80/a11y/formatting-option-state-description
Expose formatting toggle state to TalkBack
2026-07-17 10:00:31 +02:00
Thirumani Vihaan f005693874 Fix #7181: Remove back arrow from device verified screen and use 'Done' text (#7222)
* Fix #7181: Remove back arrow from device verified screen and use 'Done' text

* Update screenshots

* Fix failing Maestro test by tapping 'Done' instead of 'Continue'

---------

Co-authored-by: ElementBot <android@element.io>
Co-authored-by: thirumani-vihaan <your-github-email@example.com>
2026-07-16 16:50:58 +02:00
ElementBot 2112e1bf04 Update screenshots 2026-07-16 12:19:40 +00:00
Benoit Marty 2ed7cfe79c Merge pull request #7225 from unclejay80/a11y/role-button-login-onboarding
Add a button role to clickable icons and text on the login/onboarding screens
2026-07-16 12:37:02 +02:00
Benoit Marty bf54701577 Merge pull request #7228 from element-hq/mxandreas/no-hs-check-qr
Fix logging via QR code when using MDM-configured homeserver
2026-07-16 11:14:48 +02:00
Valere 25c9f9706f Merge branch 'develop' into feature/valere/timeline_active_call_rendering 2026-07-16 10:51:34 +02:00
Benoit Marty 3eea53840f Merge pull request #7182 from element-hq/feature/bma/focussedButton
[A11y] Improve rendering on focused UI items
2026-07-16 10:47:19 +02:00
Benoit Marty 3854caa8f1 Fix test compilation issue. 2026-07-16 10:27:20 +02:00
renovate[bot] 4cb9d272f4 Update dependency io.sentry:sentry-android to v8.48.0 (#7217)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-16 10:05:43 +02:00
Jorge Martin Espinosa 0d03472c60 Content scanner (Element Pro): scan timeline events for unsafe content (#7159)
* Simplify how event content with captions are displayed

The caption component will now be added in `TimelineItemEventContentView` instead of separately in each `TimelineItem__View` component

* Make sure `CoilMediaFetcher` methods to fetch media throw errors

We need to catch them later to check if the content scanner marked some thumbnails or medias as invalid/unsafe in `AsyncImage` instances

* Add a `ContentScannerService` that allows us to manually scan a list of medias in an event and then update and store the results

The results take the form of `ContentValidationValue`, stored in a `ContentValidationState` instance tied to the event

* Create `EventContentValidationCache` to keep the `ContentValidationStates` for events in a room

* Expose `EventContentValidationCache` to Compose using `LocalEventContentValidationState`

Also add a couple of helper functions to avoid having to manually remember the states

* Use the new components to display invalid content in the timeline items

* Add `blurhash` to most media items so we can use it while validating the thumbnails/medias

* Fix custom layout issues with screenshot and UI tests

* Also check the validation state for replied-to events in the composer

* Fix and add tests

* Handle unrecoverable content validation errors too: display a 'not found' UI

* Improve the performance of `EqualWidthColumn` for the single item case

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
2026-07-16 07:59:51 +00:00
mxandreas 16bfca1e16 Remove unused code. 2026-07-16 08:34:07 +01:00
ganfra 56cf00560e Merge pull request #7184 from element-hq/feature/fga/lls_fix_timeout
Live location sharing timeout
2026-07-16 09:33:02 +02:00
Valere 5ab3ed1f09 fix test compilation 2026-07-16 09:30:51 +02:00
mxandreas 67cb11f6d0 Fix tests and remove unused code. 2026-07-16 08:25:47 +01:00
mxandreas ea61942ccc Do not enforce MDM-configured homeserver to login via QR-code. 2026-07-16 07:58:22 +01:00
Jürgen Wischer 9c6e7b4464 Add Button role to clickable icons and text on login/onboarding screens
Several clickable elements on the login and onboarding flows (the
account provider list items, the search clear icon, the login clear
icon, and the hidden dev-mode version tap) use plain
Modifier.clickable without a semantics role. Screen readers announce
these as generic elements instead of buttons, so users relying on
TalkBack cannot tell they are interactive.

Add role = Role.Button to each clickable modifier, and reuse the
existing clear-icon content description as onClickLabel where one is
available.
2026-07-15 22:07:33 +02:00
Jürgen Wischer a6aa29f375 Expose formatting toggle state to accessibility services
FormattingOption used clearAndSetSemantics with only a contentDescription,
so the Bold/Italic/Underline toggle buttons never exposed whether they
were currently active to TalkBack. Add a stateDescription reflecting the
selected/unselected state alongside the existing contentDescription.

New a11y_active / a11y_inactive strings added to temporary.xml for
Localazy import.
2026-07-15 22:05:04 +02:00
ganfra a6b5eb4dce Extract expired-share handling into a helper method 2026-07-15 21:22:23 +02:00
Valere 90d92e023e Merge branch 'develop' into feature/valere/timeline_active_call_rendering 2026-07-15 16:34:41 +02:00
Valere 938eabae70 fixup: poste merge error 2026-07-15 16:34:24 +02:00
Valere 3052316bf5 Merge branch 'develop' into feature/valere/timeline_active_call_rendering 2026-07-15 16:22:26 +02:00
ElementBot 058b494d82 Update screenshots 2026-07-15 14:17:15 +00:00
Benoit Marty 0fe423757f Merge branch 'develop' into feature/bma/focussedButton 2026-07-15 15:58:01 +02:00
Jorge Martín c10f4e521d Merge branch 'release/26.07.1' 2026-07-15 15:46:59 +02:00
Jorge Martín 0524b3c274 Adding fastlane file for version 26.07.1 2026-07-15 15:46:58 +02:00
Jorge Martín 4187f9814d Setting version for the release 26.07.1 2026-07-15 15:44:43 +02:00
Valere 3af396c505 Merge branch 'develop' into feature/valere/timeline_active_call_rendering 2026-07-15 14:12:54 +02:00
Valere e4d6661926 fixup: import 2026-07-15 12:18:22 +02:00
Matthew Hodgson 34293eb824 Refer to the knock push rule by its published MSC number (MSC4506)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uk8aPxHn3BHCe52L226jdG
2026-07-14 17:51:08 +01:00
Matthew Hodgson c86307dead Render push notifications for knocks
The homeserver's new MSCxxxx knock push rule notifies members who can
act on a knock; the push resolver previously filtered every
m.room.member state event out, so the push produced no notification.
Split RoomMemberContent out of the filtered-out catch-all and render
membership=knock as "Requested to join" from the knocking user
(other membership changes stay filtered out).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 17:50:30 +01:00
Valere f3746d3394 Merge branch 'develop' into feature/valere/timeline_active_call_rendering 2026-07-13 15:59:48 +02:00
Valere aaba63ca60 fix duplicate string 2026-07-13 15:20:17 +02:00
Valere e9b090a552 Merge branch 'develop' into feature/valere/timeline_active_call_rendering 2026-07-13 14:54:16 +02:00
Valere a1801c7225 fixup Beacon missing imports 2026-07-13 14:53:48 +02:00
Valere 33f1eb9cf8 fix detekt errors 2026-07-13 14:38:08 +02:00
Valere 8b78259b17 Temporary implem for sdk changes on beacon 2026-07-13 14:25:25 +02:00
Valere cf21bb1045 update sdk to 26.07.13 2026-07-13 14:25:10 +02:00
ElementBot e42f974c00 Update screenshots 2026-07-10 14:43:36 +00:00
Benoit Marty 2baeca5edf Merge branch 'develop' into feature/bma/focussedButton 2026-07-10 14:49:49 +02:00
ElementBot a7502a8fe1 Update screenshots 2026-07-10 12:23:53 +00:00
ganfra 9d76f0c920 Test location-tick stops live location share past expiry 2026-07-10 12:46:26 +02:00
ganfra ac0ad510c8 Stop live location share on location tick when past expiry 2026-07-10 12:44:51 +02:00
Benoit Marty a8cbc7911e Fix typo 2026-07-10 12:24:45 +02:00
Benoit Marty e41c806dd4 Add Figma link 2026-07-10 12:22:42 +02:00
Benoit Marty 8c935d8682 Improve focus rendering: now display a blue inset ring around the focused component.
Closes #6397
2026-07-10 12:14:55 +02:00
Benoit Marty a2f1e9eeec Use better ButtonDefaults method.
It should not change anything since we are providing the 4 parameters
2026-07-10 11:53:52 +02:00
Valere bdf4c56b96 Merge branch 'develop' into feature/valere/timeline_active_call_rendering 2026-07-08 17:59:59 +02:00
Valere 17bcf8c55c Reflect new design updates 2026-07-08 17:58:06 +02:00
bxdxnn b1a3c2acb8 Review + lint 2026-07-07 16:34:59 +00:00
bxdxnn 16c1046293 Merge branch 'develop' into misc/reactions-pinned-messages 2026-07-07 17:38:59 +03:00
Benoit Marty 26e4e895a9 Fix accessibility: announce checked/unchecked state for invite user rows (#6407)
Replace clickable(role = Role.Checkbox) with toggleable(value = checked, role =
Role.Checkbox) so TalkBack can announce the current state and state changes.
Set the inner Checkbox.onCheckedChange = null to make it a purely visual
indicator, avoiding duplicate accessibility focus on the same control.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 17:51:12 +02:00
bxdxnn 80db5f8514 Merge branch 'develop' into misc/reactions-pinned-messages 2026-06-26 13:16:16 +03:00
Valere ddd72ec526 feat(call): Active call timeline rendering 2026-06-18 12:17:41 +02:00
bxdxnn f067ba34e7 Update features/poll/api/src/main/kotlin/io/element/android/features/poll/api/pollcontent/PollContentState.kt
Co-authored-by: Benoit Marty <benoit.marty@gmail.com>
2026-06-11 17:04:37 +03:00
bxdxnn a3fb65e924 Address design 2026-06-09 11:00:18 +00:00
bxdxnn 995a0524ed Add reactions support in the pinned messages list 2026-04-28 10:11:47 +00:00
bxdxnn 2cb68ba811 Max selections support 2026-04-07 16:16:05 +00:00
5719 changed files with 76294 additions and 25929 deletions
@@ -0,0 +1,307 @@
---
name: releasing-healthchat
description: Cut a HealthChat Google Play release of element-x-android by tagging release/healthchat. Use when the user asks to release, publish, tag, deploy, ship, or cut a new HealthChat Android version, asks about the Play Store release process, asks about rotating the Android release keystore, or asks about the required GitHub Actions secrets for the release pipeline.
---
# Releasing HealthChat (element-x-android)
Releases are driven by git tags of the form `healthchat-v<version>` pushed to
`origin`. The tag triggers `.github/workflows/healthchat-release-playstore.yml`,
which runs on the self-hosted `novaloop-incus-large` runner, builds a signed
AAB, and uploads it to the Play Store **internal** track. No GitHub Release is
created.
**Promotion to production is always a manual step** in Play Console after an
internal build has been validated — see "Promoting to production" below.
## Preflight checklist
Verify each item before tagging. If any fail, fix and commit first.
- [ ] On branch `release/healthchat` with a clean working tree (`git status`).
- [ ] `HEALTHCHAT_CHANGES.md` has a section `## Changes in <new-version> (<date>)`
at the top of the file. The pipeline's awk extractor matches this exact
heading format; no match means a fallback placeholder ships as the Play
Store "What's new" text.
- [ ] `plugins/src/main/kotlin/Versions.kt` `versionYear`, `versionMonth`, and
`versionReleaseNumber` produce the tag's core version (format
`<year>.<month zero-padded to 2>.<releaseNumber>`, e.g. `26.07.1`).
**The workflow hard-fails** if they don't match — this is load-bearing,
not cosmetic.
- [ ] If you edited `HEALTHCHAT_CHANGES.md` or `Versions.kt`, both commits are
pushed to `release/healthchat` **before** you push the tag.
## Cutting the release (normal path)
```bash
# from release/healthchat, fully committed:
git tag healthchat-v<YEAR>.<MONTH>.<RELEASE>
git push origin healthchat-v<YEAR>.<MONTH>.<RELEASE>
```
Examples:
- `git tag healthchat-v26.07.1` (next release in July 2026)
- `git tag healthchat-v26.08.0` (first release in August 2026)
- `git tag healthchat-v26.07.2-rc1` (release candidate)
Watch the workflow with `gh run watch` or in the GitHub Actions UI. When it
finishes, the build appears in Play Console → Internal testing within a few
minutes (Play Store's processing queue).
## Manual / retry path (workflow_dispatch)
Use when retrying a failed run or rebuilding without moving tags.
1. GitHub → Actions → "HealthChat Release to Google Play Store" → **Run workflow**.
2. Fill in:
- **version** (required): e.g. `26.07.1`. Same regex validation as tag path.
Must still match `Versions.kt`.
- **track**: default `internal`; pick `alpha`/`beta`/`production` to promote
an already-uploaded build to a higher track without rebuilding (note:
Play Console is usually a better UX for promotion).
- **release_status**: default `completed`; use `draft` for dry-runs that
land in Play Console but stay unpublished.
- **release_notes_override** (optional): replaces the
`HEALTHCHAT_CHANGES.md` lookup. Useful for ad-hoc test builds.
3. Run. The AAB is built and uploaded exactly as the tag path would.
## Runner and build tuning
The job runs on `novaloop-incus-large` — an incus container with 4 cores and
24 GiB, not a GitHub-hosted runner. Two consequences the workflow handles, and
that anyone editing it needs to keep in mind:
- **The image ships no Android SDK.** The `Set up Android SDK` step installs
it. Platform packages are minor-versioned, so `COMPILE_SDK = 37` means
`platforms;android-37.0`. Build-tools follows AGP's own default
(`SdkConstants.CURRENT_BUILD_TOOLS_VERSION`), not `BUILD_TOOLS_VERSION` in
`Versions.kt` — nothing sets `buildToolsVersion`, so bump it when AGP is
upgraded.
- **`GRADLE_OPTS` is deliberately smaller than upstream's.** Upstream's
workflows use `-Xmx8g` with a 4 GB Kotlin daemon, sized for the 4-core/16GB
`ubuntu-latest` that *public* repos get; this repo is private, so its hosted
runners are 2-core/7GB and its container is 24 GiB. At upstream's numbers the
daemon plus the Kotlin daemon plus four parallel lint/R8 workers exceed the
limit and the daemon is SIGKILLed — the symptom is
`DaemonDisappearedException` with **no** `OutOfMemoryError` and no heap dump,
which is what an external kill looks like. `-Xmx6g` with a 2 GB Kotlin daemon
fits.
The `Report runner capacity` step prints `free`, `nproc`, `df` and the enforced
cgroup limit on every run, so a future failure can be read against the runner's
actual capacity rather than guessed at. Note `free` reports the container's
limit only when lxcfs virtualises `/proc/meminfo`; `/sys/fs/cgroup/memory.max`
is the number the kernel enforces.
## Versioning (calendar scheme — differs from the old Classic repo)
Element X uses calendar versioning, defined in
`plugins/src/main/kotlin/Versions.kt`:
```
versionName = <versionYear>.<versionMonth padded to 2>.<versionReleaseNumber> // e.g. 26.07.1
versionCode = ((2000 + versionYear) * 10_000 + versionMonth * 100 + versionReleaseNumber) * 10
```
The trailing `* 10` is the ABI multiplier applied per output APK
(`app/build.gradle.kts` androidComponents block); the AAB gets ABI code `0`.
Example: `26.07.1` → versionCode `202607010`.
To bump: edit `versionYear`/`versionMonth`/`versionReleaseNumber` in
`Versions.kt` (increment `versionReleaseNumber` within a month; reset it to 0
when the month changes). `versionCode` must be strictly monotonic in Play
Store across all uploads — the calendar scheme guarantees this as long as you
never decrease the triple. All HealthChat codes (≥ 202,607,000) supersede the
old Classic app's last code (40,154,100).
**Keep the same version as the upstream Element X release the branch is based
on unless HealthChat ships an intermediate build** — then bump
`versionReleaseNumber` past upstream's. When merging a newer upstream release
into `release/healthchat`, take upstream's `Versions.kt` if its version is
higher.
## Release notes
`HEALTHCHAT_CHANGES.md` is the single source. The workflow extracts the
section matching the version being released, then writes it to
`fastlane/metadata/android/en-US/changelogs/<versionCode>.txt` and
`fastlane/metadata/android/de-DE/changelogs/<versionCode>.txt`, which
`r0adkll/upload-google-play` picks up via its `whatsNewDirectory` input.
Format:
```markdown
## Changes in 26.07.1 (2026-07-20)
🙌 Improvements
- Bullet one.
- Bullet two.
```
Keep the entire section under **500 characters** — Play Store's "What's new"
hard cap per locale. The workflow emits a warning if you exceed it; Play Store
will truncate.
Never hand-edit the generated `fastlane/metadata/android/*/changelogs/<N>.txt`
files for this branch — they'll be overwritten on the next run. Only edit
`HEALTHCHAT_CHANGES.md`. (Older checked-in files from upstream are harmless.)
If no matching section exists, the pipeline emits a workflow warning and
falls back to `HealthChat <version> — automated build from <ref>.`. Prefer
fixing the changelog over relying on the fallback.
## After the release
- [ ] Workflow run is green in GitHub Actions.
- [ ] Build appears under Play Console → Internal testing within ~10 minutes.
- [ ] Internal testers get the update automatically if they're enrolled.
- [ ] AAB + `mapping.txt` are attached to the workflow run, retained 90 days.
**A green "Build Release AAB" step does not mean the release shipped.** The
upload step logs `Successfully uploaded 1 artifacts` before it commits the Play
edit; if the commit fails, the AAB is uploaded but no release exists on the
track. Always check the `Upload to Play Store` step, not just the job's build
steps.
## Troubleshooting: `changesNotSentForReview must not be set`
```
Successfully uploaded 1 artifacts
Committing the Edit
##[error]Changes are sent for review automatically.
The query parameter changesNotSentForReview must not be set.
```
Google flips this per app: once Play sends an app's changes for review
automatically, the Edits API rejects `changesNotSentForReview` instead of
ignoring it. The 26.07.0 release (2026-07-27) committed fine with the flag
set; 26.08.0 (2026-08-03) failed with it. The parameter is no longer passed —
if it ever needs to come back, gate it rather than setting it unconditionally.
Recovery: the failed edit is never committed, so the `versionCode` is not
consumed. Re-run via `workflow_dispatch` with the same version — no tag move
and no version bump needed.
## Required GitHub Actions secrets
The pipeline depends on these repository secrets. Missing any causes the
workflow to fail at the corresponding step. The values are identical to the
ones configured on the old `Novaloop-AG/element-android` repository (same
upload keystore, same Play listing) — except `GOOGLE_SERVICES_JSON_BASE64`,
which element-x-android does **not** use (Firebase config is compiled in via
`libraries/pushproviders/firebase/src/main/res/values/firebase.xml`).
| Secret | Source |
|---|---|
| `RELEASE_KEYSTORE_BASE64` | `base64 -w0 release.keystore` (the HealthChat upload keystore, alias `healthchat-upload`) |
| `RELEASE_KEYSTORE_PASSWORD` | Keystore password |
| `RELEASE_KEY_ALIAS` | Key alias inside the keystore |
| `RELEASE_KEY_PASSWORD` | Key password |
| `GOOGLE_PLAY_SERVICE_ACCOUNT_JSON` | Play Console → Users and permissions → invite a service account → download JSON. Needs "Release manager" permissions scoped to `ch.healthchat.element`. |
## Keystore rotation (rare, high-risk)
Unlike Apple Distribution certificates, **Android upload keystores do not
expire** and should not be rotated casually. The upload key is tied to the
app's identity in Play Console (Play App Signing is enabled; the repo key is
the upload key); rotating it is a one-way process that uses Play App
Signing's "Upload key reset" flow.
If the keystore is lost or believed to be compromised:
1. Play Console → Release → Setup → App integrity → Upload key → **Request
upload key reset**. Instructions follow, requiring you to upload a new
keystore and wait for Google to rotate.
2. Generate a fresh keystore on the operator's machine:
```bash
keytool -genkey -v -keystore healthchat-upload-new.jks \
-keyalg RSA -keysize 2048 -validity 10000 -alias healthchat
```
3. Once Google confirms the rotation, re-encode and update secrets:
`base64 -w0 healthchat-upload-new.jks | pbcopy`, then replace
`RELEASE_KEYSTORE_BASE64`, `RELEASE_KEYSTORE_PASSWORD`, `RELEASE_KEY_ALIAS`,
`RELEASE_KEY_PASSWORD` in repo settings.
4. Run the release workflow via `workflow_dispatch` with a throwaway version
bump and `release_status: draft` to verify end-to-end. Discard the draft.
**Do not attempt rotation without reading Play App Signing docs end-to-end**
and coordinating a maintenance window. A botched rotation means users can't
receive updates until Google completes the reset.
## Promoting internal → production
Internal builds and production builds are the same AAB. To release a
validated internal build:
1. Play Console → HealthChat → Testing → **Internal testing** → find the
release.
2. Confirm it passed internal testing.
3. **Promote release** → Production → **Release details** → fill in "What's
new" (Play Console copies it from the internal release — verify) →
**Review release** → **Start rollout to Production**.
4. Choose staged rollout % if desired.
**This step is deliberately not automated.** Production releases require
human judgment on rollout percentage, Play Store listing changes, and
compliance metadata review.
## Rollback
- **Bad build, release notes fine** → fix the cause, bump
`versionReleaseNumber`, commit, tag `healthchat-v<ver>`. The new build
supersedes the bad one on the internal track.
- **Bad release notes only** → edit `HEALTHCHAT_CHANGES.md` on
`release/healthchat`, then update the "What's new" text in the Play Console
release manually (the pipeline only writes it at upload time).
- **Completely wrong version number** → treat as burned. Bump
`versionReleaseNumber` further, tag a higher `healthchat-v<ver>`. Play
Store's `versionCode` rule means the next run naturally supersedes.
- **Production rollout gone wrong** → Play Console → Production → **Halt
rollout** (works during staged rollout), then ship a hotfix as above.
## Known branch divergence when syncing with upstream
Upstream is taken by **rebasing** the HealthChat commits onto the new release
tag, never by merging — the procedure, Git LFS handling and force-push live in
the `syncing-upstream-healthchat` skill. The HealthChat-only facts that must
survive every sync:
- **`.github/workflows/healthchat-release-playstore.yml`**,
**`HEALTHCHAT_CHANGES.md`**, and
**`.claude/skills/releasing-healthchat/SKILL.md`** are HealthChat-only —
any merge conflict here means something upstream accidentally overlaps;
keep `--ours`.
- **`plugins/src/main/kotlin/config/BuildTimeConfig.kt`** —
`APPLICATION_ID = "ch.healthchat.element"`, `APPLICATION_NAME = "HealthChat"`,
`METADATA_HOST_REVERSED`, and the healthchat.ch URLs must survive merges.
Upstream uses `io.element.android.x` / `Element X` / nulls.
- **`appconfig/.../ApplicationConfig.kt`** — `PRODUCTION_APPLICATION_NAME` /
`DESKTOP_APPLICATION_NAME = "HealthChat"` must survive. Upstream: `Element`.
- **`features/enterprise/impl-foss/.../DefaultEnterpriseService.kt`** — the
homeserver lock: `accountProviderAllowList() = listOf(Generic("matrix.healthchat.ch"))`,
`canConnectToAnyAccountProvider() = false`, `isAllowedToConnectToAccountProvider()`
= membership in that list (since upstream 26.10.0; before that
`defaultHomeserverList()`). Upstream returns an empty list / `true` / `true`.
`BuildTimeConfig.OAUTH_CLIENT_URL_PATH` must stay `null` (upstream:
`"apps/android"`), or MAS rejects the redirect scheme.
- **`app/build.gradle.kts`** — the `healthchat` signing config and the
`appicon/healthchat` dependency must survive; upstream signs release with
the debug keystore and depends on `appicon/element`.
- **`app/src/gplay/AndroidManifest.xml`** — HealthChat-only file that strips
`REQUEST_INSTALL_PACKAGES` and the location permissions
(`FOREGROUND_SERVICE_LOCATION`, `ACCESS_FINE_LOCATION`,
`ACCESS_COARSE_LOCATION`, plus `LiveLocationSharingService`) from the Play
build. Play Console rejects the release at edit-commit time while either is
declared but not justified on the listing. Upstream has no `app/src/gplay/`
manifest, so this should never conflict. If HealthChat ever ships maps
(a MapTiler key in `BuildTimeConfig`), the location removals must come out
first — the feature is gated on that key, not on these declarations.
- **`plugins/src/main/kotlin/Versions.kt`** — take upstream's version when
higher (see Versioning above).
- **`fastlane/metadata/android/`** — HealthChat listing texts (en-US, de-DE)
must survive; upstream ships Element X texts. The per-versionCode changelog
files generated by this pipeline carry HealthChat text; conflicts there
mean a versionCode collided — regenerate from `HEALTHCHAT_CHANGES.md` on
the next release run rather than hand-merging.
@@ -0,0 +1,201 @@
---
name: syncing-upstream-healthchat
description: Bring the HealthChat fork (release/healthchat) onto a newer element-hq/element-x-android release tag. Use when the user asks to rebase, sync, merge or update to upstream, take a new Element X version, or asks why the fork is behind upstream. Covers the patch-stack model, the rebase procedure, Git LFS, conflicts, verification, the force-push and the rewind.
---
# Syncing the HealthChat fork with upstream
`release/healthchat` = upstream release tag + HealthChat commits on top. Patch stack, same as
healthchat-web and element-x-ios. A sync replaces the stack's base and force-pushes the branch.
That is the one sanctioned history rewrite in this repo; everything else in `AGENTS.md` still
holds. We rebase, we never merge upstream into the branch.
First done v26.07.0 → v26.10.0 (2026-10-16 to 10-19). Numbers below come from that run.
## Before touching anything
- [ ] **Toolchain.** Upstream pins the JDK (`java-version` in `.github/workflows/*.yml`, 21),
`COMPILE_SDK` / `BUILD_TOOLS_VERSION` (`plugins/src/main/kotlin/Versions.kt`), AGP, Kotlin
and Metro (`gradle/libs.versions.toml`). Gradle and AGP install themselves; the Rust SDK
comes from Maven. What does not: the self-hosted incus runners ship no Android SDK — the
`Set up Android SDK` step installs it (see the releasing skill, "Runner and build tuning").
If `COMPILE_SDK` moved, check that step still resolves the platform. 26.10.0 moved nothing.
- [ ] **Dev Mac Gradle.** A warm daemon can hang `assembleDebug` forever (0 % CPU, no log).
`./gradlew --stop` first, then compile-only tasks with `--max-workers=2`, e.g.
`:tests:uitests:compileDebugUnitTestKotlin` compiles every module. Gate scripts on the
exit code — a `rebase --continue` after an unchecked compile committed a broken stop once.
- [ ] **Upstream.** There is no upstream remote by default. Tags are `v<version>`.
`git fetch https://github.com/element-hq/element-x-android.git tag v<new> --no-tags` is
enough for the rebase. LFS needs a named remote:
`git remote add upstream https://github.com/element-hq/element-x-android.git`. After
adding it, `gh` may resolve to upstream — always pass `-R Novaloop-AG/element-x-android`.
- [ ] **Decisions, product owner:** which upstream feature-flag defaults flipped
(`git diff v<old> v<new> -- libraries/featureflag/api/.../FeatureFlags.kt`; 26.10.0:
message search default on, kept on), where fork UI lands when upstream restructures a
screen (26.10.0: settings split, HealthChat entries went to the account section), and
whether fork commits may be squashed (only within one HealthChat release, never across).
## The stack
```bash
git log --oneline --no-merges v<old>..release/healthchat # the fork's commits
git merge-base --is-ancestor v<old> release/healthchat # must be true
```
Upstream files the fork edits — conflicts live here every time:
- `features/enterprise/impl-foss/.../DefaultEnterpriseService.kt` + test — the homeserver
lock (since 26.10.0: `accountProviderAllowList()` = `Generic("matrix.healthchat.ch")`,
`canConnectToAnyAccountProvider() = false`, membership check).
- `plugins/src/main/kotlin/config/BuildTimeConfig.kt` — app id, name, OIDC URLs,
`OAUTH_CLIENT_URL_PATH = null` (MAS: redirect scheme must reverse-match the apex host).
- `plugins/src/main/kotlin/Versions.kt` — version; dropped from the stack, set again at release.
- `features/home/impl` (Members tab: `HomeView`, `HomePresenter`, `HomeState`,
`HomeStatePreviewParam`, `HomeNavigationBarItem`, `HomeTopBar`, `HomeFlowNode`, tests) and
`features/home/api/HomeEntryPoint.kt`, `appnav/.../LoggedInFlowNode.kt` (`navigateToUserProfile`).
- `features/preferences/impl` root node/view + `PreferencesFlowNode` (profile editor replaces
the display-name editor, account status banner).
- `features/userprofile`, `features/roomdetails` (profile fields section).
- `libraries/deeplink/impl`, `libraries/matrix/{api,impl}` permalink (HealthChat hosts),
`features/login/impl/DefaultLoginIntentResolver.kt`.
- `.github/workflows/{tests,quality,maestro-local,recordScreenshots}.yml` (self-hosted
runners, heap, fork guards), `app/src/gplay/AndroidManifest.xml` (permission strip),
`HEALTHCHAT_CHANGES.md`.
## Rebuilding the stack
Measure first: dry-run `git rebase --onto v<new> v<old> HEAD` in a throwaway worktree with a
script that records the conflicting files per commit and resolves blindly, then abort. The
conflict map decides the cleanup. 26.10.0: 11 of 31 commits stopped, 29 hunks, 62 goldens.
**1. Clean the series on the old base** (scratch branch off `release/healthchat`). Interactive
rebase is not available to agents; rebuild with `cherry-pick` (`-n` + `commit -C <orig>` for
edited commits; `cherry-pick` has no `-q`). Drop golden-only commits and the version bump;
strip every PNG, and edits to files upstream deleted, out of feature commits; squash the CI
run-of-commits within one release. `git diff --stat release/healthchat` must show only the
intended removals; compile once.
**2. Plain rebase** — `git rebase --onto v<new> v<old> <scratch>`, resolve per stop, keep each
commit's subject and intent. Upstream API removals cannot be "resolved", they need new code
*inside that commit* (26.10.0: `EnterpriseService` redesign). After a large stop compile its
modules before `--continue`. Cross-commit fallout (renames, moved fakes, signature changes)
goes into ONE trailing `Adapt HealthChat to upstream v<new>` commit, not amended into owners —
every amend replays the rest. A mistake in an already-committed stop: `git commit --fixup=<sha>`,
then `GIT_SEQUENCE_EDITOR=true git rebase -i --autosquash <sha>^` (conflict-free when later
commits do not touch the file). Intermediates may not compile; the tip must.
**3. Review** — `git range-diff v<old>..release/healthchat v<new>..<scratch>`. `!` on a commit
that applied cleanly is context drift; check the `-`/`+` lines are not ours.
Recurring fallout to expect: Konsist naming (`*PreviewParam` for `PreviewParameterProvider`
subclasses, `*Event` for sealed interfaces), design-system signature changes (26.10.0:
`ListItem(headlineContent)` → `ListItem(content)`), test fakes moving modules, action pins in
our CI hunks regressing upstream's bumps, exhaustive `when` over `HomeNavigationBarItem`.
zsh: `for c in $VAR` does not split on newlines, a variable named `path` *is* `PATH`, and
`$TAG:file` applies a history modifier — use `while read`, never name a variable `path`,
write `${TAG}:file`.
## Git LFS — the part that bites
Goldens, compound screenshots, `docs/images-lfs`, and `libraries/mediaupload/impl` test assets
are LFS-tracked (`.gitattributes`, ~4500 files). A fresh clone holds pointers only.
- origin is a standalone repo; upstream's LFS objects are not there. GitHub rejects a push
whose *new commits* reference LFS objects it lacks (`GH008 … unknown Git LFS objects`). The
rebased branch carries every upstream commit since the old tag. Fetch the LFS tree of each
upstream commit that touched LFS paths, then push the objects before the branch:
```bash
git log --format=%H v<old>..v<new> -- '*.png' '*.jpg' '*.gif' '*.mp4' '*.m4a' '*.webp' \
| sort -u | while read c; do git lfs fetch upstream "$c"; done
git lfs push origin <scratch> # must report no "(missing)"; refetch that commit
git push origin <scratch>
```
26.10.0: 210 such commits, 9837 objects, 525 MB locally, 3809 uploaded (174 MB). Run the loop
detached (`nohup`) — an agent's tool call times out at 10 minutes — and keep a done-file so it
resumes. Objects still "(missing)" after the loop are usually goldens from the fork's *own*
commits that a pointer-only clone never held: `git lfs fetch origin <original commit sha>`
for each commit on the old branch that touched them. The dry run lists every object until
the branch itself is pushed; verify presence with the LFS batch API or trust a clean upload.
- `git lfs push <branch>` only covers objects introduced by commits absent from the remote. 26.10.0:
167 objects referenced by the v26.10.0 tree were outside that set and `Validate Git LFS` failed
with "Object does not exist on the server". Read the oids from that job's log and
`git lfs push --object-id origin <oid…>` them (they are local after the fetch loop). Treat a
green `Validate Git LFS` on the PR as the only proof the store is complete.
- The recorder commits as ElementBot, a non-member: its CI runs land in `action_required` and
need `gh api -X POST repos/<repo>/actions/runs/<id>/approve` (or the PR's "Approve and run").
Never push to the branch while a recording runs: the recorder's final `git push` is a plain
push and is rejected once the tip has moved.
- `git lfs checkout` before running `libraries/mediaupload/impl` tests locally; pointer files
fail them. The screenshot tests are never run locally (`AGENTS.md`).
- `validate-lfs.yml` fails a PR that commits a PNG outside LFS.
## Screenshots
Take upstream's goldens on every conflict and never commit locally rendered ones (macOS
renders differ). After the push, add the `Record-Screenshots` label to the PR or
`workflow_dispatch` `recordScreenshots.yml`; the Linux runner records and commits all goldens.
Review only the HealthChat ones. A PR whose base is `release/healthchat` is *conflicting* by
construction (old and new stack touch the same files) and GitHub runs no `pull_request`
workflow on a conflicting PR — open the PR against a branch at the upstream tag
(`upstream/v<new>`, pushed by the owner) so the PR shows the 19-odd stack commits and CI runs.
Retargeting a PR is an `edited` event: tests/quality/build only start on the next push.
The label path needs `persist-credentials: true` on the LFS checkout steps (private fork,
fixed in 26.10.0). If the job dies on the LayoutLib static-initialiser crash,
restore the "one JVM per screenshot test class" workaround (`forkEvery = 1L` in the root
`build.gradle.kts`, dropped in 26.10.0 because upstream's Paparazzi bump fixed it).
## Verification of the tip
Local, all green before handover (26.10.0: 1200 unit tests):
```bash
./gradlew :tests:uitests:compileDebugUnitTestKotlin :app:compileGplayDebugKotlin
./gradlew :features:healthchat:impl:testDebugUnitTest :features:home:impl:testDebugUnitTest \
:features:preferences:impl:testDebugUnitTest :features:enterprise:impl-foss:testDebugUnitTest \
:features:login:impl:testDebugUnitTest :libraries:matrix:impl:testDebugUnitTest :appnav:testDebugUnitTest
./gradlew :tests:konsist:test ktlintCheck detekt lint
```
CI on the branch (tests, quality, screenshots) must be green on the self-hosted runners.
Device matrix (owner, debug and release build): MAS OIDC login on a fresh install — chooser
skipped, client URI `https://healthchat.ch` without appended path — is the gate for everything
else; previous release → new keeps the session; last Classic build → new lands on login without
crashing; member directory, profile editor, invites, status banners; deeplinks on HealthChat
hosts and `to.healthchat.ch` share links; push in background and killed; release build under
R8 exercising the suspend Retrofit calls; whatever upstream switched on (26.10.0: German
whole-word search in an encrypted room).
## Force-push, rewind
```bash
git push origin release/healthchat:refs/heads/backup/release-healthchat-before-<new>
git push --force-with-lease=release/healthchat:<old tip> origin <scratch>:release/healthchat
git checkout release/healthchat && git reset --hard origin/release/healthchat # every clone
```
`release/healthchat` is the default branch and the repository ruleset `healthchat-base`
(id 20385846, rules `deletion` + `non_fast_forward`, no bypass actors) rejects the force-push with
`GH013 … Cannot force-push to this branch`. Classic branch protection is empty, so
`gh api repos/<repo>/branches/<branch>/protection` says "not protected" — check
`gh api repos/<repo>/rules/branches/release%2Fhealthchat` instead. Decision (2026-10-20): disable the
ruleset for the push and re-enable it right after, no permanent bypass:
```bash
gh api -X PUT repos/Novaloop-AG/element-x-android/rulesets/20385846 -f enforcement=disabled
git push --force-with-lease=release/healthchat:<old tip> origin <stack>:release/healthchat
gh api -X PUT repos/Novaloop-AG/element-x-android/rulesets/20385846 -f enforcement=active
gh api repos/Novaloop-AG/element-x-android/rulesets/20385846 -q .enforcement # must print active
```
Old `healthchat-v*` tags keep their commits alive and off the branch line. Rewind =
`git push --force origin healthchat-v<last>^{commit}:release/healthchat`. A rewind does not
remove a tag or Play build made on the new history — tag only after the device tests. Then set
the version, update `HEALTHCHAT_CHANGES.md`, the releasing skill ("Known branch divergence")
and this skill with what changed.
## What not to do
- Merge upstream into `release/healthchat`, or squash the fork into a side branch and merge
upstream there: both duplicate every fork commit and conflict on every touched file.
- Pull on a rewritten branch (merges the old history back in). Fetch and reset.
- Commit goldens from a Mac, or resolve a golden conflict by hand — take upstream's and record.
- Amend compile fixes into owner commits mid-rebase; collect them in the adaptation commit.
- Touch `release/healthchat` before the owner has reviewed the range-diff.
+4
View File
@@ -28,6 +28,10 @@ changelog:
labels:
- PR-Dependencies
- title: 💼 Element Pro
labels:
- PR-Element-Pro
- title: Others
labels:
- PR-misc
+22 -1
View File
@@ -7,10 +7,17 @@
"PR-Dependencies",
],
"ignoreDeps": [
"string:app_name",
"gradle",
],
"minimumReleaseAge": "7 days",
"hostRules": [
{
"matchHost": "https://repo1.maven.org/maven2/",
"concurrentRequestLimit": 5,
"maxRequestsPerSecond": 5,
"timeout": 10000,
}
],
"packageRules": [
{
"matchPackageNames": [
@@ -18,6 +25,20 @@
"/^io.element.android/",
],
"minimumReleaseAge": null,
// Max default value is 6, with this we give absolute priority to our own dependencies
"prPriority": 7,
"registryUrls": [ "https://repo1.maven.org/maven2/" ],
},
{
// Fetch Android dependencies from Google's Maven repo instead of Maven Central.
"matchPackageNames": [
"/^androidx\\./",
"/^android\\./",
"/^com\\.android\\.tools/",
"/^com\\.google\\.firebase/",
"/^com\\.google\\.android/",
],
"registryUrls": [ "https://dl.google.com/android/maven2/" ],
},
{
"groupName": "kotlin",
+24 -7
View File
@@ -7,18 +7,32 @@ on:
push:
branches: [ develop ]
permissions: {}
permissions:
# Private fork: actions/checkout needs contents:read to clone. Upstream is public,
# where an empty token can still read the repo, so an empty permissions block works there and
# fails here with `fatal: repository not found`.
contents: read
# Enrich gradle.properties for CI/CD
env:
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx4g
# Reduced from upstream's -Xmx8g / 4 GB Kotlin daemon for the self-hosted runners.
# Each incus container reports 48 GiB and 8 cores, so a single build has ample room —
# but the containers are oversubscribed against their host, and two concurrent heavy
# Gradle jobs get their daemons SIGKILLed ("the daemon has disappeared", no
# OutOfMemoryError, no heap dump). Running the same job alone succeeds. Lower heaps
# make two builds likelier to coexist; the real fix is sizing the containers to the
# host. See the "Report runner capacity" step.
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx6g -XX:MaxMetaspaceSize=768m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx2g
CI_GRADLE_ARG_PROPERTIES: --stacktrace -Dsonar.gradle.skipCompile=true --no-configuration-cache
jobs:
build:
name: Build APKs
runs-on: ubuntu-latest
runs-on: [self-hosted, Linux, X64]
permissions:
# A job-level block replaces the workflow-level one, so contents:read has to be
# repeated here or checkout fails on this private fork.
contents: read
# For NejcZdovc/comment-pr
pull-requests: write
strategy:
@@ -31,6 +45,9 @@ jobs:
cancel-in-progress: true
steps:
- name: Free Disk Space (Ubuntu)
# Only meaningful on GitHub's throwaway VMs. On a persistent self-hosted runner this
# would delete dotnet/haskell/docker images from the host on every run.
if: runner.environment == 'github-hosted'
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
@@ -44,19 +61,19 @@ jobs:
docker-images: true
swap-storage: false
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# Ensure we are building the branch and not the branch after being merged on develop
# https://github.com/actions/checkout/issues/881
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
persist-credentials: false
- name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Assemble debug APKs
@@ -74,7 +91,7 @@ jobs:
run: ./gradlew :app:assembleGplayDebug app:assembleFDroidDebug -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES
- name: Upload debug APKs
if: ${{ matrix.variant == 'debug' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: elementx-debug
path: |
+21 -7
View File
@@ -7,17 +7,28 @@ on:
push:
branches: [ develop ]
permissions: {}
permissions:
# Private fork: actions/checkout needs contents:read to clone. Upstream is public,
# where an empty token can still read the repo, so an empty permissions block works there and
# fails here with `fatal: repository not found`.
contents: read
# Enrich gradle.properties for CI/CD
env:
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx4g
# Reduced from upstream's -Xmx8g / 4 GB Kotlin daemon for the self-hosted runners.
# Each incus container reports 48 GiB and 8 cores, so a single build has ample room —
# but the containers are oversubscribed against their host, and two concurrent heavy
# Gradle jobs get their daemons SIGKILLed ("the daemon has disappeared", no
# OutOfMemoryError, no heap dump). Running the same job alone succeeds. Lower heaps
# make two builds likelier to coexist; the real fix is sizing the containers to the
# host. See the "Report runner capacity" step.
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx6g -XX:MaxMetaspaceSize=768m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx2g
CI_GRADLE_ARG_PROPERTIES: --stacktrace -Dsonar.gradle.skipCompile=true --no-configuration-cache
jobs:
build:
name: Build Enterprise APKs
runs-on: ubuntu-latest
runs-on: [self-hosted, Linux, X64]
# Skip in forks
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
strategy:
@@ -30,6 +41,9 @@ jobs:
cancel-in-progress: true
steps:
- name: Free Disk Space (Ubuntu)
# Only meaningful on GitHub's throwaway VMs. On a persistent self-hosted runner this
# would delete dotnet/haskell/docker images from the host on every run.
if: runner.environment == 'github-hosted'
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
@@ -43,7 +57,7 @@ jobs:
docker-images: true
swap-storage: false
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# Ensure we are building the branch and not the branch after being merged on develop
# https://github.com/actions/checkout/issues/881
@@ -56,12 +70,12 @@ jobs:
- name: Clone submodules
run: git submodule update --init --recursive
- name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Assemble debug Gplay Enterprise APK
@@ -79,7 +93,7 @@ jobs:
run: ./gradlew :app:assembleGplayDebug -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES
- name: Upload debug Enterprise APKs
if: ${{ matrix.variant == 'debug' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: elementx-enterprise-debug
path: |
-33
View File
@@ -1,33 +0,0 @@
name: Danger CI
on: [pull_request, merge_group]
permissions: {}
jobs:
build:
runs-on: ubuntu-latest
name: Danger main check
# Skip in forks, it doesn't work even with the fallback token
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Add SSH private keys for submodule repositories
uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
with:
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
- name: Clone submodules
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
run: git submodule update --init --recursive
- run: |
npm install --save-dev @babel/plugin-transform-flow-strip-types
- name: Danger
uses: danger/danger-js@e9b58820039524318198d8059ec98b0c02b7875c # 13.0.10
with:
args: "--dangerfile ./tools/danger/dangerfile.js"
env:
DANGER_GITHUB_API_TOKEN: ${{ secrets.DANGER_GITHUB_API_TOKEN }}
# Fallback for forks
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+3 -3
View File
@@ -19,16 +19,16 @@ jobs:
- name: ⏬ Checkout with LFS
uses: nschloe/action-cached-lfs-checkout@385a8ecc719e50b8c71af6ab01a624b486b7c3bc # v1.2.5
- name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Set up Python 3.12
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: 3.14
- name: Run World screenshots generation script
+2 -3
View File
@@ -13,12 +13,11 @@ jobs:
# Skip in forks
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
- uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
name: Use JDK 21
if: (github.event_name == 'pull_request' && github.event.pull_request.fork == null) || github.event_name == 'workflow_dispatch'
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
@@ -0,0 +1,254 @@
name: HealthChat Release to Google Play Store
on:
push:
tags:
- 'healthchat-v*'
workflow_dispatch:
inputs:
version:
description: 'Marketing version (e.g. 26.07.0). Required when running manually.'
required: true
type: string
track:
description: 'Play Store track'
required: true
default: 'internal'
type: choice
options:
- internal
- alpha
- beta
- production
release_status:
description: 'Release status'
required: true
default: 'completed'
type: choice
options:
- completed
- inProgress
- halted
- draft
release_notes_override:
description: 'Optional release notes. If empty, falls back to HEALTHCHAT_CHANGES.md section for the given version.'
required: false
type: string
permissions:
# Only need read access to clone. Upload uses the Play Console service
# account JSON from secrets, not GITHUB_TOKEN.
contents: read
concurrency:
group: healthchat-release-${{ github.event.inputs.version || github.ref_name }}
cancel-in-progress: true
env:
# Upstream's workflows hardcode -Xmx8g / Kotlin daemon -Xmx4g, sized for the
# 4-core/16GB ubuntu-latest that public repos get. On the previous incus
# container the daemon kept getting SIGKILLed mid-build
# (DaemonDisappearedException, no OutOfMemoryError, no heap dump), so these
# values stay well under upstream's ceiling and leave room for the Kotlin
# daemon and the parallel lint workers. Re-check them against the "Report
# runner capacity" output now that jobs land on the persistent org runners,
# whose specs may differ.
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx6g -XX:MaxMetaspaceSize=768m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx2g
CI_GRADLE_ARG_PROPERTIES: --stacktrace --no-daemon -Dsonar.gradle.skipCompile=true --no-configuration-cache
jobs:
release:
name: Build and upload AAB to Play Store
# Persistent org runners (github-runner-01, gha-runner-1..3). The previous
# novaloop-incus-large label pointed at the GARM ephemeral pool, whose freshly
# spawned runners were reaped mid-job — jobs died ~10 minutes in with
# "The operation was canceled" and no other diagnostic.
runs-on: [self-hosted, Linux, X64]
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
fetch-depth: 0
persist-credentials: false
# Tag pushes must originate from release/healthchat. Dispatch runs must be
# invoked from that branch too. Reject anything else up-front so we don't
# ship off main/develop.
- name: Verify ref is on release/healthchat
run: |
set -euo pipefail
# On tag pushes, GITHUB_REF_NAME is the tag, not the branch. Resolve
# the commit's branch membership instead.
if ! git branch -r --contains "$GITHUB_SHA" | grep -qE 'origin/release/healthchat$'; then
echo "::error::Commit $GITHUB_SHA is not on origin/release/healthchat. Refusing to release."
exit 1
fi
- name: Resolve marketing version
id: version
env:
# Passed through the environment, never expanded into the script (zizmor: template-injection).
INPUT_VERSION: ${{ github.event.inputs.version }}
run: |
set -euo pipefail
if [[ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]]; then
VERSION="$INPUT_VERSION"
else
VERSION="${GITHUB_REF_NAME#healthchat-v}"
fi
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]; then
echo "::error::Invalid marketing version '$VERSION' — expected e.g. 26.07.0"
exit 1
fi
# Drop any pre-release/build suffix (e.g. -rc1, -hotfix1) for the Versions.kt comparison.
BASE_VERSION=$(echo "$VERSION" | sed -E 's/([0-9]+\.[0-9]+\.[0-9]+)([.-].*)?$/\1/')
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "base_version=$BASE_VERSION" >> "$GITHUB_OUTPUT"
echo "Resolved marketing version: $VERSION (base: $BASE_VERSION)"
- name: Verify Versions.kt matches tag
id: gradle_version
env:
BASE_VERSION: ${{ steps.version.outputs.base_version }}
run: |
set -euo pipefail
VERSIONS_FILE=plugins/src/main/kotlin/Versions.kt
YEAR=$(grep -E 'private const val versionYear' "$VERSIONS_FILE" | sed -E 's/.*=\s*([0-9]+).*/\1/')
MONTH=$(grep -E 'private const val versionMonth' "$VERSIONS_FILE" | sed -E 's/.*=\s*([0-9]+).*/\1/')
REL=$(grep -E 'private const val versionReleaseNumber' "$VERSIONS_FILE" | sed -E 's/.*=\s*([0-9]+).*/\1/')
if [[ -z "$YEAR" || -z "$MONTH" || -z "$REL" ]]; then
echo "::error::Could not parse versionYear/versionMonth/versionReleaseNumber from $VERSIONS_FILE"
exit 1
fi
# VERSION_NAME format (see Versions.kt): year on 2 digits, month zero-padded to 2, release number unpadded.
GRADLE_VERSION="${YEAR}.$(printf '%02d' "$MONTH").${REL}"
echo "Parsed $VERSIONS_FILE: $GRADLE_VERSION"
if [[ "$GRADLE_VERSION" != "$BASE_VERSION" ]]; then
echo "::error::Tag version $BASE_VERSION does not match Versions.kt ($GRADLE_VERSION). Bump versionYear/versionMonth/versionReleaseNumber and commit before tagging."
exit 1
fi
# Formula (see Versions.kt VERSION_CODE): (2000+year)*10_000 + month*100 + releaseNumber,
# then *10 for the AAB output (ABI code 0 — see app/build.gradle.kts androidComponents block).
BASE_CODE=$(( (2000 + YEAR) * 10000 + MONTH * 100 + REL ))
VERSION_CODE=$(( BASE_CODE * 10 ))
echo "version_code=$VERSION_CODE" >> "$GITHUB_OUTPUT"
echo "Computed versionCode: $VERSION_CODE"
- name: Extract release notes
id: notes
env:
VERSION: ${{ steps.version.outputs.base_version }}
OVERRIDE: ${{ github.event.inputs.release_notes_override }}
run: |
set -euo pipefail
if [[ -n "${OVERRIDE:-}" ]]; then
NOTES="$OVERRIDE"
echo "Using release notes from workflow input"
else
NOTES=$(awk -v ver="$VERSION" '
/^## Changes in / {
if (found) { exit }
if ($0 ~ "## Changes in " ver "( |$|\\()") { found = 1; next }
}
found { print }
' HEALTHCHAT_CHANGES.md | sed '/^[[:space:]]*$/{N;/^\n$/D;}')
if [[ -z "$NOTES" ]]; then
NOTES="HealthChat ${VERSION} — automated build from ${GITHUB_REF_NAME}."
echo "::warning::No HEALTHCHAT_CHANGES.md section found for $VERSION — using fallback"
fi
fi
# Play Store "What's new" has a 500-character limit per locale.
CHAR_COUNT=$(printf '%s' "$NOTES" | wc -c)
if (( CHAR_COUNT > 500 )); then
echo "::warning::Release notes are $CHAR_COUNT chars; Play Store truncates at 500"
fi
{
echo 'notes<<NOTES_EOF'
echo "$NOTES"
echo 'NOTES_EOF'
} >> "$GITHUB_OUTPUT"
- name: Generate fastlane changelog files
env:
VERSION_CODE: ${{ steps.gradle_version.outputs.version_code }}
NOTES: ${{ steps.notes.outputs.notes }}
run: |
set -euo pipefail
for locale in en-US de-DE; do
dir="fastlane/metadata/android/${locale}/changelogs"
mkdir -p "$dir"
printf '%s\n' "$NOTES" > "${dir}/${VERSION_CODE}.txt"
echo "Wrote ${dir}/${VERSION_CODE}.txt"
done
- name: Use JDK 21
uses: actions/setup-java@cf277c60eb25467037889841efdb72551f06f6c3 # v4.9.1
with:
distribution: 'temurin'
java-version: '21'
# The Android SDK now comes from the self-hosted runner image, so there is no
# setup-android step here. The image must provide it AND export ANDROID_HOME to
# the job environment — Gradle fails with "SDK location not found" otherwise.
# The image also has to carry the platform matching COMPILE_SDK in
# plugins/src/main/kotlin/Versions.kt (platform packages are minor-versioned, so
# compileSdk 37 means android-37.0) and the build-tools version AGP defaults to
# (SdkConstants.CURRENT_BUILD_TOOLS_VERSION), since nothing sets buildToolsVersion
# here. Both need refreshing in the image when AGP or COMPILE_SDK moves.
- name: Configure gradle
uses: gradle/actions/setup-gradle@748248ddd2a24f49513d8f472f81c3a07d4d50e1 # v4.4.4
with:
# Release builds must not consume a cache another workflow could have poisoned.
cache-disabled: true
- name: Decode keystore
run: |
echo "${{ secrets.RELEASE_KEYSTORE_BASE64 }}" | base64 -d > app/signature/release.keystore
# Recorded on every run so build failures can be read against the
# runner's actual capacity instead of guessed at.
- name: Report runner capacity
run: |
free -h
nproc
df -h .
# free reads /proc/meminfo, which in a container may report the host's
# total rather than the enforced limit. This is what the kernel enforces.
cat /sys/fs/cgroup/memory.max 2>/dev/null \
|| cat /sys/fs/cgroup/memory/memory.limit_in_bytes 2>/dev/null \
|| echo "no cgroup memory limit visible"
- name: Build Release AAB
run: |
./gradlew bundleGplayRelease $CI_GRADLE_ARG_PROPERTIES
env:
RELEASE_STORE_FILE: signature/release.keystore
RELEASE_STORE_PASSWORD: ${{ secrets.RELEASE_KEYSTORE_PASSWORD }}
RELEASE_KEY_ALIAS: ${{ secrets.RELEASE_KEY_ALIAS }}
RELEASE_KEY_PASSWORD: ${{ secrets.RELEASE_KEY_PASSWORD }}
- name: Upload workflow artifacts
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: healthchat-${{ steps.version.outputs.version }}
path: |
app/build/outputs/bundle/gplayRelease/*.aab
app/build/outputs/mapping/gplayRelease/mapping.txt
retention-days: 90
if-no-files-found: warn
- name: Upload to Play Store
uses: r0adkll/upload-google-play@e738b9dd8f2476ea806d921b64aacd24f34515a5 # v1.1.5
with:
serviceAccountJsonPlainText: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON }}
packageName: ch.healthchat.element
releaseFiles: app/build/outputs/bundle/gplayRelease/*.aab
tracks: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.track || 'internal' }}
status: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_status || 'completed' }}
whatsNewDirectory: fastlane/metadata/android
mappingFile: app/build/outputs/mapping/gplayRelease/mapping.txt
# Do NOT set changesNotSentForReview. Play now sends this app's changes for review
# automatically, and the Edits API rejects the parameter outright when it does:
# "Changes are sent for review automatically. The query parameter
# changesNotSentForReview must not be set." The AAB uploads fine and then the commit
# fails, so the release silently never lands on the track. See SKILL.md.
+44 -11
View File
@@ -5,11 +5,22 @@ on:
workflow_dispatch:
pull_request:
permissions: {}
permissions:
# Private fork: actions/checkout needs contents:read to clone. Upstream is public,
# where an empty token can still read the repo, so an empty permissions block works there and
# fails here with `fatal: repository not found`.
contents: read
# Enrich gradle.properties for CI/CD
env:
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx4g
# Reduced from upstream's -Xmx8g / 4 GB Kotlin daemon for the self-hosted runners.
# Each incus container reports 48 GiB and 8 cores, so a single build has ample room —
# but the containers are oversubscribed against their host, and two concurrent heavy
# Gradle jobs get their daemons SIGKILLed ("the daemon has disappeared", no
# OutOfMemoryError, no heap dump). Running the same job alone succeeds. Lower heaps
# make two builds likelier to coexist; the real fix is sizing the containers to the
# host. See the "Report runner capacity" step.
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx6g -XX:MaxMetaspaceSize=768m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx2g
CI_GRADLE_ARG_PROPERTIES: --stacktrace --no-daemon -Dsonar.gradle.skipCompile=true --no-configuration-cache
ARCH: x86_64
DEVICE: pixel_7_pro
@@ -19,12 +30,18 @@ env:
jobs:
build-apk:
name: Build APK
runs-on: ubuntu-latest
runs-on: [self-hosted, Linux, X64]
# Secrets are not available to pull_request runs from forks, so the Maestro job below
# cannot run for them; skip the build too, as its only consumer is that job.
if: github.event_name == 'workflow_dispatch' || github.event.pull_request.head.repo.full_name == github.repository
concurrency:
group: ${{ format('maestro-build-{0}', github.ref) }}
cancel-in-progress: true
steps:
- name: Free Disk Space (Ubuntu)
# Only meaningful on GitHub's throwaway VMs. On a persistent self-hosted runner this
# would delete dotnet/haskell/docker images from the host on every run.
if: runner.environment == 'github-hosted'
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
@@ -38,19 +55,19 @@ jobs:
docker-images: true
swap-storage: false
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# Ensure we are building the branch and not the branch after being merged on develop
# https://github.com/actions/checkout/issues/881
ref: ${{ github.ref }}
persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
- uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
name: Use JDK 21
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Assemble debug APK
@@ -60,7 +77,7 @@ jobs:
ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }}
ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }}
- name: Upload APK as artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: elementx-apk-maestro
path: |
@@ -73,13 +90,16 @@ jobs:
name: Maestro test suite
runs-on: ubuntu-latest
needs: [ build-apk ]
if: github.event_name == 'workflow_dispatch' || github.event.pull_request.head.repo.full_name == github.repository
# Allow only one to run at a time, since they use the same environment.
# Otherwise, tests running in parallel can break each other.
# queue: max lets several runs wait their turn instead of the default, where a
# newly queued run cancels the one already pending (that run then never gets a verdict).
concurrency:
group: maestro-test
queue: max
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
if: (github.event_name == 'pull_request' && github.event.pull_request.fork == null) || github.event_name == 'workflow_dispatch'
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# Ensure we are building the branch and not the branch after being merged on develop
# https://github.com/actions/checkout/issues/881
@@ -98,7 +118,7 @@ jobs:
run: curl -fsSL "https://get.maestro.mobile.dev" | bash
- name: Run Maestro tests in emulator
id: maestro_test
uses: reactivecircus/android-emulator-runner@e89f39f1abbbd05b1113a29cf4db69e7540cae5a # v2.37.0
uses: reactivecircus/android-emulator-runner@a421e43855164a8197daf9d8d40fe71c6996bb0d # v2.38.0
continue-on-error: true
env:
MAESTRO_USERNAME: maestroelement
@@ -118,8 +138,21 @@ jobs:
disk-size: 3G
script: |
.github/workflows/scripts/maestro/maestro-local-with-screen-recording.sh app-gplay-x86_64-debug.apk
- name: Set up Python
if: always()
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: 3.14
- name: End all the remaining sessions of the Maestro account
if: always()
continue-on-error: true
env:
MAS_PASSWORD: ${{ secrets.MATRIX_MAESTRO_ACCOUNT_PASSWORD }}
run: |
pip install requests
python3 ./tools/mas/delete_all_sessions.py --homeserver matrix.org --username maestroelement
- name: Upload test results
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: test-results
path: |
+2 -2
View File
@@ -32,11 +32,11 @@ jobs:
docker-images: true
swap-storage: false
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
+9 -9
View File
@@ -37,28 +37,28 @@ jobs:
uses: nschloe/action-cached-lfs-checkout@385a8ecc719e50b8c71af6ab01a624b486b7c3bc # v1.2.5
- name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
with:
cache-read-only: false
- name: ⚙️ Run unit tests, debug and release
run: ./gradlew test $CI_GRADLE_ARG_PROPERTIES
run: ./gradlew test $CI_GRADLE_ARG_PROPERTIES --continue
- name: 📸 Run screenshot tests
run: ./gradlew verifyPaparazziDebug $CI_GRADLE_ARG_PROPERTIES
run: ./gradlew verifyPaparazziDebug $CI_GRADLE_ARG_PROPERTIES --continue
- name: 📈 Generate kover report and verify coverage
run: ./gradlew :app:koverXmlReportGplayDebug :app:koverHtmlReportGplayDebug :app:koverVerifyAll $CI_GRADLE_ARG_PROPERTIES
- name: ✅ Upload kover report
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: kover-results
path: |
@@ -76,23 +76,23 @@ jobs:
name: Dependency analysis
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Dependency analysis
run: ./gradlew dependencyCheckAnalyze $CI_GRADLE_ARG_PROPERTIES
- name: Upload dependency analysis
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: dependency-analysis
path: build/reports/dependency-check-report.html
+128
View File
@@ -0,0 +1,128 @@
name: PR Checks
on:
# Privilege escalation necessary to comment on pull requests from forks.
# 🚨 We must not check out or execute any code here, and be careful around use of user-controlled inputs.
pull_request_target: # zizmor: ignore[dangerous-triggers]
types: [ opened, reopened, edited, synchronize, labeled, unlabeled ]
permissions: {}
jobs:
pr-checks:
# Upstream-only: this fork does not use the PR- label convention.
if: github.repository == 'element-hq/element-x-android'
name: PR Checks
runs-on: ubuntu-latest
timeout-minutes: 5
concurrency:
# Only allow a single run of this workflow on each branch, automatically cancelling older runs.
group: ${{ format('pr-checks-{0}', github.ref) }}
cancel-in-progress: true
permissions:
# Required to comment on the pull request.
pull-requests: write
steps:
- name: Check the pull request
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
// Classes that must not be modified anymore. Add the file name suffix here to freeze a class.
const FROZEN_CLASSES = [
];
// Only this user is allowed to edit the translation files, since they come from Localazy.
const TRANSLATION_ALLOW_LIST = ["ElementBot"];
const pr = context.payload.pull_request;
const body = pr.body ?? "";
const files = await github.paginate(github.rest.pulls.listFiles, {
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pr.number
});
// Deleted files are not interesting for any of the checks below.
const paths = files.filter(file => file.status !== "removed").map(file => file.filename);
const warnings = [];
const failures = [];
if (!body) {
warnings.push("Please provide a description for this PR.");
}
if (paths.length > 50) {
warnings.push("This pull request seems relatively large. Please consider splitting it into multiple smaller ones.");
}
if (pr.title.endsWith("…")) {
failures.push("Please provide a complete title that can be used as a changelog entry.");
}
if (pr.labels.filter(label => label.name.startsWith("PR-")).length !== 1) {
failures.push("Please add a single `PR-` label to categorise the changelog entry.");
}
for (const frozen of FROZEN_CLASSES) {
if (paths.some(path => path.endsWith(frozen))) {
failures.push(`Frozen class \`${frozen}\` has been modified. Please do not modify frozen class.`);
}
}
// Exclude the recorded screenshots, which are legitimately pngs.
if (paths.some(path => path.toLowerCase().endsWith(".png") && !path.includes("snapshots/images/"))) {
warnings.push("You seem to have made changes to some images. Please consider using a vector drawable.");
}
if (!TRANSLATION_ALLOW_LIST.includes(pr.user.login) && paths.some(path => path.endsWith("translations.xml"))) {
failures.push("Some translation files have been edited. Only user `ElementBot` (i.e. translations coming from Localazy) is allowed to do that. " +
"Please read more about translations management [in the doc](https://github.com/element-hq/element-x-android/blob/develop/CONTRIBUTING.md#strings).");
}
warnings.forEach(warning => core.warning(warning));
failures.forEach(failure => core.error(failure));
// Keep a single comment up to date rather than adding a new one on every run.
const marker = "<!-- pr-checks -->";
const comments = await github.paginate(github.rest.issues.listComments, {
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number
});
const existing = comments.find(comment => comment.body.includes(marker));
if (warnings.length || failures.length) {
const lines = [...failures.map(failure => `- ❌ ${failure}`),
...warnings.map(warning => `- ⚠️ ${warning}`)];
const commentBody = `${marker}\n### PR Checks\n\n${lines.join("\n")}`;
if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body: commentBody
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
body: commentBody
});
}
} else if (existing) {
await github.rest.issues.deleteComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id
});
}
if (failures.length) {
core.setFailed(`${failures.length} check(s) failed, see the comment on the pull request.`);
}
+32 -27
View File
@@ -23,33 +23,38 @@ jobs:
script: |
core.setFailed("PR has been labeled with X-Blocked; it cannot be merged.");
community-prs:
name: Label Community PRs
runs-on: ubuntu-latest
if: github.event.action == 'opened'
permissions:
pull-requests: write
steps:
- name: Check membership
if: github.event.pull_request.user.login != 'renovate[bot]'
uses: tspascoal/get-user-teams-membership@b2546c5affc730fd8e3d8483ae9ad3621938c2f9 # v4.0.2
id: teams
with:
username: ${{ github.event.pull_request.user.login }}
organization: element-hq
team: Vector Core
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN_READ_ORG }}
- name: Add label
if: steps.teams.outputs.isTeamMember == 'false'
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: ['Z-Community-PR']
});
# Disabled on the HealthChat fork. This job asks whether the PR author belongs to the
# element-hq "Vector Core" team, using secrets.ELEMENT_BOT_TOKEN_READ_ORG — a secret
# that only exists upstream. Here it fails on every PR that is opened, and there is no
# community to label anyway. Kept commented rather than deleted so upstream changes to
# it still conflict visibly on merge.
# community-prs:
# name: Label Community PRs
# runs-on: ubuntu-latest
# if: github.event.action == 'opened'
# permissions:
# pull-requests: write
# steps:
# - name: Check membership
# if: github.event.pull_request.user.login != 'renovate[bot]'
# uses: tspascoal/get-user-teams-membership@b2546c5affc730fd8e3d8483ae9ad3621938c2f9 # v4.0.2
# id: teams
# with:
# username: ${{ github.event.pull_request.user.login }}
# organization: element-hq
# team: Vector Core
# GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN_READ_ORG }}
# - name: Add label
# if: steps.teams.outputs.isTeamMember == 'false'
# uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
# with:
# script: |
# github.rest.issues.addLabels({
# issue_number: context.issue.number,
# owner: context.repo.owner,
# repo: context.repo.repo,
# labels: ['Z-Community-PR']
# });
close-if-fork-develop:
name: Forbid develop branch fork contributions
+226 -61
View File
@@ -7,11 +7,22 @@ on:
push:
branches: [ main, develop ]
permissions: {}
permissions:
# Private fork: actions/checkout needs contents:read to clone. Upstream is public,
# where an empty token can still read the repo, so an empty permissions block works there and
# fails here with `fatal: repository not found`.
contents: read
# Enrich gradle.properties for CI/CD
env:
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx4g
# Reduced from upstream's -Xmx8g / 4 GB Kotlin daemon for the self-hosted runners.
# Each incus container reports 48 GiB and 8 cores, so a single build has ample room —
# but the containers are oversubscribed against their host, and two concurrent heavy
# Gradle jobs get their daemons SIGKILLed ("the daemon has disappeared", no
# OutOfMemoryError, no heap dump). Running the same job alone succeeds. Lower heaps
# make two builds likelier to coexist; the real fix is sizing the containers to the
# host. See the "Report runner capacity" step.
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx6g -XX:MaxMetaspaceSize=768m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx2g
CI_GRADLE_ARG_PROPERTIES: --stacktrace --no-daemon -Dsonar.gradle.skipCompile=true --no-configuration-cache
jobs:
@@ -20,6 +31,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Free Disk Space (Ubuntu)
# Only meaningful on GitHub's throwaway VMs. On a persistent self-hosted runner this
# would delete dotnet/haskell/docker images from the host on every run.
if: runner.environment == 'github-hosted'
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
@@ -33,7 +47,7 @@ jobs:
docker-images: true
swap-storage: false
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Add SSH private keys for submodule repositories
@@ -51,11 +65,11 @@ jobs:
name: Search for invalid screenshot files
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up Python 3.12
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: 3.14
- name: Search for invalid screenshot files
@@ -63,22 +77,22 @@ jobs:
checkDependencies:
name: Search for invalid dependencies
runs-on: ubuntu-latest
runs-on: [self-hosted, Linux, X64]
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Set up Python 3.12
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: 3.14
- name: Search for invalid dependencies
@@ -87,13 +101,13 @@ jobs:
# Code checks
konsist:
name: Konsist tests
runs-on: ubuntu-latest
runs-on: [self-hosted, Linux, X64]
# Allow all jobs on main and develop. Just one per PR.
concurrency:
group: ${{ github.ref == 'refs/heads/main' && format('check-konsist-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('check-konsist-develop-{0}', github.sha) || format('check-konsist-{0}', github.ref) }}
cancel-in-progress: true
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# Ensure we are building the branch and not the branch after being merged on develop
# https://github.com/actions/checkout/issues/881
@@ -108,19 +122,19 @@ jobs:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
run: git submodule update --init --recursive
- name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Run Konsist tests
run: ./gradlew :tests:konsist:testDebugUnitTest $CI_GRADLE_ARG_PROPERTIES --no-daemon
- name: Upload reports
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: konsist-report
path: |
@@ -128,13 +142,22 @@ jobs:
compose:
name: Compose tests
runs-on: ubuntu-latest
runs-on: [self-hosted, Linux, X64]
# Allow all jobs on main and develop. Just one per PR.
concurrency:
group: ${{ github.ref == 'refs/heads/main' && format('check-compose-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('check-compose-develop-{0}', github.sha) || format('check-compose-{0}', github.ref) }}
cancel-in-progress: true
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# Records the container's real limits so heap settings can be read against them
# instead of guessed at. Mirrors healthchat-release-playstore.yml.
- name: Report runner capacity
if: runner.environment == 'self-hosted'
run: |
free -h; nproc; df -h .
cat /sys/fs/cgroup/memory.max 2>/dev/null \
|| cat /sys/fs/cgroup/memory/memory.limit_in_bytes 2>/dev/null \
|| echo "no cgroup memory limit visible"
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# Ensure we are building the branch and not the branch after being merged on develop
# https://github.com/actions/checkout/issues/881
@@ -149,12 +172,12 @@ jobs:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
run: git submodule update --init --recursive
- name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Run compose tests
@@ -162,13 +185,13 @@ jobs:
lint:
name: Android lint check
runs-on: ubuntu-latest
runs-on: [self-hosted, Linux, X64]
# Allow all jobs on main and develop. Just one per PR.
concurrency:
group: ${{ github.ref == 'refs/heads/main' && format('check-lint-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('check-lint-develop-{0}', github.sha) || format('check-lint-{0}', github.ref) }}
cancel-in-progress: true
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# Ensure we are building the branch and not the branch after being merged on develop
# https://github.com/actions/checkout/issues/881
@@ -183,12 +206,12 @@ jobs:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
run: git submodule update --init --recursive
- name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Build Gplay Debug
@@ -199,7 +222,7 @@ jobs:
run: ./gradlew :app:lintGplayDebug :app:lintFdroidDebug lintDebug $CI_GRADLE_ARG_PROPERTIES --continue
- name: Upload reports
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: linting-report
path: |
@@ -207,13 +230,13 @@ jobs:
detekt:
name: Detekt checks
runs-on: ubuntu-latest
runs-on: [self-hosted, Linux, X64]
# Allow all jobs on main and develop. Just one per PR.
concurrency:
group: ${{ github.ref == 'refs/heads/main' && format('check-detekt-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('check-detekt-develop-{0}', github.sha) || format('check-detekt-{0}', github.ref) }}
cancel-in-progress: true
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# Ensure we are building the branch and not the branch after being merged on develop
# https://github.com/actions/checkout/issues/881
@@ -228,19 +251,19 @@ jobs:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
run: git submodule update --init --recursive
- name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Run Detekt
run: ./gradlew detekt $CI_GRADLE_ARG_PROPERTIES --no-daemon
run: ./gradlew detekt $CI_GRADLE_ARG_PROPERTIES --no-daemon --continue
- name: Upload reports
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: detekt-report
path: |
@@ -248,13 +271,13 @@ jobs:
ktlint:
name: Ktlint checks
runs-on: ubuntu-latest
runs-on: [self-hosted, Linux, X64]
# Allow all jobs on main and develop. Just one per PR.
concurrency:
group: ${{ github.ref == 'refs/heads/main' && format('check-ktlint-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('check-ktlint-develop-{0}', github.sha) || format('check-ktlint-{0}', github.ref) }}
cancel-in-progress: true
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# Ensure we are building the branch and not the branch after being merged on develop
# https://github.com/actions/checkout/issues/881
@@ -269,19 +292,19 @@ jobs:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
run: git submodule update --init --recursive
- name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Run Ktlint check
run: ./gradlew ktlintCheck $CI_GRADLE_ARG_PROPERTIES
run: ./gradlew ktlintCheck $CI_GRADLE_ARG_PROPERTIES --continue
- name: Upload reports
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: ktlint-report
path: |
@@ -295,7 +318,7 @@ jobs:
group: ${{ github.ref == 'refs/heads/main' && format('check-docs-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('check-docs-develop-{0}', github.sha) || format('check-docs-{0}', github.ref) }}
cancel-in-progress: true
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# Ensure we are building the branch and not the branch after being merged on develop
# https://github.com/actions/checkout/issues/881
@@ -319,7 +342,7 @@ jobs:
name: Check shell scripts
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Run shellcheck
@@ -331,39 +354,181 @@ jobs:
name: Run zizmor
runs-on: ubuntu-latest
permissions:
# A job-level block replaces the workflow-level one, so contents:read has to be
# repeated here or checkout fails on this private fork.
contents: read
actions: read # Required for zizmor to read workflow files.
security-events: write # Required for upload-sarif (used by zizmor-action) to upload SARIF files.
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: zizmorcore/zizmor-action@192e21d79ab29983730a13d1382995c2307fbcaa # v0.5.7
- uses: zizmorcore/zizmor-action@cc914d7f3750a2d13d75c7f184a1060aa0e9d482 # v0.6.4
upload_reports:
name: Project Check Suite
runs-on: ubuntu-latest
needs: [konsist, lint, ktlint, detekt]
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
# Run even if a previous job failed: that is precisely when there are issues to report.
if: ${{ !cancelled() }}
permissions:
# Required to list the files modified by the pull request.
pull-requests: read
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
# Ensure we are building the branch and not the branch after being merged on develop
# https://github.com/actions/checkout/issues/881
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
persist-credentials: false
- name: Download reports from previous jobs
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- name: Prepare Danger
if: always()
run: |
npm install --save-dev @babel/core
npm install --save-dev @babel/plugin-transform-flow-strip-types
yarn add danger-plugin-lint-report --dev
- name: Danger lint
if: always()
uses: danger/danger-js@e9b58820039524318198d8059ec98b0c02b7875c # 13.0.10
- name: Annotate the lint, ktlint and detekt issues
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
args: "--dangerfile ./tools/danger/dangerfile-lint.js"
env:
DANGER_GITHUB_API_TOKEN: ${{ secrets.DANGER_GITHUB_API_TOKEN }}
# Fallback for forks
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
script: |
// GitHub only surfaces a handful of annotations per level and per step, so cap what we emit
// and let the job summary carry the full list.
const MAX_ANNOTATIONS = 20;
const MAX_SUMMARY_ROWS = 200;
function unescapeXml(value) {
return value
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&quot;/g, "\"")
.replace(/&apos;/g, "'")
.replace(/&#(\d+);/g, (_, code) => String.fromCharCode(Number(code)))
.replace(/&amp;/g, "&");
}
function attribute(tag, name) {
const match = new RegExp(`\\b${name}="([^"]*)"`).exec(tag);
return match ? unescapeXml(match[1]) : undefined;
}
// Paths in the reports are absolute paths on the runner which generated them, i.e.
// /home/runner/work/<repo>/<repo>/<path>.
const workspace = `/work/${context.repo.repo}/${context.repo.repo}/`;
function repoRelative(path) {
const index = path.indexOf(workspace);
return index === -1 ? path : path.slice(index + workspace.length);
}
// Checkstyle XML, as produced by ktlint and detekt:
// <file name="…"><error line=".." column=".." severity=".." message=".." source=".."/></file>
function parseCheckstyle(content) {
const issues = [];
const fileRegex = /<file\s+([^>]*?)(\/>|>([\s\S]*?)<\/file>)/g;
let file;
while ((file = fileRegex.exec(content)) !== null) {
const path = attribute(file[1], "name");
if (!path) continue;
const errorRegex = /<error\s+([^>]*?)\/?>/g;
let error;
while ((error = errorRegex.exec(file[3] ?? "")) !== null) {
issues.push({
file: repoRelative(path),
line: Number(attribute(error[1], "line")) || 1,
column: Number(attribute(error[1], "column")) || undefined,
severity: (attribute(error[1], "severity") ?? "warning").toLowerCase(),
rule: attribute(error[1], "source"),
message: attribute(error[1], "message") ?? "",
});
}
}
return issues;
}
// Android lint XML, which is not Checkstyle:
// <issue id=".." severity=".." message=".."><location file=".." line=".." column=".."/></issue>
function parseAndroidLint(content) {
const issues = [];
const issueRegex = /<issue\s+([^>]*?)(\/>|>([\s\S]*?)<\/issue>)/g;
let issue;
while ((issue = issueRegex.exec(content)) !== null) {
const location = /<location\s+([^>]*?)\/?>/.exec(issue[3] ?? "");
if (!location) continue;
const path = attribute(location[1], "file");
if (!path) continue;
issues.push({
file: repoRelative(path),
line: Number(attribute(location[1], "line")) || 1,
column: Number(attribute(location[1], "column")) || undefined,
severity: (attribute(issue[1], "severity") ?? "warning").toLowerCase(),
rule: attribute(issue[1], "id"),
message: attribute(issue[1], "message") ?? "",
});
}
return issues;
}
const reports = await (await glob.create("**/reports/**/*.xml")).glob();
core.info(`Found ${reports.length} XML report(s).`);
const fs = require("fs");
let issues = [];
for (const report of reports) {
const content = fs.readFileSync(report, "utf8");
if (content.includes("<checkstyle")) {
issues.push(...parseCheckstyle(content));
} else if (content.includes("<issues")) {
issues.push(...parseAndroidLint(content));
}
}
// Only report on the files touched by the pull request.
const pr = context.payload.pull_request;
if (pr) {
const files = await github.paginate(github.rest.pulls.listFiles, {
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pr.number
});
const changed = new Set(files.map(file => file.filename));
issues = issues.filter(issue => changed.has(issue.file));
}
const seen = new Set();
issues = issues.filter(issue => {
const key = `${issue.file}:${issue.line}:${issue.rule}:${issue.message}`;
if (seen.has(key)) return false;
seen.add(key);
return true;
});
if (issues.length === 0) {
core.info("No issue found in the files modified by this pull request.");
return;
}
const level = severity => {
if (severity === "error" || severity === "fatal") return "error";
if (severity === "warning") return "warning";
return "notice";
};
const rank = { error: 0, warning: 1, notice: 2 };
issues.sort((a, b) =>
rank[level(a.severity)] - rank[level(b.severity)] ||
a.file.localeCompare(b.file) ||
a.line - b.line
);
issues.slice(0, MAX_ANNOTATIONS).forEach(issue => {
const annotation = {
title: issue.rule,
file: issue.file,
startLine: issue.line,
startColumn: issue.column,
};
core[level(issue.severity)](issue.message, annotation);
});
core.summary.addHeading(`${issues.length} code quality issue(s)`, 3);
core.summary.addTable([
[{ data: "Severity", header: true }, { data: "File", header: true }, { data: "Rule", header: true }, { data: "Message", header: true }],
...issues.slice(0, MAX_SUMMARY_ROWS).map(issue => [
level(issue.severity),
`${issue.file}:${issue.line}`,
issue.rule ?? "",
issue.message,
]),
]);
if (issues.length > MAX_SUMMARY_ROWS) {
core.summary.addRaw(`…and ${issues.length - MAX_SUMMARY_ROWS} more, see the uploaded reports.`, true);
}
await core.summary.write();
+37 -11
View File
@@ -5,12 +5,27 @@ on:
pull_request:
types: [ labeled ]
permissions: {}
permissions:
# Private fork: actions/checkout needs contents:read to clone. Upstream is public,
# where an empty token can still read the repo, so an empty permissions block works there and
# fails here with `fatal: repository not found`.
contents: read
# Enrich gradle.properties for CI/CD
env:
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx4g -Dsonar.gradle.skipCompile=true
CI_GRADLE_ARG_PROPERTIES: --no-configuration-cache
# Reduced from upstream's -Xmx8g / 4 GB Kotlin daemon for the self-hosted runners.
# Each incus container reports 48 GiB and 8 cores, so a single build has ample room —
# but the containers are oversubscribed against their host, and two concurrent heavy
# Gradle jobs get their daemons SIGKILLed ("the daemon has disappeared", no
# OutOfMemoryError, no heap dump). Running the same job alone succeeds. Lower heaps
# make two builds likelier to coexist; the real fix is sizing the containers to the
# host. See the "Report runner capacity" step.
# Kept character-identical to tests.yml so the record path and the verify path cannot drift
# apart again. These only affect the Gradle daemon — Paparazzi renders in forked test workers
# configured entirely by build.gradle.kts — but keeping them aligned removes a whole class of
# "did record and verify run the same way?" questions.
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx6g -XX:MaxMetaspaceSize=768m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.daemon.jvm.options=-Xmx2g -XX:+UseG1GC
CI_GRADLE_ARG_PROPERTIES: --stacktrace --no-daemon -Dsonar.gradle.skipCompile=true --no-configuration-cache --max-workers=4
jobs:
record:
@@ -19,11 +34,14 @@ jobs:
pull-requests: write
contents: write
name: Record screenshots on branch ${{ github.event.pull_request.head.ref || github.ref_name }}
runs-on: ubuntu-latest
runs-on: [self-hosted, Linux, X64]
if: github.event_name == 'workflow_dispatch' || github.event.label.name == 'Record-Screenshots'
steps:
- name: Free Disk Space (Ubuntu)
# Only meaningful on GitHub's throwaway VMs. On a persistent self-hosted runner this
# would delete dotnet/haskell/docker images from the host on every run.
if: runner.environment == 'github-hosted'
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
@@ -42,25 +60,29 @@ jobs:
uses: actions-ecosystem/action-remove-labels@2ce5d41b4b6aa8503e285553f75ed56e0a40bae0 # v1.3.0
with:
labels: Record-Screenshots
# Private fork: the action checks out and then runs `git lfs pull` on a cache miss. Upstream
# can do that anonymously because it is public; here the pull needs the job token, so the
# credentials must stay configured. Without this the step dies with
# `could not read Username for 'https://github.com'`.
- name: ⏬ Checkout with LFS (PR)
if: github.event.label.name == 'Record-Screenshots'
uses: nschloe/action-cached-lfs-checkout@385a8ecc719e50b8c71af6ab01a624b486b7c3bc # v1.2.5
uses: nschloe/action-cached-lfs-checkout@385a8ecc719e50b8c71af6ab01a624b486b7c3bc # v1.2.5 zizmor: ignore[artipacked]
with:
persist-credentials: false
persist-credentials: true
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || github.ref }}
- name: ⏬ Checkout with LFS (Branch)
if: github.event_name == 'workflow_dispatch'
uses: nschloe/action-cached-lfs-checkout@385a8ecc719e50b8c71af6ab01a624b486b7c3bc # v1.2.5
uses: nschloe/action-cached-lfs-checkout@385a8ecc719e50b8c71af6ab01a624b486b7c3bc # v1.2.5 zizmor: ignore[artipacked]
with:
persist-credentials: false
persist-credentials: true
- name: ☕️ Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
# Add gradle cache, this should speed up the process
- name: Configure gradle
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Record screenshots
@@ -68,5 +90,9 @@ jobs:
run: ./.github/workflows/scripts/recordScreenshots.sh
env:
GITHUB_TOKEN: ${{ secrets.DANGER_GITHUB_API_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_REPOSITORY: ${{ secrets.GITHUB_REPOSITORY }}
# Must be the `github.repository` context, not a secret. There is no
# GITHUB_REPOSITORY secret on this fork, so `secrets.GITHUB_REPOSITORY` expands to ""
# and overrides the built-in env var of the same name — recordScreenshots.sh then
# exits 1 with "No repo specified" before Gradle ever runs.
GITHUB_REPOSITORY: ${{ github.repository }}
GRADLE_ARGS: ${{ env.CI_GRADLE_ARG_PROPERTIES }}
+12 -46
View File
@@ -34,16 +34,18 @@ jobs:
docker-images: true
swap-storage: false
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Check Matrix Rust SDK is not a nightly version
run: ./tools/release/check_no_nightly_sdk.sh
- name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
- name: Create app bundle
env:
ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }}
@@ -57,50 +59,12 @@ jobs:
ELEMENT_CALL_RAGESHAKE_URL: ${{ secrets.ELEMENT_CALL_RAGESHAKE_URL }}
run: ./gradlew bundleGplayRelease $CI_GRADLE_ARG_PROPERTIES
- name: Upload bundle as artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: elementx-app-gplay-bundle-unsigned
path: |
app/build/outputs/bundle/gplayRelease/app-gplay-release.aab
enterprise:
name: Create App Bundle Enterprise
runs-on: ubuntu-latest
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
concurrency:
group: ${{ format('build-release-main-enterprise-{0}', github.sha) }}
cancel-in-progress: true
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Add SSH private keys for submodule repositories
uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
with:
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
- name: Clone submodules
run: git submodule update --init --recursive
- name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
- name: Create Enterprise app bundle
env:
ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }}
ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }}
ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }}
run: ./gradlew bundleGplayRelease $CI_GRADLE_ARG_PROPERTIES
- name: Upload bundle as artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: elementx-enterprise-app-gplay-bundle-unsigned
path: |
app/build/outputs/bundle/gplayRelease/app-gplay-release.aab
fdroid:
name: Create APKs (FDroid)
runs-on: ubuntu-latest
@@ -122,16 +86,18 @@ jobs:
docker-images: true
swap-storage: false
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Check Matrix Rust SDK is not a nightly version
run: ./tools/release/check_no_nightly_sdk.sh
- name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
- name: Create APKs
env:
ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }}
@@ -139,7 +105,7 @@ jobs:
ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }}
run: ./gradlew assembleFdroidRelease $CI_GRADLE_ARG_PROPERTIES
- name: Upload apks as artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: elementx-app-fdroid-apks-unsigned
path: |
@@ -51,18 +51,13 @@ if [[ -z ${REPO} ]]; then
exit 1
fi
echo "Deleting previous screenshots"
./gradlew removeOldSnapshots --stacktrace --warn $GRADLE_ARGS
# Note: the previous screenshots are deliberately kept, so that Paparazzi can leave the ones that only
# differ by less than `maxPercentDifference` untouched instead of rewriting their Git LFS blob. The
# screenshots of previews that no longer exist are pruned afterwards by `pruneObsoleteSnapshots`, which
# `recordPaparazziDebug` is finalized by.
echo "Record screenshots"
./gradlew recordPaparazziDebug --stacktrace $GRADLE_ARGS
echo "Deleting previous screenshots"
./gradlew removeOldScreenshots --stacktrace --warn $GRADLE_ARGS
echo "Record screenshots (Compound)"
./gradlew :libraries:compound:recordRoborazziDebug --stacktrace -PpreDexEnable=false --max-workers 4 --warn $GRADLE_ARGS
echo "Committing changes"
git config http.sslVerify false
@@ -78,6 +73,10 @@ else
git config --local user.name "${INPUT_AUTHOR_EMAIL}"
fi
git add -A
if git diff --cached --quiet; then
echo "Screenshots are already up to date, nothing to commit."
exit 0
fi
git commit -m "Update screenshots"
GITHUB_REPO="https://$GITHUB_ACTOR:$TOKEN@github.com/$REPO.git"
+20 -6
View File
@@ -7,24 +7,38 @@ on:
push:
branches: [ main, develop ]
permissions: {}
permissions:
# Private fork: actions/checkout needs contents:read to clone. Upstream is public,
# where an empty token can still read the repo, so an empty permissions block works there and
# fails here with `fatal: repository not found`.
contents: read
# Enrich gradle.properties for CI/CD
env:
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx4g
# Reduced from upstream's -Xmx8g / 4 GB Kotlin daemon for the self-hosted runners.
# Each incus container reports 48 GiB and 8 cores, so a single build has ample room —
# but the containers are oversubscribed against their host, and two concurrent heavy
# Gradle jobs get their daemons SIGKILLed ("the daemon has disappeared", no
# OutOfMemoryError, no heap dump). Running the same job alone succeeds. Lower heaps
# make two builds likelier to coexist; the real fix is sizing the containers to the
# host. See the "Report runner capacity" step.
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx6g -XX:MaxMetaspaceSize=768m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx2g
CI_GRADLE_ARG_PROPERTIES: --stacktrace --warn -Dsonar.gradle.skipCompile=true --no-configuration-cache
GROUP: ${{ format('sonar-{0}', github.ref) }}
jobs:
sonar:
name: Sonar Quality Checks
runs-on: ubuntu-latest
runs-on: [self-hosted, Linux, X64]
# Allow all jobs on main and develop. Just one per PR.
concurrency:
group: ${{ format('sonar-{0}', github.ref) }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/develop' }}
steps:
- name: Free Disk Space (Ubuntu)
# Only meaningful on GitHub's throwaway VMs. On a persistent self-hosted runner this
# would delete dotnet/haskell/docker images from the host on every run.
if: runner.environment == 'github-hosted'
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
@@ -38,19 +52,19 @@ jobs:
docker-images: true
swap-storage: false
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# Ensure we are building the branch and not the branch after being merged on develop
# https://github.com/actions/checkout/issues/881
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
persist-credentials: false
- name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Build debug code and test fixtures
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
permissions:
issues: write
steps:
- uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0
- uses: actions/stale@4391f3da665fdf50b6810c1a66712fb9ba21aa93 # v11.0.0
with:
only-labels: "X-Needs-Info"
days-before-issue-stale: 30
@@ -0,0 +1,85 @@
name: Sync Enterprise submodule
on:
workflow_dispatch:
push:
branches: [ develop ]
paths: [ enterprise ]
schedule:
# At 06:00 UTC every day
- cron: '0 6 * * *'
permissions: {}
jobs:
sync-enterprise-submodule:
runs-on: ubuntu-latest
# Skip in forks
if: github.repository == 'element-hq/element-x-android'
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Add SSH private keys for submodule repositories
uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
with:
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
- name: Check enterprise submodule reference
id: check
env:
EVENT_NAME: ${{ github.event_name }}
run: |
current=$(git ls-tree HEAD enterprise | awk '{print $3}')
# Do not init the submodule: the current commit may be on a deleted branch.
git init -q "$RUNNER_TEMP/enterprise"
git -C "$RUNNER_TEMP/enterprise" fetch -q git@github.com:element-hq/element-android-enterprise.git main
main=$(git -C "$RUNNER_TEMP/enterprise" rev-parse FETCH_HEAD)
if git -C "$RUNNER_TEMP/enterprise" merge-base --is-ancestor "$current" "$main" 2>/dev/null; then
on_main=true
else
on_main=false
fi
echo "Current: $current, main: $main, current is on main: $on_main"
if [ "$current" = "$main" ]; then
echo "Submodule is already up to date."
echo "update=false" >> "$GITHUB_OUTPUT"
elif [ "$EVENT_NAME" = "push" ] && [ "$on_main" = "true" ]; then
echo "Submodule targets a commit on main, nothing to do."
echo "update=false" >> "$GITHUB_OUTPUT"
else
echo "update=true" >> "$GITHUB_OUTPUT"
fi
{
echo "body<<EOF"
if [ "$on_main" = "true" ]; then
# Do not list the commit messages, the enterprise repository is private.
count=$(git -C "$RUNNER_TEMP/enterprise" rev-list --count "$current..$main")
echo "Update the \`enterprise\` submodule from \`$current\` to the latest commit of \`main\` (\`$main\`), $count new commit(s)."
else
echo "The \`enterprise\` submodule targets \`$current\`, which is not on the \`main\` branch."
echo
echo "Update it to the latest commit of \`main\` (\`$main\`)."
fi
echo "EOF"
} >> "$GITHUB_OUTPUT"
if [ "$current" != "$main" ]; then
# Update the submodule reference without cloning it.
git update-index --cacheinfo "160000,$main,enterprise"
git -c user.name="github-actions[bot]" -c user.email="41898282+github-actions[bot]@users.noreply.github.com" \
commit -q -m "Update enterprise submodule to latest main" -m "Update to $main"
fi
- name: Create Pull Request to update the enterprise submodule
# On schedule and manual runs, also run when there is nothing to update, so that a stale PR gets closed.
if: ${{ steps.check.outputs.update == 'true' || github.event_name != 'push' }}
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with:
token: ${{ secrets.DANGER_GITHUB_API_TOKEN }}
title: Update enterprise submodule to latest main
body: ${{ steps.check.outputs.body }}
branch: sync-enterprise-submodule
base: develop
delete-branch: true
labels: PR-Element-Pro
+7 -4
View File
@@ -7,26 +7,29 @@ on:
permissions: {}
env:
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx4g
jobs:
sync-localazy:
runs-on: ubuntu-latest
# Skip in forks
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Set up Python 3.12
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: 3.14
- name: Setup Localazy
+2 -2
View File
@@ -14,11 +14,11 @@ jobs:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
# No concurrency required, runs every time on a schedule.
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up Python 3.12
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: 3.14
- name: Install Prerequisite dependencies
+57 -13
View File
@@ -7,24 +7,53 @@ on:
push:
branches: [ main, develop ]
permissions: {}
permissions:
# Private fork: actions/checkout needs contents:read to clone. Upstream is public,
# where an empty token can still read the repo, so an empty permissions block works there and
# fails here with `fatal: repository not found`.
contents: read
# Enrich gradle.properties for CI/CD
env:
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx7g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.daemon.jvm.options=-Xmx2g -XX:+UseG1GC
CI_GRADLE_ARG_PROPERTIES: --stacktrace --no-daemon -Dsonar.gradle.skipCompile=true --no-configuration-cache
# Reduced from upstream's -Xmx8g / 4 GB Kotlin daemon for the self-hosted runners.
# Each incus container reports 48 GiB and 8 cores, so a single build has ample room —
# but the containers are oversubscribed against their host, and two concurrent heavy
# Gradle jobs get their daemons SIGKILLed ("the daemon has disappeared", no
# OutOfMemoryError, no heap dump). Running the same job alone succeeds. Lower heaps
# make two builds likelier to coexist; the real fix is sizing the containers to the
# host. See the "Report runner capacity" step.
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx6g -XX:MaxMetaspaceSize=768m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.daemon.jvm.options=-Xmx2g -XX:+UseG1GC
# --max-workers caps how many test tasks run at once. build.gradle.kts sets
# maxParallelForks = cores/2 and screenshot tests fork at maxHeapSize=2g, so on these
# 8-core containers the default (workers = cores = 8) allows 8 x 4 x 2g = 64 GB of test
# JVMs against a 48 GiB cgroup limit, with no swap — the daemon gets OOM-killed and
# Gradle reports "the daemon has disappeared". GitHub's 4-core hosted runners reach only
# 2 x 2 x 2g = 16 GB and additionally have the 8 GB swapfile added above.
CI_GRADLE_ARG_PROPERTIES: --stacktrace -Dsonar.gradle.skipCompile=true --no-configuration-cache --max-workers=4
jobs:
tests:
name: Runs unit tests
runs-on: ubuntu-latest
runs-on: [self-hosted, Linux, X64]
# Allow all jobs on main and develop. Just one per PR.
concurrency:
group: ${{ github.ref == 'refs/heads/main' && format('unit-tests-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('unit-tests-develop-{0}', github.sha) || format('unit-tests-{0}', github.ref) }}
cancel-in-progress: true
steps:
# Records the container's real limits so heap settings can be read against them
# instead of guessed at. Mirrors healthchat-release-playstore.yml.
- name: Report runner capacity
if: runner.environment == 'self-hosted'
run: |
free -h; nproc; df -h .
cat /sys/fs/cgroup/memory.max 2>/dev/null \
|| cat /sys/fs/cgroup/memory/memory.limit_in_bytes 2>/dev/null \
|| echo "no cgroup memory limit visible"
- name: Free Disk Space (Ubuntu)
# Only meaningful on GitHub's throwaway VMs. On a persistent self-hosted runner this
# would delete dotnet/haskell/docker images from the host on every run.
if: runner.environment == 'github-hosted'
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
@@ -41,6 +70,9 @@ jobs:
# Increase swapfile size to prevent screenshot tests getting terminated
# https://github.com/actions/runner-images/discussions/7188#discussioncomment-6750749
- name: 💽 Increase swapfile size
# /mnt/swapfile is a GitHub runner-image path and this needs passwordless sudo,
# so it only applies to the hosted VMs.
if: runner.environment == 'github-hosted'
run: |
sudo swapoff -a
sudo fallocate -l 8G /mnt/swapfile
@@ -63,25 +95,31 @@ jobs:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
run: git submodule update --init --recursive
- name: ☕️ Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: ⚙️ Check coverage for debug variant (includes unit & screenshot tests)
run: ./gradlew testDebugUnitTest :tests:uitests:verifyPaparazziDebug :koverXmlReportMerged :koverHtmlReportMerged :koverVerifyAll $CI_GRADLE_ARG_PROPERTIES
- name: ⚙️ Run unit tests
run: ./gradlew :app:testGplayDebugUnitTest testDebugUnitTest -x :tests:konsist:testDebugUnitTest $CI_GRADLE_ARG_PROPERTIES --continue
- name: ⚙️ Run screenshot tests
run: ./gradlew verifyPaparazziDebug $CI_GRADLE_ARG_PROPERTIES --continue
- name: ⚙️ Generate kover coverage report
run: ./gradlew :koverXmlReportMerged :koverHtmlReportMerged :koverVerifyAll -x testDebugUnitTest $CI_GRADLE_ARG_PROPERTIES
- name: 🚫 Upload kover failed coverage reports
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: kover-error-report
path: |
app/build/reports/kover
build/reports/kover
- name: ✅ Upload kover report (disabled)
if: always()
@@ -89,12 +127,11 @@ jobs:
- name: 🚫 Upload test results on error
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: tests-and-screenshot-tests-results
path: |
**/build/paparazzi/failures/
**/build/roborazzi/failures/
**/build/reports/tests/*UnitTest/
- name: 🚫 Modify summary on error
@@ -107,8 +144,15 @@ jobs:
echo "---" >> $GITHUB_STEP_SUMMARY
# https://github.com/codecov/codecov-action
# This fork has no CODECOV_TOKEN, and Codecov rejects tokenless uploads from private
# repositories ("Token required - not valid tokenless upload"). With
# fail_ci_if_error: true that failed the whole job *after* the tests had passed, so
# skip the upload unless a token is configured. Adding the secret re-enables it.
- name: ☂️ Upload coverage reports to codecov
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
if: env.CODECOV_TOKEN != ''
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
with:
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}
+5 -1
View File
@@ -2,7 +2,11 @@ name: Validate Git LFS
on: [pull_request, merge_group]
permissions: {}
permissions:
# Private fork: actions/checkout needs contents:read to clone. Upstream is public,
# where an empty token can still read the repo, so an empty permissions block works there and
# fails here with `fatal: repository not found`.
contents: read
jobs:
build:
+3 -3
View File
@@ -31,9 +31,6 @@ __pycache__/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
@@ -82,6 +79,9 @@ captures/
#*.jks
#*.keystore
# HealthChat upload keystore, materialized from a CI secret — never commit it
app/signature/release.keystore
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
.cxx/
+34
View File
@@ -1,5 +1,39 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JavaCodeStyleSettings>
<option name="IMPORT_LAYOUT_TABLE">
<value>
<package name="android" withSubpackages="true" static="true" />
<package name="androidx" withSubpackages="true" static="true" />
<package name="com" withSubpackages="true" static="true" />
<package name="junit" withSubpackages="true" static="true" />
<package name="net" withSubpackages="true" static="true" />
<package name="org" withSubpackages="true" static="true" />
<package name="java" withSubpackages="true" static="true" />
<package name="javax" withSubpackages="true" static="true" />
<package name="" withSubpackages="true" static="true" />
<emptyLine />
<package name="android" withSubpackages="true" static="false" />
<emptyLine />
<package name="androidx" withSubpackages="true" static="false" />
<emptyLine />
<package name="com" withSubpackages="true" static="false" />
<emptyLine />
<package name="junit" withSubpackages="true" static="false" />
<emptyLine />
<package name="net" withSubpackages="true" static="false" />
<emptyLine />
<package name="org" withSubpackages="true" static="false" />
<emptyLine />
<package name="java" withSubpackages="true" static="false" />
<emptyLine />
<package name="javax" withSubpackages="true" static="false" />
<emptyLine />
<package name="" withSubpackages="true" static="false" />
<emptyLine />
</value>
</option>
</JavaCodeStyleSettings>
<JetCodeStyleSettings>
<option name="LINE_BREAK_AFTER_MULTILINE_WHEN_ENTRY" value="false" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
+1 -1
View File
@@ -1,6 +1,6 @@
<component name="CopyrightManager">
<copyright>
<option name="notice" value="© &amp;#36;today.year Element Creations Ltd.&#10;&#10;Element Creations Ltd, Element Software SARL, Element Software Inc.,&#10;and Element Software GmbH (the &quot;Element Group&quot;) only make this file available&#10;under a proprietary license model.&#10;&#10;Without a proprietary license with us, you cannot use this file. The terms of&#10;the proprietary license agreement between you and any member of the Element Group&#10;shall always apply to your use of this file. Unauthorised use, copying, distribution,&#10;or modification of this file, via any medium, is strictly prohibited.&#10;&#10;For details about the licensing terms, you must either visit our website or contact&#10;a member of our sales team." />
<option name="notice" value="© &amp;#36;today.year Element Creations Ltd.&#10;&#10;SPDX-License-Identifier: LicenseRef-Element-Commercial&#10;SPDX-License-Identifier: LicenseRef-Element-Proprietary" />
<option name="myName" value="Element Enterprise" />
</copyright>
</component>
-1
View File
@@ -16,7 +16,6 @@
<w>posthog</w>
<w>rageshake</w>
<w>securebackup</w>
<w>showkase</w>
<w>snackbar</w>
<w>spdx</w>
<w>swipeable</w>
+2 -2
View File
@@ -2,6 +2,6 @@
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="externalSystemId" value="Gradle" />
<option name="version" value="2.4.0" />
<option name="version" value="2.4.20" />
</component>
</project>
</project>
-21
View File
@@ -1,21 +0,0 @@
appId: ${MAESTRO_APP_ID}
---
- tapOn:
id: "login-change_server"
- takeScreenshot: build/maestro/200-ChangeServer
- tapOn: "matrix.org"
- tapOn:
id: "login-change_server"
- tapOn: "Other"
- tapOn:
id: "change_server-server"
- inputText: "element"
- hideKeyboard
- extendedWaitUntil:
visible: "element.io"
timeout: 10000
- tapOn: "element.io"
# Revert to matrix.org
- tapOn:
id: "login-change_server"
- tapOn: "matrix.org"
+8 -2
View File
@@ -3,11 +3,17 @@ appId: ${MAESTRO_APP_ID}
- tapOn: "Sign in manually"
- runFlow: ../assertions/assertLoginDisplayed.yaml
- takeScreenshot: build/maestro/100-SignIn
- runFlow: changeServer.yaml
- runFlow: ../assertions/assertLoginDisplayed.yaml
# The account provider field defaults to matrix.org, so continue straight to the MAS login.
- tapOn:
id: "login-continue"
## MAS page
# Click on "Use another account" if MAS is already logged in:
- runFlow:
when:
visible: "Use another account"
commands:
- tapOn:
text: "Use another account"
- retry:
maxRetries: 3
commands:
+10 -1
View File
@@ -6,10 +6,19 @@ appId: ${MAESTRO_APP_ID}
- takeScreenshot: build/maestro/900-SignOutScreen
- back
- tapOn: "Remove this device"
# Ensure pressing back cancels
- back
- tapOn: "Remove this device"
# Ensure cancel cancels
- tapOn: "Cancel"
- tapOn: "Remove this device"
- tapOn:
id: "sign-out-submit"
# Cancel the dialog cancels the logout
- tapOn:
id: "dialog-negative"
- tapOn: "Remove this device"
- tapOn:
id: "sign-out-submit"
- tapOn:
id: "dialog-positive"
- runFlow: ../assertions/assertInitDisplayed.yaml
+1 -1
View File
@@ -16,4 +16,4 @@ appId: ${MAESTRO_APP_ID}
- extendedWaitUntil:
visible: "Device verified"
timeout: 30000
- tapOn: "Continue"
- tapOn: "Done"
@@ -1,5 +1,5 @@
appId: ${MAESTRO_APP_ID}
---
- extendedWaitUntil:
visible: "Change account provider"
visible: "Enter your account provider"
timeout: 10000
@@ -8,6 +8,12 @@ appId: ${MAESTRO_APP_ID}
- tapOn: "Add description…"
- inputText: "aRoomTopic"
- tapOn: "Create"
# It can take ages to create room on matrix.org
- extendedWaitUntil:
timeout: 30000
visible: "Invite people"
- takeScreenshot: build/maestro/320-createAndDeleteRoom
- tapOn: "Search for someone"
- inputText: ${MAESTRO_INVITEE1_MXID}
+1 -1
View File
@@ -1,7 +1,7 @@
appId: ${MAESTRO_APP_ID}
---
- runFlow: cleanUpRoomList.yaml
- runFlow: searchRoomList.yaml
#- runFlow: searchRoomList.yaml
- takeScreenshot: build/maestro/300-RoomList
- runFlow: timeline/timeline.yaml
- runFlow: roomContextMenu.yaml
@@ -3,5 +3,5 @@ appId: ${MAESTRO_APP_ID}
- takeScreenshot: build/maestro/520-Timeline
- tapOn: "Add attachment"
- tapOn: "Share location"
- tapOn: "Share selected location"
- tapOn: "Share my location"
- takeScreenshot: build/maestro/521-Timeline
+61 -8
View File
@@ -4,9 +4,12 @@ appId: ${MAESTRO_APP_ID}
id: "home_screen-settings"
- assertVisible: "Settings"
- takeScreenshot: build/maestro/600-Settings
## Account settings
- tapOn:
text: "Analytics"
- assertVisible: "Share analytics data"
id: "settings-user_profile"
- assertVisible: "Edit profile"
- takeScreenshot: build/maestro/610-AccountEditProfile
- back
- tapOn:
@@ -14,16 +17,27 @@ appId: ${MAESTRO_APP_ID}
- assertVisible: "Enable notifications on this device"
- back
- tapOn:
text: "Encryption"
- assertVisible: "Key storage"
- back
- tapOn:
text: "Moderation & safety"
- assertVisible: "Your appearance"
- back
- tapOn:
text: "Report a problem"
- assertVisible: "Report a problem"
- back
## App settings section
- assertVisible: "Appearance"
- tapOn:
text: "About"
- assertVisible: "Copyright"
- assertVisible: "Acceptable use policy"
- assertVisible: "Privacy policy"
text: "Media upload quality"
- assertVisible: "Media upload quality"
- back
- tapOn:
@@ -32,14 +46,53 @@ appId: ${MAESTRO_APP_ID}
- hideKeyboard
- back
- scrollUntilVisible:
direction: DOWN
element:
text: "Location sharing"
- tapOn:
text: "Advanced settings"
- assertVisible: "View source"
text: "Location sharing"
- assertVisible: "Live location updates"
- back
## General section
- scrollUntilVisible:
direction: DOWN
element:
text: "Analytics"
- tapOn:
text: "Analytics"
- assertVisible: "Share analytics data"
- back
- scrollUntilVisible:
direction: DOWN
element:
text: "Labs"
- tapOn:
text: "Labs"
- assertVisible: "Feeling experimental?"
- back
- scrollUntilVisible:
direction: DOWN
element:
text: "About"
- tapOn:
text: "About"
- assertVisible: "Copyright"
- assertVisible: "Acceptable use policy"
- assertVisible: "Privacy policy"
- back
- scrollUntilVisible:
direction: DOWN
element:
text: "Developer options"
- tapOn:
text: "Developer options"
- assertVisible: "Feature flags"
- assertVisible: "View source"
- back
- back
+5 -3
View File
@@ -37,8 +37,9 @@ PRs must meet these rules.
- Create previews for **all main states** of a Composable.
- Use `@PreviewsDayNight` for consistency.
- Use `PreviewParameterProvider` (e.g., `FooStateProvider`) to provide states.
- Use `PreviewParameterProvider` (e.g., `FooStatePreviewParam`) to provide states. Always add new states to existing providers at the end of the sequence.
- Wrap previews in `ElementPreview { ... }`.
- When writing tests, never try to record the screenshots, the CI will do it.
---
@@ -49,9 +50,10 @@ PRs must meet these rules.
- Title = changelog entry — descriptive, no "Fixes #…".
- Leave description template for the developer. Redirect them to the [contributing etiquette](CONTRIBUTING.md#etiquette).
- Screenshots/videos for visual changes.
- 500 additions max — split large changes.
- 500 additions max into production code. Test code can be larger — split large changes.
- Commits need a title and description; no tiny or massive commits.
- No history rewrites.
- When creating a pull request and if changes contain new or update(s) of the Composable Previews, add the label "Record-Screenshots" to the PR so that the CI will record the screenshots.
---
@@ -88,7 +90,7 @@ We use [Appyx](https://bumble-tech.github.io/appyx/) for navigation and [Molecul
| `FooView.kt` | Stateless Composable rendering the UI from `FooState`. |
| `FooState.kt` | Data class representing the immutable UI state. |
| `FooEvent.kt` | Sealed interface for UI actions sent to the Presenter. |
| `FooStateProvider.kt` | Provides sample states for Previews and Screenshot tests. |
| `FooStatePreviewParam.kt` | Provides sample states for Previews and Screenshot tests. |
| `FooPresenterTest.kt` | Unit tests for the Presenter logic using Turbine. |
---
+515
View File
@@ -1,3 +1,518 @@
Changes in Element X v26.09.4
=============================
## Hotfix version:
Fixes an issue with the room list not displaying all activity from unread messages.
**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v26.09.3...v26.09.4
Changes in Element X v26.09.3
=============================
<!-- Release notes generated using configuration in .github/release.yml at v26.09.3 -->
## What's Changed
### ✨ Features
* Integrate the new timeline renderer behind a feature flag by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7722
* Add headers (`h1-h6`) tag support for the new timeline renderer by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7754
### 🙌 Improvements
* Add smooth animation to voice messages play progress by @bxdxnn in https://github.com/element-hq/element-x-android/pull/7698
* Add linkification to the new timeline renderer by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7731
### 🐛 Bugfixes
* Make global search use a `Joined` filter for room results by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7695
* Fix flaky test in `TimelinePresenterTest` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7702
* Close the threaded timelines created to create a poll, share a location or send media by @bmarty in https://github.com/element-hq/element-x-android/pull/7705
* Start sync manually after completing the auth flow by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7734
* Fix forwarding an event not working in threads by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7743
* Fix blank screen at launch when the homeserver expires the sliding sync session by @bmarty in https://github.com/element-hq/element-x-android/pull/7692
* Fix unread notification count in room list by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7749
### 🗣 Translations
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/7699
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/7736
### 🧱 Build
* Reduce parallel JVMs spawned for tests by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7701
* Replace git flow with regular git commands in the release script V2 by @bmarty in https://github.com/element-hq/element-x-android/pull/7707
* Remove Roborazzi, use Paparazzi for Compound by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7735
* Fix regex for invalid placeholders by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7738
* Add a script to install and launch an APK given a PR URL by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7752
### 🚧 In development 🚧
* New timeline event renderer: add new modules by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7721
* Add recent search results to global search by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7742
### Dependency upgrades
* Update dependency io.sentry:sentry-android to v8.55.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7685
* Update coil to v3.6.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7693
* Update kotlinpoet to v2.4.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7709
* Update plugin sonarqube to v7.5.0.8588 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7671
* Update roborazzi to v1.74.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7712
* Update dependency io.element.android:element-call-embedded to v0.26.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7715
* Use patched Konsist version (forked, 0.18.0) by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7719
* Update dependency net.zetetic:sqlcipher-android to v4.19.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7716
* Update metro to v1.4.3 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7711
* Update actions/setup-java action to v6.0.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7728
* Update dependency androidx.compose:compose-bom to v2026.09.00 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7730
* Update dependency androidx.sqlite:sqlite-ktx to v2.7.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7729
* Update dependencyAnalysis to v3.19.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7755
* Update media3 to v1.11.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7756
* Upgrade SDK bindings to `v26.09.26` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7780
### Others
* Let the user give a reason when removing a message by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7538
* Iterate on client creation by @bmarty in https://github.com/element-hq/element-x-android/pull/7591
* Iteration on Acount provider by @bmarty in https://github.com/element-hq/element-x-android/pull/7703
* Do not report detekt LargeClass issues on test classes by @bmarty in https://github.com/element-hq/element-x-android/pull/7713
* Let the user give an optional reason when removing a message by @bmarty in https://github.com/element-hq/element-x-android/pull/7679
* Make NotificationResultProcessor.emit wait until the batch is processed by @Johennes in https://github.com/element-hq/element-x-android/pull/7640
* Settings iteration: move user status and update multi-account section by @bmarty in https://github.com/element-hq/element-x-android/pull/7741
* Update remove message confirmation dialog. by @bmarty in https://github.com/element-hq/element-x-android/pull/7757
* Add a script to end all the sessions of a Matrix account using MAS by @bmarty in https://github.com/element-hq/element-x-android/pull/7762
* Settings iteration: split account settings and application settings. by @bmarty in https://github.com/element-hq/element-x-android/pull/7750
**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v26.09.2...v26.09.3
Changes in Element X v26.09.2
=============================
<!-- Release notes generated using configuration in .github/release.yml at v26.09.2 -->
## What's Changed
### 🙌 Improvements
* Keep the push foreground service alive while notifications are fetched by @Johennes in https://github.com/element-hq/element-x-android/pull/7639
* Stop clearing the remote config when clearing the cache by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7662
### 🐛 Bugfixes
* Fix fetched remote config being cached with the wrong key by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7626
* Make sure `enforce_element_pro` is taken into account in FOSS by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7631
### 🗣 Translations
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/7628
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/7657
### 🧱 Build
* Allow retrying finding the release assets in release script by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7619
* Improve detection of invalid placeholders in localazy strings by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7624
* Add `GRADLE_OPTS` for `sync-localazy.yml` flow by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7629
* Clean up ProGuard keep rules files by @bmarty in https://github.com/element-hq/element-x-android/pull/7647
* Replace Danger with actions/github-script by @bmarty in https://github.com/element-hq/element-x-android/pull/7649
* Remove Showkase and its in-app component browser by @bmarty in https://github.com/element-hq/element-x-android/pull/7678
* Improve Renovate configuration to fix issues with Maven Central by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7677
### Dependency upgrades
* Update dependency com.posthog:posthog-android to v3.60.7 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7616
* Update dependency org.jsoup:jsoup to v1.23.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7620
* Update dependency io.element.android:element-call-embedded to v0.25.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7622
* Update dependencyAnalysis to v3.19.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7623
* Update wysiwyg to v2.42.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7627
* Update android.gradle.plugin to v9.3.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7603
* Update actions/setup-java action to v6 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7614
* Update danger/danger-js action to v14 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7602
* Update camera to v1.6.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7632
* Update coil to v3.6.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7630
* Update dependency io.sentry:sentry-android to v8.54.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7636
* Update dependency org.jetbrains.kotlinx:kotlinx-collections-immutable to v0.5.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7650
* Bump SDK bindings to `v26.09.3` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7637
* Update zizmorcore/zizmor-action action to v0.6.3 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7658
* Update coil to v3.6.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7664
* Update dependency org.matrix.rustcomponents:sdk-android to v26.09.08 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7668
* Update dependency org.matrix.rustcomponents:sdk-android to v26.09.9 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7683
### 💼 Element Pro
* Fix remote config getting refreshed with `Origin.Cache` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7666
### Others
* Move knock and gallery to labs by @bmarty in https://github.com/element-hq/element-x-android/pull/7625
* Say so when this session has no secrets to verify another one with by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7551
* Expose the timeline item long press and swipe to reply as TalkBack actions by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7458
* Bump PR priority for our dependencies in Renovate by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7642
* Import string from Localazy and remove temporary file. by @bmarty in https://github.com/element-hq/element-x-android/pull/7645
* Revert "Say so when this session has no secrets to verify another one with" by @bmarty in https://github.com/element-hq/element-x-android/pull/7644
* Maestro (local): queue pending runs in the maestro-test concurrency group instead of cancelling them by @andreBurnt in https://github.com/element-hq/element-x-android/pull/7656
* Maestro (local): skip the lane on pull requests from forks by @andreBurnt in https://github.com/element-hq/element-x-android/pull/7655
* Update maplibre compose to 0.15 by @ganfra in https://github.com/element-hq/element-x-android/pull/7663
* Try improving renovate issues with Maven Central rate limiting by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7667
## New Contributors
* @andreBurnt made their first contribution in https://github.com/element-hq/element-x-android/pull/7656
**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v26.09.1...v26.09.2
Changes in Element X v26.09.1
=============================
## What's Changed
### ✨ Features
* Support creating polls with multiple answers and send response with multiple answers by @bmarty in https://github.com/element-hq/element-x-android/pull/7613
### 🙌 Improvements
* Only track read receipts on message-like events by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7361
* Stop rescanning message bodies for emoji only on every recomposition by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7531
### 🐛 Bugfixes
* Ensure that noisy notifications are still noisy when the app has a PIN code. by @bmarty in https://github.com/element-hq/element-x-android/pull/7584
* Ensure that sessionVerifiedStatus is updated when the crypto is ready. by @bmarty in https://github.com/element-hq/element-x-android/pull/7604
* Fix invalid placeholders in translations by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7612
### 🧱 Build
* Do not open the editor when finishing the release by @bmarty in https://github.com/element-hq/element-x-android/pull/7583
* [release] Check that there is no unmerged PR with the label "Z-NextRelease", else exit by @bmarty in https://github.com/element-hq/element-x-android/pull/7606
### Dependency upgrades
* Update dependency net.zetetic:sqlcipher-android to v4.18.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7552
* Update dependency com.google.firebase:firebase-bom to v34.18.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7581
* Update dependency io.github.sergio-sastre.ComposablePreviewScanner:android to v0.9.3 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7535
* Update dependency com.google.guava:guava to v33.7.1-android by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7534
* Update dependency org.unifiedpush.android:connector to v3.3.4 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7586
* Update dependency org.unifiedpush.android:connector to v3.3.5 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7595
* Update roborazzi to v1.73.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7618
### 💼 Element Pro
* Implement a hook allowing enterprise modules to perform actions on startup by @richvdh in https://github.com/element-hq/element-x-android/pull/7585
### Others
* Don't hide the thread of a deleted message inside the removed messages group by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7571
* Name a sender the timeline has no profile for from the room member list by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7539
* Offer conversations in the system share sheet by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7574
* Rank the mention suggestions by how well they match what was typed by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7536
* Log the SDK git Sha at application startup by @bmarty in https://github.com/element-hq/element-x-android/pull/7589
* Limit usage of nullable callback. by @bmarty in https://github.com/element-hq/element-x-android/pull/7588
* Fix crash due to JSON.stringify on a cirular structure in injectes js. by @toger5 in https://github.com/element-hq/element-x-android/pull/7600
* Support multi-select polls by @bxdxnn in https://github.com/element-hq/element-x-android/pull/6477
* Remove ExternalSession, fallback login with Element Web is gone. by @bmarty in https://github.com/element-hq/element-x-android/pull/7608
* Fall back to the message body when the formatted body has nothing to render by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7523
**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v26.08.4...v26.09.1
Changes in Element X v26.08.4
=============================
<!-- Release notes generated using configuration in .github/release.yml at v26.08.4 -->
## What's Changed
### 🧱 Build
* Add releaseV2.sh, a release script automating the GitHub release by @bmarty in https://github.com/element-hq/element-x-android/pull/7579
* Update submodule ref. by @bmarty in https://github.com/element-hq/element-x-android/pull/7582
### Others
* Keep the formatting of a caption that is being edited by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7572
* Say when the server cannot be reached instead of showing nothing by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7567
* Keep saving files to Downloads once 32 files share the same name by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7344
* Fix removed messages hiding its thread on deletion by @bxdxnn in https://github.com/element-hq/element-x-android/pull/7480
* Drop the sender's name from the room list preview of two-person rooms by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7562
* Document how the Element Call base URL developer option is used by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7546
**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v26.08.3...v26.08.4
Changes in Element X v26.08.3
=============================
<!-- Release notes generated using configuration in .github/release.yml at v26.08.3 -->
## What's Changed
### ✨ Features
* Let users enter their full Matrix user ID on the account provider screen by @jennaharris7 in https://github.com/element-hq/element-x-android/pull/7500
* Enable user status feature and hide status text in the timeline by @bmarty in https://github.com/element-hq/element-x-android/pull/7517
### 🐛 Bugfixes
* Fix cold start with a mobile deep link by @bmarty in https://github.com/element-hq/element-x-android/pull/7488
* Make the account provider screen scrollable so its description is not clipped by the keyboard by @jennaharris7 in https://github.com/element-hq/element-x-android/pull/7521
### 🧱 Build
* Update Gradle Wrapper from 9.7.0 to 9.7.1 by @ElementBot in https://github.com/element-hq/element-x-android/pull/7485
* Fix flaky test in `DefaultVoiceMessageComposerPresenterTest` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7487
* Fix warning about deprecated Gradle `keepRules.file` API by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7498
* Update Enterprise Copyright in idea settings, so that new files get an expected header. by @bmarty in https://github.com/element-hq/element-x-android/pull/7501
* Remove workaround for Renovate. by @bmarty in https://github.com/element-hq/element-x-android/pull/7502
### 🚧 In development 🚧
* Make PIP react to camera orientation changes in the other participant's device by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6951
* Feature: global search by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7410
### Dependency upgrades
* Update dependency org.maplibre.gl:android-sdk to v13.5.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7486
* Update dependency androidx.webkit:webkit to v1.17.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7484
* Update dependency io.sentry:sentry-android to v8.53.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7481
* Update dependency io.element.android:element-call-embedded to v0.24.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7474
* Update roborazzi to v1.72.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7489
* Update metro to v1.4.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7493
* Update dependency com.squareup.okhttp3:okhttp-bom to v5.5.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7512
* Update dependency com.google.guava:guava to v33.7.0-android by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7516
* Update sdk to version 26.08.25 by @bmarty in https://github.com/element-hq/element-x-android/pull/7550
### 💼 Element Pro
* Add `ClientEnterpriseHook` and `ClientBuilderEnterpriseHook` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7495
* Remove dependency on enterprise module. by @bmarty in https://github.com/element-hq/element-x-android/pull/7505
### Others
* Show the right image when opening one that has not finished sending by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7359
* Move strings to the login module by @bmarty in https://github.com/element-hq/element-x-android/pull/7494
* Allow empty problem description if the GitHub issue number is present for rageshakes by @bxdxnn in https://github.com/element-hq/element-x-android/pull/7497
* Apply the system theme when switching back to it from a forced light or dark theme by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7453
* Show dialog with reason when user clicks on a message with a failure by @bmarty in https://github.com/element-hq/element-x-android/pull/7490
* Keep a focused timeline focused until the event has been rendered by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7463
* Do not render in-chat key verification requests in the timeline by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7443
* Share a file whose mime type is not one of the supported ones by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7435
* Show reactions in the pinned messages list by @bxdxnn in https://github.com/element-hq/element-x-android/pull/6676
* Send a concrete mime type for media shared with a wildcard one by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7449
* Rename module and ensure that naming convention is followed by @bmarty in https://github.com/element-hq/element-x-android/pull/7514
* Fix lint issue, ensure that PiP methods are not called on old devices not supporting them. by @bmarty in https://github.com/element-hq/element-x-android/pull/7515
* Rename PreviewParameterProvider subclasses to the PreviewParam suffix by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7468
* Update Ref. by @bmarty in https://github.com/element-hq/element-x-android/pull/7518
* Remove the reference to the decommissioned OIDC playground by @sandhose in https://github.com/element-hq/element-x-android/pull/7519
* Hide the encryption badge when key storage is set up on the server by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7447
* Rename remaining events sealed interface by @bmarty in https://github.com/element-hq/element-x-android/pull/7533
* Stop waiting on a verification request the other session never accepts by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7457
* Point the proxy debugging doc at the method that builds the clients by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7543
* Show the build date in the screenshot gallery footer by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7544
* Run the database migrations again when a database is upgraded by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7570
* Update fonts on the headings by @bmarty in https://github.com/element-hq/element-x-android/pull/7520
* Add a "Push rules" entry in the developer options by @bmarty in https://github.com/element-hq/element-x-android/pull/7577
## New Contributors
* @sandhose made their first contribution in https://github.com/element-hq/element-x-android/pull/7519
**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v26.08.2...v26.08.3
Changes in Element X v26.08.2
=============================
<!-- Release notes generated using configuration in .github/release.yml at v26.08.2 -->
**Note: this release in an internal release, it will not be pushed to the stores.**
## What's Changed
### ✨ Features
* Remove the legacy web-based account creation for matrix.org by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7412
### 🙌 Improvements
* Move the custom tile server URL config to the Element wellknown content by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7416
* Explain a rate limiting push gateway in the notification troubleshooter by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7448
* Move the `:libraries:wellknown` logic into `enterprise` modules by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7429
* Refresh the room members when a mention is started by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7438
* Close the invite screen once the invite has been declined by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7440
* Use Profiles sliding sync extension check for own profile subscription by @ganfra in https://github.com/element-hq/element-x-android/pull/7403
* Simplify choosing the account provider during sign in, with autocomplete of previously used ones by @jennaharris7 in https://github.com/element-hq/element-x-android/pull/7394
### 🐛 Bugfixes
* Fix wrong TextComposer placeholder displayed when in a thread. by @bmarty in https://github.com/element-hq/element-x-android/pull/7413
* Fix bug reports failing to send after the app has crashed by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7395
* Fix outgoing session verification stuck with incomplete recovery setup by @hardcore-sushi in https://github.com/element-hq/element-x-android/pull/7433
* Keep transparency when an image is shared into the app with an imprecise mime type by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7452
* Don't restart the sync service if the client is shutting down by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7472
* Normalize the applicationName used in user agent by @bmarty in https://github.com/element-hq/element-x-android/pull/7477
* Keep playing a voice message when another app plays a short sound by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7454
* Keep the send button when the composer is emptied while editing a message by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7456
* Do not open the room again when the app is resumed from the recents list by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7436
* Stop waiting for a room alias that the remote server never resolves by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7446
### 🧱 Build
* build-rust-sdk: print the command we run by @richvdh in https://github.com/element-hq/element-x-android/pull/7407
* Remove license check on Pro modules. by @bmarty in https://github.com/element-hq/element-x-android/pull/7418
* Improve build time config and update the Firebase configuration by @bmarty in https://github.com/element-hq/element-x-android/pull/7473
* Make the PUSHER_APP_ID configurable at build time. by @bmarty in https://github.com/element-hq/element-x-android/pull/7478
### 📄 Documentation
* Add KDoc to all the interfaces of the api modules by @bmarty in https://github.com/element-hq/element-x-android/pull/7426
### 🚧 In development 🚧
* [LinkNewDevice] Ensure the notice screen is also protected by the timeout. by @bmarty in https://github.com/element-hq/element-x-android/pull/7428
### Dependency upgrades
* Update kotlin to v2.3.11 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7396
* Update plugin sonarqube to v7.4.0.8496 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7415
* Update metro to v1.4.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7421
* Update roborazzi to v1.71.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7423
* Update dependency io.sentry:sentry-android to v8.52.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7414
* Update dependency org.matrix.rustcomponents:sdk-android to v26.08.13 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7420
* Update dependency io.github.sergio-sastre.ComposablePreviewScanner:android to v0.9.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7431
* Update dependency org.maplibre.compose:maplibre-compose to v0.14.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7432
* Update dependency org.matrix.rustcomponents:sdk-android to v26.08.17 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7460
* Update dependency org.matrix.rustcomponents:sdk-android to v26.08.19 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7479
* Update media3 to v1.11.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7427
* Update dependency androidx.compose:compose-bom to v2026.08.00 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7483
### 💼 Element Pro
* Add hooks to allow providing additional feature flags in EX pro by @richvdh in https://github.com/element-hq/element-x-android/pull/7419
* Use the right base URL for the ESS config/well known endpoints by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7469
### Others
* Do not display pinned events that the client cannot render by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7379
* Hide the forward action on live location shares by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7343
* Do not fail the screenshot recording job when there is nothing to commit by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7355
* Show the account a UnifiedPush registration belongs to in the distributor by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7345
* Stop repeating messages that are already shown in a room notification by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7370
* Turn a matrix: URI in a message into a pill by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7354
* Send a message with Enter on a physical keyboard, Shift+Enter for a new line by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7353
* Do not group timeline messages sent more than five minutes apart by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7349
* Linkify URLs whose host is a bracketed IPv6 address by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7347
* Add missing tests for `RustTemporaryMatrixClient` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7417
* Revert "Keep manually revealed media visible when re-entering a room" by @bmarty in https://github.com/element-hq/element-x-android/pull/7424
* Fix outdated documentation. by @bmarty in https://github.com/element-hq/element-x-android/pull/7430
* Sync strings - New language supported: Arabic by @bmarty in https://github.com/element-hq/element-x-android/pull/7461
* Keep the focused login field visible above the keyboard by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7455
* Relax image compression so photos and screenshots keep more detail by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7451
* Open the user profile of a user id that contains a slash by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7445
* Remove the F-Droid dex patching step from the release script by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7442
* Convert markdown to HTML when a caption is edited by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7441
* Exclude carriage returns from the login and password fields by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7439
* Show an error when the room to leave cannot be found by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7437
* Give the room name in the timeline top bar a 48dp tap target by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7450
* Model the fake voice recorder on a cancellable recording job by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7466
## New Contributors
* @hardcore-sushi made their first contribution in https://github.com/element-hq/element-x-android/pull/7433
**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v26.08.1...v26.08.2
Changes in Element X v26.08.1
=============================
<!-- Release notes generated using configuration in .github/release.yml at v26.08.1 -->
## What's Changed
### 🙌 Improvements
* Render push notifications for live location share starts (MSC4505) by @ara4n in https://github.com/element-hq/element-x-android/pull/7203
* [Task] Show active call participants in the people list #7199 by @Eklavya47 in https://github.com/element-hq/element-x-android/pull/7300
### 🐛 Bugfixes
* Fix overlap issue. by @bmarty in https://github.com/element-hq/element-x-android/pull/7331
* Announce checked/unchecked state for user rows in invite screen by @bmarty in https://github.com/element-hq/element-x-android/pull/7136
* Display 'add reactions' menu in the threads screen by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7388
* Expand the emoji picker to full height when the keyboard is shown by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7382
* UI fixes by @bmarty in https://github.com/element-hq/element-x-android/pull/7409
### 🗣 Translations
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/7389
### 🧱 Build
* Don't check enterprise previews in `KonsistPreviewTest` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7297
* CI: split unit tests, screenshots and coverage in `tests.yml` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7296
* Update Gradle Wrapper from 9.6.1 to 9.7.0 by @ElementBot in https://github.com/element-hq/element-x-android/pull/7335
### 🚧 In development 🚧
* QR code login iteration by @bmarty in https://github.com/element-hq/element-x-android/pull/6719
* User status last pieces by @ganfra in https://github.com/element-hq/element-x-android/pull/7328
* [Link new device] Add timeout by @bmarty in https://github.com/element-hq/element-x-android/pull/7338
### Dependency upgrades
* Update dependency org.maplibre.compose:maplibre-compose to v0.13.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7291
* Update android.gradle.plugin to v9.3.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7248
* Update dependency io.sentry:sentry-android to v8.50.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7298
* Update zizmorcore/zizmor-action action to v0.6.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7306
* Update android.gradle.plugin to v9.3.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7304
* Update dependency com.posthog:posthog-android to v3.56.4 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7311
* Update dependencyAnalysis to v3.18.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7318
* Update actions/stale action to v11 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7317
* Update dependency io.github.zxing-cpp:android to v3.1.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7322
* Update dependency org.matrix.rustcomponents:sdk-android to v26.08.05 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7324
* Update dependency androidx.constraintlayout:constraintlayout to v2.2.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7329
* Update reactivecircus/android-emulator-runner action to v2.38.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7189
* Update dependency io.sentry:sentry-android to v8.51.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7301
* Update dependency org.maplibre.gl:android-sdk to v13.4.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7305
* Update dependency io.element.android:element-call-embedded to v0.23.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7323
* Update metro to v1.4.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7334
* Update dependency com.google.firebase:firebase-bom to v34.17.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7333
* Update dependency androidx.compose.material3:material3 to v1.5.0-alpha23 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7175
* Update dependency org.jsoup:jsoup to v1.23.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7332
* Update dependency androidx.constraintlayout:constraintlayout-compose to v1.1.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7330
* Update zizmorcore/zizmor-action action to v0.6.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7383
* Update actions/setup-java action to v5.7.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7339
* Update gradle/actions action to v6.3.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7384
* Update plugin dependencycheck to v13 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7386
* Update dependency org.matrix.rustcomponents:sdk-android to v26.08.11 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7402
### 💼 Element Pro
* Add support for checking the ESS config in Element Pro by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7302
### Others
* Add extra vertical padding to `ReplyToInvalidContent` and `ReplyToNotFoundContent` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7288
* Index messages locally in an encrypted on-device store so encrypted rooms can be searched at all by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7249
* Bump enterprise module to the latest commit by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7307
* Bump enterprise module to the latest commit by @jennaharris7 in https://github.com/element-hq/element-x-android/pull/7309
* Move deviceId and git details to developer settings by @bmarty in https://github.com/element-hq/element-x-android/pull/7241
* Show a Message not found placeholder when a replied to message cannot be loaded by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7376
* Update in reply to message when message is not found. by @bmarty in https://github.com/element-hq/element-x-android/pull/7387
* Add default `OAUTH_CLIENT_URL_PATH` alternative by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7385
* Fix the feature module template writing the impl build script into the api module by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7374
* Let users enter their full Matrix user ID when searching for a homeserver by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7375
* Keep manually revealed media visible when re-entering a room by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7378
* Copy a blocked user's Matrix ID by long pressing them by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7372
* Do not draw the media loading progress bar under the status bar by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7366
* Do not notify about an event sent by another account on the same device by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7360
* Keep the opened image on screen when the media gallery fails to load by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7362
* Do not linkify the server part of a fediverse handle as an email address by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7356
* Expose global account data get and set on MatrixClient by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7373
* Show the alias or the direct message user's Matrix ID in the room picker by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7371
* Use a dedicated string for the Mute room notification mode by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7367
* Keep playing a voice message when the screen turns off by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7365
* Let a voice message without a waveform be seeked by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7363
* Ignore a second voice recording start while one is already running by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7346
* Do not offer to start a call in a DM with no other member by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7352
* Do not notify about a redaction whose target event is unknown by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7350
* Ignore a room list filter that is incompatible with an already selected one by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7342
* Stop a right to left sender name from reordering a room list preview by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7348
* Do not append the application MapTiler key to a custom map style url by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7340
* Hide space children you have left that can only be joined by invitation by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7369
* Never re-encode a video at a higher bitrate than the source by @hayaksi1 in https://github.com/element-hq/element-x-android/pull/7351
* Display push notifications for knocks by @ara4n in https://github.com/element-hq/element-x-android/pull/7207
## New Contributors
* @hayaksi1 made their first contribution in https://github.com/element-hq/element-x-android/pull/7249
* @ara4n made their first contribution in https://github.com/element-hq/element-x-android/pull/7203
* @Eklavya47 made their first contribution in https://github.com/element-hq/element-x-android/pull/7300
**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v26.08.0...v26.08.1
Changes in Element X v26.08.0
=============================
<!-- Release notes generated using configuration in .github/release.yml at v26.08.0 -->
## What's Changed
### ✨ Features
* feat(call): Active call timeline rendering by @BillCarsonFr in https://github.com/element-hq/element-x-android/pull/7051
### 🙌 Improvements
* Announce expanded/collapsed state on the timeline group header by @unclejay80 in https://github.com/element-hq/element-x-android/pull/7226
* Allow adding extra elements to the developer settings from external modules by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7230
* Update keyboard visibility to track hasFocus by @jennaharris7 in https://github.com/element-hq/element-x-android/pull/7284
### 🐛 Bugfixes
* Live location sharing timeout by @ganfra in https://github.com/element-hq/element-x-android/pull/7184
* Fix logging via QR code when using MDM-configured homeserver by @mxandreas in https://github.com/element-hq/element-x-android/pull/7228
* Fix #7181: Remove back arrow from device verified screen and use 'Done' text by @thirumani-vihaan in https://github.com/element-hq/element-x-android/pull/7222
* Fix padding for captions of media in 'not found' state by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7247
* Fix banned mime type errors in `DefaultContentScannerService` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7255
* Fix `ContentErrorView` not calculating its text size synchronously by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7281
* Fix media viewer for medias not related to events displaying invalid state by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7280
* Try making `HeaderFooterView` more robust by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7267
* Fix insets in several screens by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7285
### ⚠️ API Changes
* Add a password strength estimator wrapping the SDK zxcvbn API by @jennaharris7 in https://github.com/element-hq/element-x-android/pull/7186
### 🗣 Translations
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/7277
### 🧱 Build
* CI: fix 'Update Gradle Wrapper' by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7238
* Update Gradle Wrapper from 9.5.1 to 9.6.1 by @ElementBot in https://github.com/element-hq/element-x-android/pull/7240
* Localazy: improve search, parallelize checks by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7261
* Run `Konsist` tests sequentially by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7293
### 📄 Documentation
* Group Element Pro changes into a dedicated release notes section by @manuroe in https://github.com/element-hq/element-x-android/pull/7236
### 🚧 In development 🚧
* Plug emoji picker for custom user status by @ganfra in https://github.com/element-hq/element-x-android/pull/7257
* WIP : display user status by @ganfra in https://github.com/element-hq/element-x-android/pull/7232
### Dependency upgrades
* Update roborazzi to v1.67.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7188
* Update dependency io.sentry:sentry-android to v8.48.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7217
* Update dependency com.google.crypto.tink:tink-android to v1.23.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7229
* Update kotlin to v2.3.10 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7234
* Update dependency net.zetetic:sqlcipher-android to v4.17.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7218
* Update dependency com.google.firebase:firebase-bom to v34.16.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7237
* Update dependencyAnalysis to v3.16.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7212
* Update actions/setup-java action to v5.5.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7214
* Update dependency io.element.android:element-call-embedded to v0.22.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7161
* Update kotlin to v2.4.10 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7244
* Update roborazzi to v1.68.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7245
* Update metro to v1.3.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7243
* Update dependency org.matrix.rustcomponents:sdk-android to v26.07.22 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7258
* Update zizmorcore/zizmor-action action to v0.6.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7256
* Update dependency org.matrix.rustcomponents:sdk-android to v26.07.23 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7262
* Update dependency io.sentry:sentry-android to v8.49.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7263
* Update dependencyAnalysis to v3.17.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7265
* Update actions/setup-java action to v5.6.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7268
* Update dependency org.jetbrains.kotlinx:kover-gradle-plugin to v0.9.9 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7273
* Update actions/setup-python action to v7 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7278
* Update roborazzi to v1.70.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7274
* Update actions/checkout action to v7.0.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7282
* Import Compound tokens from `v10.2.3` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7272
* Update dependency org.matrix.rustcomponents:sdk-android to v26.07.28 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7287
* Update dependency org.maplibre.gl:android-sdk to v13.4.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/7290
### 💼 Element Pro
* Content scanner (Element Pro): scan timeline events for unsafe content by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7159
* Add content scanner support to media gallery and viewer (Element Pro) by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7233
* Enforce HS disabling E2EE in DM creation too (Element Pro) by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7292
* Allow adding a path suffix to the OAuth client URI by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7286
### Others
* [A11y] Improve rendering on focused UI items by @bmarty in https://github.com/element-hq/element-x-android/pull/7182
* Add a button role to clickable icons and text on the login/onboarding screens by @unclejay80 in https://github.com/element-hq/element-x-android/pull/7225
* Expose formatting toggle state to TalkBack by @unclejay80 in https://github.com/element-hq/element-x-android/pull/7227
* Add warning to the 'Scan QR code' screen by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7264
* Refactor SearchBarResultState.Initial/NoResultsFound to data objects by @ganfra in https://github.com/element-hq/element-x-android/pull/7266
* Remove no longer temporary strings by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7260
* Use the right insets in the home screen UI by @jmartinesp in https://github.com/element-hq/element-x-android/pull/7271
* Make settings section separators visible in black theme by @mvanhorn in https://github.com/element-hq/element-x-android/pull/7039
## New Contributors
* @thirumani-vihaan made their first contribution in https://github.com/element-hq/element-x-android/pull/7222
* @mvanhorn made their first contribution in https://github.com/element-hq/element-x-android/pull/7039
**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v26.07.1...v26.08.0
Changes in Element X v26.07.1
=============================
-3
View File
@@ -1,3 +0,0 @@
source "https://rubygems.org"
gem 'danger'
+41
View File
@@ -0,0 +1,41 @@
# HealthChat Android changelog
This file is the single source for Play Store "What's new" texts. The release
pipeline (`.github/workflows/healthchat-release-playstore.yml`) extracts the
top section matching the released version — the heading must have the exact
form `## Changes in <version> (<date>)` and the whole section must stay under
500 characters. See `.claude/skills/releasing-healthchat/SKILL.md`.
## Changes in 26.10.0 (2026-10-20)
🎉 Neue Funktionen
- Nachrichtensuche: Chats durchsuchen, auch in verschlüsselten Räumen.
- Umfragen mit Mehrfachauswahl.
🙌 Verbesserungen
- Basis aktualisiert auf Element X 26.10.0 mit zahlreichen Fehlerbehebungen.
- Einstellungen neu gegliedert in Konto- und App-Einstellungen.
## Changes in 26.08.0 (2026-08-06)
🎉 Neue Funktionen
- Mitgliederverzeichnis: Alle Mitglieder durchsuchen und direkt einen Chat starten.
- Profil bearbeiten: Angaben und Profilbild direkt in der App pflegen.
- Neue Mitglieder per E-Mail einladen.
- Statushinweise zeigen den Stand der Kontofreigabe.
🙌 Verbesserungen
- HealthChat-Links öffnen sich neu direkt in der App statt im Browser.
- Geteilte Profillinks verweisen neu auf HealthChat statt auf Element.
## Changes in 26.07.0 (2026-07-16)
🎉 Komplett neue App-Generation
- HealthChat basiert neu auf Element X: schnellere Synchronisation, moderne Oberfläche und verbesserte Verschlüsselung.
- Anmeldung neu über den sicheren HealthChat Login (SSO).
- Hinweis: Nach dem Update ist eine einmalige Neuanmeldung erforderlich.
@@ -14,16 +14,16 @@ import kotlin.reflect.KClass
* Adds Node to the specified component graph.
* Equivalent to the following declaration:
*
* ```
* @BindingContainer
* @ContributesTo(Scope::class)
* abstract class YourNodeModule {
* @Binds
* @IntoMap
* @NodeKey(YourNode::class)
* abstract fun bindYourNodeFactory(factory: YourNode.Factory): AssistedNodeFactory<*>
*}
* interface YourNodeBindingContainer {
* @Binds
* @IntoMap
* @NodeKey(YourNode::class)
* fun bindYourNodeAssistedFactory(factory: YourNodeAssistedFactory): AssistedNodeFactory<*>
* }
* ```
*/
@Target(AnnotationTarget.CLASS)
annotation class ContributesNode(
+65 -32
View File
@@ -21,6 +21,7 @@ import extension.allFeaturesImpl
import extension.allLibrariesImpl
import extension.allServicesImpl
import extension.buildConfigFieldStr
import extension.environmentOrGradleValue
import extension.locales
import extension.setupDependencyInjection
import extension.testCommonDependencies
@@ -88,13 +89,26 @@ android {
storePassword = "android"
}
register("nightly") {
keyAlias = System.getenv("ELEMENT_ANDROID_NIGHTLY_KEYID")
?: project.property("signing.element.nightly.keyId") as? String?
keyPassword = System.getenv("ELEMENT_ANDROID_NIGHTLY_KEYPASSWORD")
?: project.property("signing.element.nightly.keyPassword") as? String?
storeFile = file("./signature/nightly.keystore")
storePassword = System.getenv("ELEMENT_ANDROID_NIGHTLY_STOREPASSWORD")
?: project.property("signing.element.nightly.storePassword") as? String?
keyAlias = environmentOrGradleValue("ELEMENT_ANDROID_NIGHTLY_KEYID", "signing.element.nightly.keyId")
keyPassword = environmentOrGradleValue("ELEMENT_ANDROID_NIGHTLY_KEYPASSWORD", "signing.element.nightly.keyPassword")
storeFile = file(
environmentOrGradleValue("ELEMENT_ANDROID_NIGHTLY_STOREFILE", "signing.element.nightly.storeFile")
?.ifBlank { null }
?: "./signature/nightly.keystore"
)
storePassword = environmentOrGradleValue("ELEMENT_ANDROID_NIGHTLY_STOREPASSWORD", "signing.element.nightly.storePassword")
}
// HealthChat Play Store upload key. The keystore is not in the repo:
// CI decodes it from the RELEASE_KEYSTORE_BASE64 secret (see
// .github/workflows/healthchat-release-playstore.yml).
register("healthchat") {
keyAlias = System.getenv("RELEASE_KEY_ALIAS")
?: project.property("signing.healthchat.keyId") as? String?
keyPassword = System.getenv("RELEASE_KEY_PASSWORD")
?: project.property("signing.healthchat.keyPassword") as? String?
storeFile = file(System.getenv("RELEASE_STORE_FILE") ?: "./signature/release.keystore")
storePassword = System.getenv("RELEASE_STORE_PASSWORD")
?: project.property("signing.healthchat.storePassword") as? String?
}
}
@@ -122,29 +136,22 @@ android {
"login_redirect_scheme",
oAuthRedirectSchemeBase,
)
signingConfig = signingConfigs.getByName("debug")
// Sign with the HealthChat upload key when it is available (CI),
// fall back to the debug key for local release builds.
signingConfig = if (System.getenv("RELEASE_STORE_FILE") != null || file("./signature/release.keystore").exists()) {
signingConfigs.getByName("healthchat")
} else {
signingConfigs.getByName("debug")
}
optimization {
enable = true
keepRules {
files.add(File(projectDir, "common-proguard-rules.pro"))
files.add(getDefaultProguardFile("proguard-android-optimize.txt"))
// Depending on whether the app flavor is enterprise or not we want to use different proguard rules.
val flavorProguardFile = if (isEnterpriseBuild) {
// Custom rules for enterprise builds
File(projectDir, "enterprise-proguard-rules.pro")
} else {
// These default rules prevent the OSS app from being obfuscated
File(projectDir, "default-proguard-rules.pro")
}
if (flavorProguardFile.exists()) {
files.add(flavorProguardFile)
} else {
logger.warn("Proguard file ${flavorProguardFile.absolutePath} does not exist")
}
// Equivalent of adding `getDefaultProguardFile("proguard-android-optimize.txt")` (this is the default value).
includeDefault = true
}
// Our custom keep rules are registered as `keepRules` source folders in the `androidComponents` block below,
// as the former `keepRules.files` DSL is deprecated since AGP 9.
}
}
@@ -229,6 +236,30 @@ androidComponents {
)
onVariants { variant ->
// Register the R8 keep rules source folders for optimized build types (release, nightly).
// Replaces the deprecated `optimization.keepRules.files` DSL (AGP 9+).
if (variant.buildType != "debug") {
variant.sources.keepRules?.let { keepRules ->
// Common rules, always applied.
keepRules.addStaticSourceDirectory("proguard/common")
// Depending on whether the app flavor is enterprise or not we want to use different proguard rules.
val flavorProguardDir = if (isEnterpriseBuild) {
// Custom rules for enterprise builds
"../enterprise/proguard"
} else {
// Custom fules for FOSS builds
"proguard/foss"
}
if (File(projectDir, flavorProguardDir).exists()) {
keepRules.addStaticSourceDirectory(flavorProguardDir)
} else {
logger.warn("Proguard folder ${File(projectDir, flavorProguardDir).absolutePath} does not exist")
}
}
}
// Assigns a different version code for each output APK
// other than the universal APK.
variant.outputs.forEach { output ->
@@ -261,13 +292,12 @@ dependencies {
implementation(projects.appicon.enterprise)
} else {
implementation(projects.features.enterprise.implFoss)
implementation(projects.appicon.element)
implementation(projects.appicon.healthchat)
}
allFeaturesImpl(project)
implementation(projects.features.migration.api)
implementation(projects.appnav)
implementation(projects.appconfig)
implementation(projects.libraries.uiStrings)
implementation(projects.services.analytics.compose)
if (ModulesConfig.pushProvidersConfig.includeFirebase) {
@@ -277,22 +307,21 @@ dependencies {
implementation(projects.libraries.pushproviders.unifiedpush)
}
// Google Play Services fused location backend. Discovered at runtime via ServiceLoader by the
// location feature.
"gplayImplementation"(libs.maplibre.compose.location.runtime.gms)
implementation(libs.appyx.core)
implementation(libs.androidx.splash)
implementation(libs.androidx.core)
implementation(libs.androidx.corektx)
implementation(libs.androidx.lifecycle.runtime)
implementation(libs.androidx.lifecycle.process)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.startup)
implementation(libs.androidx.preference)
implementation(libs.coil)
implementation(platform(libs.network.okhttp.bom))
implementation(libs.network.okhttp.logging)
implementation(libs.serialization.json)
implementation(libs.matrix.emojibase.bindings)
testCommonDependencies(libs)
testImplementation(projects.libraries.matrix.test)
@@ -314,6 +343,7 @@ licensee {
allow("BSD-3-Clause")
allow("EPL-1.0")
allowUrl("https://opensource.org/license/bsd-3-clause")
allowUrl("https://opensource.org/license/bsd-2-clause")
allowUrl("https://opensource.org/licenses/MIT")
allowUrl("https://developer.android.com/studio/terms.html")
allowUrl("https://www.zetetic.net/sqlcipher/license/")
@@ -324,6 +354,9 @@ licensee {
ignoreDependencies("com.github.matrix-org", "matrix-analytics-events")
// Ignore dependency that are not third-party licenses to us.
ignoreDependencies(groupId = "io.element.android")
// The rustls-platform-verifier dependency does not provide a license file in their maven repo, but there are MIT and Apache licenses in
// the source code repo: see https://github.com/rustls/rustls-platform-verifier
ignoreDependencies(groupId = "org.rustls", artifactId = "rustls-platform-verifier")
}
fun Project.configureLicensesTasks(reportingExtension: ReportingExtension) {
-2
View File
@@ -1,2 +0,0 @@
# Don't obfuscate anything for non-enterprise builds
-dontobfuscate
@@ -0,0 +1,9 @@
# Don't obfuscate anything for non-enterprise builds
-dontobfuscate
# Strip PostHog's Firebase push token integration. We don't use it and it breaks F-Droid.
# Removing the contents of this method will mark the Firebase-related classes as unused,
# which will allow R8 to remove them from the final build.
-assumevalues class com.posthog.android.PostHogAndroidConfig {
boolean getCapturePushNotificationSubscriptions() return false;
}
+63
View File
@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2026 Novaloop AG
~
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
~ Please see LICENSE files in the repository root for full details.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!--
HealthChat does not want APK sideloading from chat. Upstream declares
REQUEST_INSTALL_PACKAGES so that tapping an .apk attachment in the media
viewer deep-links to "Install unknown apps" and hands the file to the
system installer (see AndroidLocalMediaActions.open) — a workflow aimed at
Element's nightly/F-Droid audience, and not one we want in a healthcare
messenger. Play Console also rejects releases carrying this permission
unless it is declared and justified on the listing.
Removing it is a configuration upstream supports: the media viewer checks
for the permission before using it and otherwise falls through to the
normal system open path, so .apk attachments still open, just without the
in-app install shortcut.
-->
<uses-permission
android:name="android.permission.REQUEST_INSTALL_PACKAGES"
tools:node="remove" />
<!--
HealthChat ships no location features, so it should not ask for location.
They are already unreachable at runtime: DefaultLocationService gates the
whole feature on BuildConfig.MAPTILER_API_KEY, and HealthChat sets no
MapTiler key (BuildTimeConfig.SERVICES_MAPTILER_APIKEY = null, and the
ELEMENT_ANDROID_MAPTILER_API_KEY fallback is unset in CI). The composer's
"Share location" action and the location viewer are both hidden as a
result, so LiveLocationSharingService can never start — only these
declarations survive, and FOREGROUND_SERVICE_LOCATION forces a foreground
service declaration on the Play listing for a feature we do not ship.
Removing the declarations changes nothing users can reach, and keeps the
location module in the dependency graph so upstream merges stay clean.
The service must be named fully qualified: the library declares it
relative to its own namespace (io.element.android.features.location.impl),
whereas a leading dot here would resolve against the app's namespace.
FOREGROUND_SERVICE itself stays — the call and push modules need it.
-->
<uses-permission
android:name="android.permission.FOREGROUND_SERVICE_LOCATION"
tools:node="remove" />
<uses-permission
android:name="android.permission.ACCESS_FINE_LOCATION"
tools:node="remove" />
<uses-permission
android:name="android.permission.ACCESS_COARSE_LOCATION"
tools:node="remove" />
<application>
<service
android:name="io.element.android.features.location.impl.live.service.LiveLocationSharingService"
tools:node="remove" />
</application>
</manifest>
+21 -28
View File
@@ -49,13 +49,14 @@
Using launchMode singleTask to avoid multiple instances of the Activity
when the app is already open. This is important for incoming share (see
https://github.com/element-hq/element-x-android/issues/4074) and for opening
the application from a mobile.element.io link.
the application from a permalink.
-->
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden|uiMode"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|keyboardHidden|uiMode"
android:exported="true"
android:launchMode="singleTask"
android:supportsPictureInPicture="true"
android:theme="@style/Theme.ElementX.Splash"
android:windowSoftInputMode="adjustResize">
<intent-filter>
@@ -69,7 +70,7 @@
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="open"
android:scheme="elementx" />
android:scheme="healthchat" />
</intent-filter>
<!--
OAuth redirection
@@ -83,7 +84,9 @@
<data android:scheme="@string/login_redirect_scheme" />
</intent-filter>
<!--
Element web links
HealthChat web links
Note: hosts are listed one by one on purpose, a "*.healthchat.ch" wildcard would
also intercept the homeserver and authentication domains.
-->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
@@ -92,28 +95,12 @@
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
<!-- Note: we can't use "*.element.io" here because it'll intercept the "mas.element.io" domain too. -->
<!-- Matching asset file: https://app.element.io/.well-known/assetlinks.json -->
<data android:host="app.element.io" />
<!-- Matching asset file: https://develop.element.io/.well-known/assetlinks.json -->
<data android:host="develop.element.io" />
<!-- Matching asset file: https://staging.element.io/.well-known/assetlinks.json -->
<data android:host="staging.element.io" />
</intent-filter>
<!--
Element mobile links
Example: https://mobile.element.io/element/?account_provider=example.org&login_hint=mxid:@alice:example.org
-->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
<!-- Matching asset file: https://mobile.element.io/.well-known/assetlinks.json -->
<data android:host="mobile.element.io" />
<data android:path="/element/" />
<!-- HealthChat web client permalinks, example: https://app.healthchat.ch/#/room/#room:healthchat.ch -->
<!-- Matching asset file: https://app.healthchat.ch/.well-known/assetlinks.json -->
<data android:host="app.healthchat.ch" />
<!-- HealthChat matrix.to deployment, example: https://to.healthchat.ch/#/#room:healthchat.ch -->
<!-- Matching asset file: https://to.healthchat.ch/.well-known/assetlinks.json -->
<data android:host="to.healthchat.ch" />
</intent-filter>
<!--
matrix: links
@@ -127,7 +114,7 @@
<data android:scheme="matrix" />
</intent-filter>
<!--
links from matrix.to website
links from the HealthChat matrix.to website, example: healthchat://room/%23room%3Ahealthchat.ch
-->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@@ -135,10 +122,16 @@
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="element" />
<data android:scheme="healthchat" />
<data android:host="user" />
<data android:host="room" />
</intent-filter>
<!--
Conversation shortcuts offered in the Direct Share row of the system share sheet.
-->
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" />
<!-- Incoming share simple -->
<intent-filter>
<action android:name="android.intent.action.SEND" />
@@ -9,14 +9,19 @@
package io.element.android.x
import android.app.Application
import android.os.StrictMode
import androidx.compose.material3.ComposeMaterial3Flags.isAnchoredDraggableComponentsStrictOffsetCheckEnabled
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.startup.AppInitializer
import androidx.work.Configuration
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.createGraphFactory
import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.di.DependencyInjectionGraphOwner
import io.element.android.libraries.matrix.api.SdkMetadata
import io.element.android.libraries.workmanager.api.di.MetroWorkerFactory
import io.element.android.x.di.AppGraph
import io.element.android.x.di.ApplicationBindings
import io.element.android.x.info.logApplicationInfo
import io.element.android.x.initializer.CacheCleanerInitializer
import io.element.android.x.initializer.CrashInitializer
@@ -29,19 +34,37 @@ class ElementXApplication : Application(), DependencyInjectionGraphOwner, Config
.setWorkerFactory(MetroWorkerFactory(graph.workerProviders))
.build()
@Inject lateinit var sdkMetadata: SdkMetadata
@OptIn(ExperimentalMaterial3Api::class)
override fun onCreate() {
super.onCreate()
enableStrictMode()
AppInitializer.getInstance(this).apply {
initializeComponent(CrashInitializer::class.java)
initializeComponent(PlatformInitializer::class.java)
initializeComponent(CacheCleanerInitializer::class.java)
}
logApplicationInfo(this)
bindings<ApplicationBindings>().inject(this)
logApplicationInfo(this, sdkMetadata.sdkGitSha)
// Disable the strict offset check for anchored draggable components, as it can cause issues with bottom sheets.
// Remove once https://issuetracker.google.com/issues/477038695 is fixed.
isAnchoredDraggableComponentsStrictOffsetCheckEnabled = false
}
private fun enableStrictMode() {
// Workaround to avoid NetworkOnMainThreadException when using the network on the main thread.
// This is a temporary solution to rustls doing the certificate verification using blocking network requests.
// It should be removed once the underlying issue is fixed.
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectNetwork()
.penaltyLog()
.build()
)
}
}
@@ -61,6 +61,26 @@ class MainActivity : NodeActivity() {
setContent {
MainContent(appBindings)
}
val activity = this
appBindings.appStartupHooks().forEach {
lifecycleScope.launch {
it.onAppStartup(activity)
}
}
}
/**
* The pre-Android 12 way into picture-in-picture for a native call.
*
* Everything else about it is installed where the call is drawn, which knows whose call it is.
* This cannot be: an Activity override has no session in scope. From Android 12 the system is told
* up front that this Activity would like to shrink, and the call handles it without ever coming
* through here.
*/
override fun onUserLeaveHint() {
super.onUserLeaveHint()
appBindings.nativeCallPip().onUserLeaveHint(this)
}
@Composable
@@ -18,7 +18,7 @@ import dev.zacsweers.metro.ContributesTo
import dev.zacsweers.metro.Provides
import dev.zacsweers.metro.SingleIn
import io.element.android.appconfig.ApplicationConfig
import io.element.android.features.enterprise.api.EnterpriseService
import io.element.android.features.enterprise.api.IsEnterpriseBuild
import io.element.android.libraries.androidutils.system.getVersionCodeFromManifest
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.meta.BuildMeta
@@ -28,8 +28,6 @@ import io.element.android.libraries.di.BaseDirectory
import io.element.android.libraries.di.CacheDirectory
import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.libraries.di.annotations.ApplicationContext
import io.element.android.libraries.recentemojis.api.EmojibaseProvider
import io.element.android.libraries.recentemojis.impl.DefaultEmojibaseProvider
import io.element.android.x.BuildConfig
import io.element.android.x.R
import kotlinx.coroutines.CoroutineName
@@ -40,7 +38,7 @@ import java.io.File
@BindingContainer
@ContributesTo(AppScope::class)
object AppModule {
object AppBindingContainer {
@Provides
@BaseDirectory
fun providesBaseDirectory(@ApplicationContext context: Context): File {
@@ -76,17 +74,18 @@ object AppModule {
fun providesBuildMeta(
@ApplicationContext context: Context,
buildType: BuildType,
enterpriseService: EnterpriseService,
isEnterpriseBuild: IsEnterpriseBuild,
): BuildMeta {
val applicationName = ApplicationConfig.APPLICATION_NAME.takeIf { it.isNotEmpty() } ?: context.getString(R.string.app_name)
val isEnterprise = isEnterpriseBuild()
return BuildMeta(
isDebuggable = BuildConfig.DEBUG,
buildType = buildType,
applicationName = applicationName,
productionApplicationName = if (enterpriseService.isEnterpriseBuild) applicationName else ApplicationConfig.PRODUCTION_APPLICATION_NAME,
desktopApplicationName = if (enterpriseService.isEnterpriseBuild) applicationName else ApplicationConfig.DESKTOP_APPLICATION_NAME,
productionApplicationName = if (isEnterprise) applicationName else ApplicationConfig.PRODUCTION_APPLICATION_NAME,
desktopApplicationName = if (isEnterprise) applicationName else ApplicationConfig.DESKTOP_APPLICATION_NAME,
applicationId = BuildConfig.APPLICATION_ID,
isEnterpriseBuild = enterpriseService.isEnterpriseBuild,
isEnterpriseBuild = isEnterprise,
// TODO EAx Config.LOW_PRIVACY_LOG_ENABLE,
lowPrivacyLoggingEnabled = false,
versionName = BuildConfig.VERSION_NAME,
@@ -112,13 +111,7 @@ object AppModule {
@Provides
@SingleIn(AppScope::class)
fun provideSnackbarDispatcher(): SnackbarDispatcher {
fun providesSnackbarDispatcher(): SnackbarDispatcher {
return SnackbarDispatcher()
}
@Provides
@SingleIn(AppScope::class)
fun providesEmojibaseProvider(@ApplicationContext context: Context): EmojibaseProvider {
return DefaultEmojibaseProvider(context)
}
}
@@ -10,10 +10,13 @@ package io.element.android.x.di
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesTo
import io.element.android.features.api.MigrationEntryPoint
import dev.zacsweers.metro.Multibinds
import io.element.android.features.callnative.api.NativeCallPip
import io.element.android.features.enterprise.api.AppStartupHook
import io.element.android.features.enterprise.api.EnterpriseService
import io.element.android.features.lockscreen.api.LockScreenEntryPoint
import io.element.android.features.lockscreen.api.LockScreenService
import io.element.android.features.migration.api.MigrationEntryPoint
import io.element.android.features.rageshake.api.reporter.BugReporter
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
@@ -50,5 +53,15 @@ interface AppBindings {
fun buildMeta(): BuildMeta
/**
* For picture-in-picture. Only an Activity is told it is being left, and only the call knows
* whether that is worth shrinking for, so `MainActivity` needs this much of the native call and
* no more.
*/
fun nativeCallPip(): NativeCallPip
fun sentrySdkDsn(): SentrySdkDsn?
@Multibinds(allowEmpty = true)
fun appStartupHooks(): Set<AppStartupHook>
}
@@ -0,0 +1,17 @@
/*
* Copyright (c) 2026 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.x.di
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesTo
import io.element.android.x.ElementXApplication
@ContributesTo(AppScope::class)
interface ApplicationBindings {
fun inject(application: ElementXApplication)
}
@@ -10,32 +10,22 @@ package io.element.android.x.info
import android.content.Context
import io.element.android.libraries.androidutils.system.getVersionCodeFromManifest
import io.element.android.libraries.core.log.logger.wrapInBox
import io.element.android.x.BuildConfig
import timber.log.Timber
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
fun logApplicationInfo(context: Context) {
val appVersion = buildString {
append(BuildConfig.VERSION_NAME)
append(" (")
append(context.getVersionCodeFromManifest())
append(") - ")
append(BuildConfig.BUILD_TYPE)
append(" / ")
append(BuildConfig.FLAVOR)
}
// TODO Get SDK version somehow
val sdkVersion = "SDK VERSION (TODO)"
fun logApplicationInfo(context: Context, sdkGitSha: String) {
val appVersion = "${BuildConfig.VERSION_NAME} (${context.getVersionCodeFromManifest()}) - ${BuildConfig.BUILD_TYPE} / ${BuildConfig.FLAVOR}"
val date = SimpleDateFormat("MM-dd HH:mm:ss.SSSZ", Locale.US).format(Date())
Timber.d("----------------------------------------------------------------")
Timber.d("----------------------------------------------------------------")
Timber.d(" Application version: $appVersion")
Timber.d(" Git SHA: ${BuildConfig.GIT_REVISION}")
Timber.d(" SDK version: $sdkVersion")
Timber.d(" Local time: $date")
Timber.d("----------------------------------------------------------------")
Timber.d("----------------------------------------------------------------\n\n\n\n")
listOf(
" Application version: $appVersion",
" Element X: https://github.com/element-hq/element-x-android/commit/${BuildConfig.GIT_REVISION}",
" SDK : https://github.com/matrix-org/matrix-rust-sdk/commit/$sdkGitSha",
" Local time: $date",
)
.wrapInBox(minBoxInsideWidth = 80)
.forEach { Timber.d(it) }
}
@@ -8,11 +8,14 @@
package io.element.android.x.initializer
import android.annotation.SuppressLint
import android.content.Context
import android.system.Os
import android.util.Log
import androidx.startup.Initializer
import io.element.android.features.rageshake.api.logs.createWriteToFilesConfiguration
import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.matrix.api.tracing.TracingConfiguration
import io.element.android.x.di.AppBindings
@@ -24,6 +27,15 @@ private const val ELEMENT_X_TARGET = "elementx"
class PlatformInitializer : Initializer<Unit> {
override fun create(context: Context) {
// Init the SDK with the Android Context, so it can be used with rustls and other Android specific features
runCatchingExceptions {
org.matrix.rustsdk.Android.init(context)
}.onFailure {
// We can't log this to Timber since the logs aren't initialized yet, so we log it to Logcat instead
@SuppressLint("LogNotTimber")
Log.e("PlatformInitializer", "Failed to init the SDK with an Android Context. The app probably won't work properly.", it)
}
val appBindings = context.bindings<AppBindings>()
val tracingService = appBindings.tracingService()
val platformService = appBindings.platformService()
@@ -41,6 +53,8 @@ class PlatformInitializer : Initializer<Unit> {
sdkSentryDsn = appBindings.sentrySdkDsn()?.value?.takeIf { it.isNotBlank() },
)
bugReporter.setCurrentTracingLogLevel(logLevel.name)
// Init the tracing service with the configuration so we have logging enabled as soon as possible
platformService.init(tracingConfiguration)
// Also set env variable for rust back trace
Os.setenv("RUST_BACKTRACE", "1", true)
+1
View File
@@ -1,5 +1,6 @@
<!-- File generated by importSupportedLocalesFromLocalazy.py, do not edit -->
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
<locale android:name="ar"/>
<locale android:name="be"/>
<locale android:name="bg"/>
<locale android:name="ca"/>
@@ -30,5 +30,384 @@
<domain includeSubdomains="true">home</domain>
<domain includeSubdomains="true">lan</domain>
<domain includeSubdomains="true">localdomain</domain>
<!-- Domains below are imported from rustls-platform-verifier 0.2.0 by tools/dependencies/update_rustls_platform_verifier.py -->
<!-- They are needed to fetch the certificate revocation lists over cleartext -->
<domain includeSubdomains="true">abitabdv.crl.certum.pl</domain>
<domain includeSubdomains="true">abitabev.crl.certum.pl</domain>
<domain includeSubdomains="true">abitabov.crl.certum.pl</domain>
<domain includeSubdomains="true">alpiro.crl.sectigo.com</domain>
<domain includeSubdomains="true">anticdv.crl.certum.pl</domain>
<domain includeSubdomains="true">antrustovsslg2r30ca.crl.certum.pl</domain>
<domain includeSubdomains="true">autontrustprodvtlsg2r31ca.crl.certum.pl</domain>
<domain includeSubdomains="true">bitcert.crl.sectigo.com</domain>
<domain includeSubdomains="true">btdvtlsr35g2ca.crl.certum.pl</domain>
<domain includeSubdomains="true">c.cf-b.ssl.com</domain>
<domain includeSubdomains="true">c.cf-i.ssl.com</domain>
<domain includeSubdomains="true">c.pki.goog</domain>
<domain includeSubdomains="true">ca1.agid.gov.it</domain>
<domain includeSubdomains="true">cdn.d-trust-cloudcrl.net</domain>
<domain includeSubdomains="true">cdp.disig.sk</domain>
<domain includeSubdomains="true">cdp.elektronicznypodpis.pl</domain>
<domain includeSubdomains="true">cdp.geotrust.com</domain>
<domain includeSubdomains="true">cdp.rapidssl.com</domain>
<domain includeSubdomains="true">cdp.thawte.com</domain>
<domain includeSubdomains="true">cdp1.disig.sk</domain>
<domain includeSubdomains="true">cdp1.pca.dfn.de</domain>
<domain includeSubdomains="true">cdpa.digitalcertvalidation.com</domain>
<domain includeSubdomains="true">cdpb.digitalcertvalidation.com</domain>
<domain includeSubdomains="true">cdpc.digitalcertvalidation.com</domain>
<domain includeSubdomains="true">cdpd.digitalcertvalidation.com</domain>
<domain includeSubdomains="true">cdpe.digitalcertvalidation.com</domain>
<domain includeSubdomains="true">cdpf.digitalcertvalidation.com</domain>
<domain includeSubdomains="true">cdpg.digitalcertvalidation.com</domain>
<domain includeSubdomains="true">cdph.digitalcertvalidation.com</domain>
<domain includeSubdomains="true">cdpi.digitalcertvalidation.com</domain>
<domain includeSubdomains="true">certcloud.crl.trust-provider.com</domain>
<domain includeSubdomains="true">certdata.crl.sectigo.com</domain>
<domain includeSubdomains="true">certera.crl.sectigo.com</domain>
<domain includeSubdomains="true">certificates.godaddy.com</domain>
<domain includeSubdomains="true">certum-crl.wt.trustasia.com</domain>
<domain includeSubdomains="true">certum.crl.sheca.com</domain>
<domain includeSubdomains="true">certumdvtlsg2e39ca.crl.certum.pl</domain>
<domain includeSubdomains="true">certumdvtlsg2r39ca.crl.certum.pl</domain>
<domain includeSubdomains="true">certumevtlsg2e39ca.crl.certum.pl</domain>
<domain includeSubdomains="true">certumevtlsg2r39ca.crl.certum.pl</domain>
<domain includeSubdomains="true">certumovtlsg2e39ca.crl.certum.pl</domain>
<domain includeSubdomains="true">certumovtlsg2r39ca.crl.certum.pl</domain>
<domain includeSubdomains="true">cfcadveccca.crl.certum.pl</domain>
<domain includeSubdomains="true">cfcadvrsaca.crl.certum.pl</domain>
<domain includeSubdomains="true">cfcaeveccca.crl.certum.pl</domain>
<domain includeSubdomains="true">cfcaevrsaca.crl.certum.pl</domain>
<domain includeSubdomains="true">cfcaoveccca.crl.certum.pl</domain>
<domain includeSubdomains="true">cfcaovrsaca.crl.certum.pl</domain>
<domain includeSubdomains="true">cnssldvtlsg2r36ca.crl.certum.pl</domain>
<domain includeSubdomains="true">cnsslovtlsg2r36ca.crl.certum.pl</domain>
<domain includeSubdomains="true">crl-01.r1.ica.amazontrust.com</domain>
<domain includeSubdomains="true">crl-01.r1.ica.amznts.eu</domain>
<domain includeSubdomains="true">crl-c.emsign.com</domain>
<domain includeSubdomains="true">crl-pro.litessl.com</domain>
<domain includeSubdomains="true">crl.anf.es</domain>
<domain includeSubdomains="true">crl.apple.com</domain>
<domain includeSubdomains="true">crl.businessid.telesec.de</domain>
<domain includeSubdomains="true">crl.buypass.no</domain>
<domain includeSubdomains="true">crl.catrust.oemssl.cn</domain>
<domain includeSubdomains="true">crl.certigna.fr</domain>
<domain includeSubdomains="true">crl.certsign.ro</domain>
<domain includeSubdomains="true">crl.certum.pl</domain>
<domain includeSubdomains="true">crl.cfca.com.cn</domain>
<domain includeSubdomains="true">crl.cntrus.oemssl.cn</domain>
<domain includeSubdomains="true">crl.cnwebtrust.oemssl.cn</domain>
<domain includeSubdomains="true">crl.comodoca.com</domain>
<domain includeSubdomains="true">crl.crlocsp.cn</domain>
<domain includeSubdomains="true">crl.cybertrust.ne.jp</domain>
<domain includeSubdomains="true">crl.d-trust.net</domain>
<domain includeSubdomains="true">crl.dhimyotis.com</domain>
<domain includeSubdomains="true">crl.digicert.cn</domain>
<domain includeSubdomains="true">crl.digicert.eu</domain>
<domain includeSubdomains="true">crl.e-szigno.hu</domain>
<domain includeSubdomains="true">crl.e2m01.amazontrust.com</domain>
<domain includeSubdomains="true">crl.e2m01.eu.amazontrust.com</domain>
<domain includeSubdomains="true">crl.e2m02.amazontrust.com</domain>
<domain includeSubdomains="true">crl.e2m03.amazontrust.com</domain>
<domain includeSubdomains="true">crl.e2m04.amazontrust.com</domain>
<domain includeSubdomains="true">crl.e2s14.amazontrust.com</domain>
<domain includeSubdomains="true">crl.e2s15.amazontrust.com</domain>
<domain includeSubdomains="true">crl.e2s16.amazontrust.com</domain>
<domain includeSubdomains="true">crl.e2s17.amazontrust.com</domain>
<domain includeSubdomains="true">crl.e2s18.amazontrust.com</domain>
<domain includeSubdomains="true">crl.e2s19.amazontrust.com</domain>
<domain includeSubdomains="true">crl.e2s20.amazontrust.com</domain>
<domain includeSubdomains="true">crl.e2s21.amazontrust.com</domain>
<domain includeSubdomains="true">crl.e3m01.amazontrust.com</domain>
<domain includeSubdomains="true">crl.e3m01.eu.amazontrust.com</domain>
<domain includeSubdomains="true">crl.e3m02.amazontrust.com</domain>
<domain includeSubdomains="true">crl.e3m03.amazontrust.com</domain>
<domain includeSubdomains="true">crl.e3m04.amazontrust.com</domain>
<domain includeSubdomains="true">crl.e3s22.amazontrust.com</domain>
<domain includeSubdomains="true">crl.e3s23.amazontrust.com</domain>
<domain includeSubdomains="true">crl.e3s24.amazontrust.com</domain>
<domain includeSubdomains="true">crl.e3s25.amazontrust.com</domain>
<domain includeSubdomains="true">crl.e3s26.amazontrust.com</domain>
<domain includeSubdomains="true">crl.e3s27.amazontrust.com</domain>
<domain includeSubdomains="true">crl.e3s28.amazontrust.com</domain>
<domain includeSubdomains="true">crl.e3s29.amazontrust.com</domain>
<domain includeSubdomains="true">crl.emsign.com</domain>
<domain includeSubdomains="true">crl.ensuredca.com</domain>
<domain includeSubdomains="true">crl.firmaprofesional.com</domain>
<domain includeSubdomains="true">crl.gdca.com.cn</domain>
<domain includeSubdomains="true">crl.global.sheca.com</domain>
<domain includeSubdomains="true">crl.globalsign.com</domain>
<domain includeSubdomains="true">crl.globalsign.net</domain>
<domain includeSubdomains="true">crl.godaddy.com</domain>
<domain includeSubdomains="true">crl.harica.gr</domain>
<domain includeSubdomains="true">crl.izenpe.com</domain>
<domain includeSubdomains="true">crl.litessl.com</domain>
<domain includeSubdomains="true">crl.netsolssl.com</domain>
<domain includeSubdomains="true">crl.oem.trustca.net</domain>
<domain includeSubdomains="true">crl.pcsp.telesec.de</domain>
<domain includeSubdomains="true">crl.pki.goog</domain>
<domain includeSubdomains="true">crl.quovadisglobal.com</domain>
<domain includeSubdomains="true">crl.r1001.amazontrust.com</domain>
<domain includeSubdomains="true">crl.r2001.amazontrust.com</domain>
<domain includeSubdomains="true">crl.r2m01.amazontrust.com</domain>
<domain includeSubdomains="true">crl.r2m01.eu.amazontrust.com</domain>
<domain includeSubdomains="true">crl.r2m02.amazontrust.com</domain>
<domain includeSubdomains="true">crl.r2m03.amazontrust.com</domain>
<domain includeSubdomains="true">crl.r2m04.amazontrust.com</domain>
<domain includeSubdomains="true">crl.r2s18.amazontrust.com</domain>
<domain includeSubdomains="true">crl.r2s19.amazontrust.com</domain>
<domain includeSubdomains="true">crl.r2s20.amazontrust.com</domain>
<domain includeSubdomains="true">crl.r2s21.amazontrust.com</domain>
<domain includeSubdomains="true">crl.r2s22.amazontrust.com</domain>
<domain includeSubdomains="true">crl.r2s23.amazontrust.com</domain>
<domain includeSubdomains="true">crl.r2s24.amazontrust.com</domain>
<domain includeSubdomains="true">crl.r2s25.amazontrust.com</domain>
<domain includeSubdomains="true">crl.r3001.amazontrust.com</domain>
<domain includeSubdomains="true">crl.r4001.amazontrust.com</domain>
<domain includeSubdomains="true">crl.r4m01.amazontrust.com</domain>
<domain includeSubdomains="true">crl.r4m02.amazontrust.com</domain>
<domain includeSubdomains="true">crl.r4m03.amazontrust.com</domain>
<domain includeSubdomains="true">crl.r4m04.amazontrust.com</domain>
<domain includeSubdomains="true">crl.root-e1.certainly.com</domain>
<domain includeSubdomains="true">crl.root-r1.certainly.com</domain>
<domain includeSubdomains="true">crl.rootca1.amazontrust.com</domain>
<domain includeSubdomains="true">crl.rootca2.amazontrust.com</domain>
<domain includeSubdomains="true">crl.rootca3.amazontrust.com</domain>
<domain includeSubdomains="true">crl.rootca4.amazontrust.com</domain>
<domain includeSubdomains="true">crl.rztrust.oemssl.cn</domain>
<domain includeSubdomains="true">crl.sbca.telesec.de</domain>
<domain includeSubdomains="true">crl.sectigo.com</domain>
<domain includeSubdomains="true">crl.sectigochina.com</domain>
<domain includeSubdomains="true">crl.serverid.telesec.de</domain>
<domain includeSubdomains="true">crl.starfieldtech.com</domain>
<domain includeSubdomains="true">crl.swisssign.ch</domain>
<domain includeSubdomains="true">crl.trust-provider.cn</domain>
<domain includeSubdomains="true">crl.trust-provider.com</domain>
<domain includeSubdomains="true">crl.tuntrust.tn</domain>
<domain includeSubdomains="true">crl.usertrust.com</domain>
<domain includeSubdomains="true">crl.wt.trustasia.com</domain>
<domain includeSubdomains="true">crl.xinchacha.oemssl.cn</domain>
<domain includeSubdomains="true">crl.zw.oemssl.cn</domain>
<domain includeSubdomains="true">crl05.actalis.it</domain>
<domain includeSubdomains="true">crl06.actalis.it</domain>
<domain includeSubdomains="true">crl09.actalis.it</domain>
<domain includeSubdomains="true">crl1.ecert.gov.hk</domain>
<domain includeSubdomains="true">crl13.actalis.it</domain>
<domain includeSubdomains="true">crl14.actalis.it</domain>
<domain includeSubdomains="true">crl15.actalis.it</domain>
<domain includeSubdomains="true">crl2.microsoft.com</domain>
<domain includeSubdomains="true">crl3.digicert.com</domain>
<domain includeSubdomains="true">crl4.digicert.com</domain>
<domain includeSubdomains="true">crls.certainly.com</domain>
<domain includeSubdomains="true">crls.ssl.com</domain>
<domain includeSubdomains="true">cyberfolks2.crl.certum.pl</domain>
<domain includeSubdomains="true">depo.kamusm.gov.tr</domain>
<domain includeSubdomains="true">digitaltrust.crl.sectigo.com</domain>
<domain includeSubdomains="true">dnencrypt.crl.sectigo.com</domain>
<domain includeSubdomains="true">dvtlsca2025-crl.e-szigno.hu</domain>
<domain includeSubdomains="true">dvtlsca2026-crl.e-szigno.hu</domain>
<domain includeSubdomains="true">e5.c.lencr.org</domain>
<domain includeSubdomains="true">e6.c.lencr.org</domain>
<domain includeSubdomains="true">e7.c.lencr.org</domain>
<domain includeSubdomains="true">e8.c.lencr.org</domain>
<domain includeSubdomains="true">ec2ca2017-crl1.e-szigno.hu</domain>
<domain includeSubdomains="true">ec2ca2017-crl2.e-szigno.hu</domain>
<domain includeSubdomains="true">ec2ca2017-crl3.e-szigno.hu</domain>
<domain includeSubdomains="true">ec2sslca2017-crl1.e-szigno.hu</domain>
<domain includeSubdomains="true">ec2sslca2017-crl2.e-szigno.hu</domain>
<domain includeSubdomains="true">ec2sslca2017-crl3.e-szigno.hu</domain>
<domain includeSubdomains="true">ec3ca2017-crl1.e-szigno.hu</domain>
<domain includeSubdomains="true">ec3ca2017-crl2.e-szigno.hu</domain>
<domain includeSubdomains="true">ec3ca2017-crl3.e-szigno.hu</domain>
<domain includeSubdomains="true">ec3sslca2017-crl1.e-szigno.hu</domain>
<domain includeSubdomains="true">ec3sslca2017-crl2.e-szigno.hu</domain>
<domain includeSubdomains="true">ec3sslca2017-crl3.e-szigno.hu</domain>
<domain includeSubdomains="true">eca.hinet.net</domain>
<domain includeSubdomains="true">edvtlsca2023-crl.e-szigno.hu</domain>
<domain includeSubdomains="true">edvtlsca2025-crl.e-szigno.hu</domain>
<domain includeSubdomains="true">edvtlsca2026-crl.e-szigno.hu</domain>
<domain includeSubdomains="true">emudhra.crl.sectigo.com</domain>
<domain includeSubdomains="true">eosslca2017-crl1.e-szigno.hu</domain>
<domain includeSubdomains="true">eosslca2017-crl2.e-szigno.hu</domain>
<domain includeSubdomains="true">eosslca2017-crl3.e-szigno.hu</domain>
<domain includeSubdomains="true">eovtlsca2023-crl.e-szigno.hu</domain>
<domain includeSubdomains="true">eovtlsca2025-crl.e-szigno.hu</domain>
<domain includeSubdomains="true">eovtlsca2026-crl.e-szigno.hu</domain>
<domain includeSubdomains="true">eqca2017-crl1.e-szigno.hu</domain>
<domain includeSubdomains="true">eqca2017-crl2.e-szigno.hu</domain>
<domain includeSubdomains="true">eqca2017-crl3.e-szigno.hu</domain>
<domain includeSubdomains="true">eqcpca2017-crl1.e-szigno.hu</domain>
<domain includeSubdomains="true">eqcpca2017-crl2.e-szigno.hu</domain>
<domain includeSubdomains="true">eqcpca2017-crl3.e-szigno.hu</domain>
<domain includeSubdomains="true">eqoca2017-crl1.e-szigno.hu</domain>
<domain includeSubdomains="true">eqoca2017-crl2.e-szigno.hu</domain>
<domain includeSubdomains="true">eqoca2017-crl3.e-szigno.hu</domain>
<domain includeSubdomains="true">eqtlsca2018-crl1.e-szigno.hu</domain>
<domain includeSubdomains="true">eqtlsca2018-crl2.e-szigno.hu</domain>
<domain includeSubdomains="true">eqtlsca2018-crl3.e-szigno.hu</domain>
<domain includeSubdomains="true">eqtlsca2023-crl.e-szigno.hu</domain>
<domain includeSubdomains="true">eqtlsca2025-crl.e-szigno.hu</domain>
<domain includeSubdomains="true">eqtlsca2026-crl.e-szigno.hu</domain>
<domain includeSubdomains="true">esafer.crl.sectigo.com</domain>
<domain includeSubdomains="true">eue2m1.crl.root.amznts.eu</domain>
<domain includeSubdomains="true">eue3m1.crl.root.amznts.eu</domain>
<domain includeSubdomains="true">eur2m1.crl.root.amznts.eu</domain>
<domain includeSubdomains="true">eveccca.crl.certum.pl</domain>
<domain includeSubdomains="true">evrsaca.crl.certum.pl</domain>
<domain includeSubdomains="true">gdcadv.crl.certum.pl</domain>
<domain includeSubdomains="true">gdcaev.crl.certum.pl</domain>
<domain includeSubdomains="true">gdcaov.crl.certum.pl</domain>
<domain includeSubdomains="true">gdcatrustauthdvtlsg3r31ca.crl.certum.pl</domain>
<domain includeSubdomains="true">gdcatrustauthevtlsg3r31ca.crl.certum.pl</domain>
<domain includeSubdomains="true">gdcatrustauthovtlsg3r31ca.crl.certum.pl</domain>
<domain includeSubdomains="true">geant.crl.sectigo.com</domain>
<domain includeSubdomains="true">genious.crl.sectigo.com</domain>
<domain includeSubdomains="true">geossldvtlsr34g2ca.crl.certum.pl</domain>
<domain includeSubdomains="true">geosslevtlsr34g2ca.crl.certum.pl</domain>
<domain includeSubdomains="true">geosslovtlsr34g2ca.crl.certum.pl</domain>
<domain includeSubdomains="true">globaltrust.crl.sectigo.com</domain>
<domain includeSubdomains="true">globessl.crl.sectigo.com</domain>
<domain includeSubdomains="true">gogetssldv.crl.certum.pl</domain>
<domain includeSubdomains="true">gogetsslev.crl.certum.pl</domain>
<domain includeSubdomains="true">gogetsslov.crl.certum.pl</domain>
<domain includeSubdomains="true">homepldvtlsg2r35ca.crl.certum.pl</domain>
<domain includeSubdomains="true">homepldvtlsg2r36ca.crl.certum.pl</domain>
<domain includeSubdomains="true">homeplovtlsg2r35ca.crl.certum.pl</domain>
<domain includeSubdomains="true">homeplovtlsg2r36ca.crl.certum.pl</domain>
<domain includeSubdomains="true">httpcrl.trust.telia.com</domain>
<domain includeSubdomains="true">ica.navercloudtrust.com</domain>
<domain includeSubdomains="true">ica.navercorp.com</domain>
<domain includeSubdomains="true">issauth.crl.sectigo.com</domain>
<domain includeSubdomains="true">itrusdv2.crl.certum.pl</domain>
<domain includeSubdomains="true">itrusdvtlsr35g2ca.crl.certum.pl</domain>
<domain includeSubdomains="true">itrusov2.crl.certum.pl</domain>
<domain includeSubdomains="true">itrusovtlsr35g2ca.crl.certum.pl</domain>
<domain includeSubdomains="true">itso.crl.sectigo.com</domain>
<domain includeSubdomains="true">joyssldvtlsg2r33ca.crl.certum.pl</domain>
<domain includeSubdomains="true">joysslovtlsg2r33ca.crl.certum.pl</domain>
<domain includeSubdomains="true">kingnettechdv.crl.certum.pl</domain>
<domain includeSubdomains="true">kingnettechev.crl.certum.pl</domain>
<domain includeSubdomains="true">kingnettechov.crl.certum.pl</domain>
<domain includeSubdomains="true">kr.crl.digicert.com</domain>
<domain includeSubdomains="true">lh2.crl.certum.pl</domain>
<domain includeSubdomains="true">namecheap.crl.sectigo.com</domain>
<domain includeSubdomains="true">nazwassldvtlsg2e29ca.crl.certum.pl</domain>
<domain includeSubdomains="true">nazwassldvtlsg2r29ca.crl.certum.pl</domain>
<domain includeSubdomains="true">netartcadv2.crl.certum.pl</domain>
<domain includeSubdomains="true">netartssldvtlsg2e31ca.crl.certum.pl</domain>
<domain includeSubdomains="true">nijimo.crl.sectigo.com</domain>
<domain includeSubdomains="true">nyalabscadv.crl.certum.pl</domain>
<domain includeSubdomains="true">nyatworkdv.crl.certum.pl</domain>
<domain includeSubdomains="true">omitsecurity.crl.sectigo.com</domain>
<domain includeSubdomains="true">onesign.crl.sectigo.com</domain>
<domain includeSubdomains="true">osslca2016-crl1.e-szigno.hu</domain>
<domain includeSubdomains="true">osslca2016-crl2.e-szigno.hu</domain>
<domain includeSubdomains="true">osslca2016-crl3.e-szigno.hu</domain>
<domain includeSubdomains="true">ovtlsca2025-crl.e-szigno.hu</domain>
<domain includeSubdomains="true">ovtlsca2026-crl.e-szigno.hu</domain>
<domain includeSubdomains="true">pki-crl.atos.net</domain>
<domain includeSubdomains="true">pki.telesec.de</domain>
<domain includeSubdomains="true">pkipro.certsign.ro</domain>
<domain includeSubdomains="true">positiwise.crl.sectigo.com</domain>
<domain includeSubdomains="true">psw.crl.sectigo.com</domain>
<domain includeSubdomains="true">public.wisekey.com</domain>
<domain includeSubdomains="true">qiduodv.crl.certum.pl</domain>
<domain includeSubdomains="true">qtlsca2018-crl1.e-szigno.hu</domain>
<domain includeSubdomains="true">qtlsca2018-crl2.e-szigno.hu</domain>
<domain includeSubdomains="true">qtlsca2018-crl3.e-szigno.hu</domain>
<domain includeSubdomains="true">qtlsca2026-crl.e-szigno.hu</domain>
<domain includeSubdomains="true">r10.c.lencr.org</domain>
<domain includeSubdomains="true">r11.c.lencr.org</domain>
<domain includeSubdomains="true">r12.c.lencr.org</domain>
<domain includeSubdomains="true">r13.c.lencr.org</domain>
<domain includeSubdomains="true">rca.navercloudtrust.com</domain>
<domain includeSubdomains="true">rca.navercorp.com</domain>
<domain includeSubdomains="true">repo.pubcert.jprs.jp</domain>
<domain includeSubdomains="true">repo1.secomtrust.net</domain>
<domain includeSubdomains="true">repository.secomtrust.net</domain>
<domain includeSubdomains="true">repository.tls.hinet.net</domain>
<domain includeSubdomains="true">rootca.twca.com.tw</domain>
<domain includeSubdomains="true">rootglobaldv.crl.certum.pl</domain>
<domain includeSubdomains="true">rootnetworksdv2.crl.certum.pl</domain>
<domain includeSubdomains="true">shenzhendv.crl.certum.pl</domain>
<domain includeSubdomains="true">shenzhenev.crl.certum.pl</domain>
<domain includeSubdomains="true">shenzhenov.crl.certum.pl</domain>
<domain includeSubdomains="true">shoperdvtlsg2r34ca.crl.certum.pl</domain>
<domain includeSubdomains="true">shuididv.crl.certum.pl</domain>
<domain includeSubdomains="true">shuidiev.crl.certum.pl</domain>
<domain includeSubdomains="true">shuidiov.crl.certum.pl</domain>
<domain includeSubdomains="true">soomadv.crl.certum.pl</domain>
<domain includeSubdomains="true">ssl2ca2016-crl1.e-szigno.hu</domain>
<domain includeSubdomains="true">ssl2ca2016-crl2.e-szigno.hu</domain>
<domain includeSubdomains="true">ssl2ca2016-crl3.e-szigno.hu</domain>
<domain includeSubdomains="true">sslca2014-crl1.e-szigno.hu</domain>
<domain includeSubdomains="true">sslca2014-crl2.e-szigno.hu</domain>
<domain includeSubdomains="true">sslca2014-crl3.e-szigno.hu</domain>
<domain includeSubdomains="true">sslcom.crl.certum.pl</domain>
<domain includeSubdomains="true">ssllimiteddv.crl.certum.pl</domain>
<domain includeSubdomains="true">sslserver.twca.com.tw</domain>
<domain includeSubdomains="true">subca.crl.certum.pl</domain>
<domain includeSubdomains="true">titrust.crl.sectigo.com</domain>
<domain includeSubdomains="true">tlcdvtlsr34g2ca.crl.certum.pl</domain>
<domain includeSubdomains="true">tlcevtlsr34g2ca.crl.certum.pl</domain>
<domain includeSubdomains="true">tlcovtlsr34g2ca.crl.certum.pl</domain>
<domain includeSubdomains="true">tlsrootca2025-crl.e-szigno.hu</domain>
<domain includeSubdomains="true">trustasia.crl.certum.pl</domain>
<domain includeSubdomains="true">trustasiadv.crl.certum.pl</domain>
<domain includeSubdomains="true">trustasiadvtlsr35g2ca.crl.certum.pl</domain>
<domain includeSubdomains="true">trustasiaev.crl.certum.pl</domain>
<domain includeSubdomains="true">trustasiaevtlse35g2ca.crl.certum.pl</domain>
<domain includeSubdomains="true">trustasiaevtlsr35g2ca.crl.certum.pl</domain>
<domain includeSubdomains="true">trustasiaov.crl.certum.pl</domain>
<domain includeSubdomains="true">trustasiaovtlse35g2ca.crl.certum.pl</domain>
<domain includeSubdomains="true">trustasiaovtlsr35g2ca.crl.certum.pl</domain>
<domain includeSubdomains="true">trustocean.crl.certum.pl</domain>
<domain includeSubdomains="true">tstlser20.crl.telesec.de</domain>
<domain includeSubdomains="true">tstlsrr23.crl.telesec.de</domain>
<domain includeSubdomains="true">unitedtrustov.crl.certum.pl</domain>
<domain includeSubdomains="true">validation.identrust.com</domain>
<domain includeSubdomains="true">verokey.crl.sectigo.com</domain>
<domain includeSubdomains="true">vtrusdvtlsg3r31ca.crl.certum.pl</domain>
<domain includeSubdomains="true">vtrusovtlsg3r31ca.crl.certum.pl</domain>
<domain includeSubdomains="true">webnic.crl.sectigo.com</domain>
<domain includeSubdomains="true">whitessldvtlsg2e36ca.crl.certum.pl</domain>
<domain includeSubdomains="true">wosign.crl.certum.pl</domain>
<domain includeSubdomains="true">wotrus-dvca.crl.certum.pl</domain>
<domain includeSubdomains="true">wotrus-evca.crl.certum.pl</domain>
<domain includeSubdomains="true">wotrus-ovca.crl.certum.pl</domain>
<domain includeSubdomains="true">wotrus.crl.sectigo.com</domain>
<domain includeSubdomains="true">wtca-cafiles.itrus.com.cn</domain>
<domain includeSubdomains="true">wtca-crl.itrus.com.cn</domain>
<domain includeSubdomains="true">www.accv.es</domain>
<domain includeSubdomains="true">www.cert.fnmt.es</domain>
<domain includeSubdomains="true">www.d-trust.net</domain>
<domain includeSubdomains="true">www.microsoft.com</domain>
<domain includeSubdomains="true">x1.c.lencr.org</domain>
<domain includeSubdomains="true">x2.c.lencr.org</domain>
<domain includeSubdomains="true">xcctrustdvtlsg3r31ca.crl.certum.pl</domain>
<domain includeSubdomains="true">xcctrustevtlsg3r31ca.crl.certum.pl</domain>
<domain includeSubdomains="true">xcctrustovtlsg3r31ca.crl.certum.pl</domain>
<domain includeSubdomains="true">xinchacha2dv.crl.certum.pl</domain>
<domain includeSubdomains="true">xinchacha2ov.crl.certum.pl</domain>
<domain includeSubdomains="true">xinchachatrustdvtlsg2r34ca.crl.certum.pl</domain>
<domain includeSubdomains="true">xinchachatrustevtlsg2r34ca.crl.certum.pl</domain>
<domain includeSubdomains="true">xinchachatrustovtlsg2r34ca.crl.certum.pl</domain>
<domain includeSubdomains="true">xinnetdvtlsr34g2ca.crl.certum.pl</domain>
<domain includeSubdomains="true">xinnetevtlsr34g2ca.crl.certum.pl</domain>
<domain includeSubdomains="true">xinnetovtlsr34g2ca.crl.certum.pl</domain>
<domain includeSubdomains="true">ye.c.lencr.org</domain>
<domain includeSubdomains="true">ye1.c.lencr.org</domain>
<domain includeSubdomains="true">ye2.c.lencr.org</domain>
<domain includeSubdomains="true">yektadv.crl.certum.pl</domain>
<domain includeSubdomains="true">yektaov.crl.certum.pl</domain>
<domain includeSubdomains="true">yr.c.lencr.org</domain>
<domain includeSubdomains="true">yr1.c.lencr.org</domain>
<domain includeSubdomains="true">yr2.c.lencr.org</domain>
<domain includeSubdomains="true">zerossl.crl.sectigo.com</domain>
<domain includeSubdomains="true">ziwit.crl.sectigo.com</domain>
<domain includeSubdomains="true">zycatrustdvtlsg3r31ca.crl.certum.pl</domain>
<domain includeSubdomains="true">zycatrustevtlsg3r31ca.crl.certum.pl</domain>
<domain includeSubdomains="true">zycatrustovtlsg3r31ca.crl.certum.pl</domain>
</domain-config>
</network-security-config>
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config xmlns:tools="http://schemas.android.com/tools">
<!-- Ref: https://developer.android.com/training/articles/security-config.html -->
<!-- By default, do not allow clearText traffic -->
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<certificates src="system" />
<certificates
src="user"
tools:ignore="AcceptsUserCertificates" />
</trust-anchors>
</base-config>
<!-- Allow clearText traffic on some specified host -->
<domain-config cleartextTrafficPermitted="true">
<!-- Localhost -->
<domain includeSubdomains="true">localhost</domain>
<domain includeSubdomains="true">127.0.0.1</domain>
<!-- Localhost for Android emulator -->
<domain includeSubdomains="true">10.0.2.2</domain>
<!-- Onion services -->
<domain includeSubdomains="true">onion</domain>
<!-- Domains that are used for LANs -->
<!-- These are IANA recognized special use domain names, see https://www.iana.org/assignments/special-use-domain-names/special-use-domain-names.xhtml -->
<domain includeSubdomains="true">home.arpa</domain>
<domain includeSubdomains="true">local</domain> <!-- Note this has been reserved for use with mDNS -->
<domain includeSubdomains="true">test</domain>
<!-- These are observed in the wild either by convention or RFCs that have not been accepted, and are not currently TLDs -->
<domain includeSubdomains="true">home</domain>
<domain includeSubdomains="true">lan</domain>
<domain includeSubdomains="true">localdomain</domain>
</domain-config>
</network-security-config>
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) 2026 Element Creations Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
Please see LICENSE files in the repository root for full details.
-->
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<!--
Lets the system offer the conversation shortcuts published by
DefaultNotificationConversationService in the Direct Share row of the share sheet.
The category must match SHARE_TARGET_CATEGORY in that class.
-->
<share-target android:targetClass="io.element.android.x.MainActivity">
<data android:mimeType="*/*" />
<category android:name="io.element.android.category.SHARE_TARGET" />
</share-target>
</shortcuts>
+4 -11
View File
@@ -22,19 +22,13 @@ android {
defaultConfig {
buildConfigFieldStr(
name = "URL_POLICY",
value = if (isEnterpriseBuild) {
BuildTimeConfig.URL_POLICY ?: ""
} else {
"https://element.io/cookie-policy"
},
// HealthChat fork: a configured value wins for every build type, not only enterprise ones.
value = BuildTimeConfig.URL_POLICY ?: if (isEnterpriseBuild) "" else "https://element.io/cookie-policy",
)
buildConfigFieldStr(
name = "BUG_REPORT_URL",
value = if (isEnterpriseBuild) {
BuildTimeConfig.BUG_REPORT_URL ?: ""
} else {
"https://rageshakes.element.io/api/submit"
},
// HealthChat fork: a configured value wins for every build type, not only enterprise ones.
value = BuildTimeConfig.BUG_REPORT_URL ?: if (isEnterpriseBuild) "" else "https://rageshakes.element.io/api/submit",
)
buildConfigFieldStr(
name = "BUG_REPORT_APP_NAME",
@@ -48,7 +42,6 @@ android {
}
dependencies {
implementation(libs.coroutines.core)
implementation(libs.androidx.annotationjvm)
implementation(libs.androidx.corektx)
implementation(projects.libraries.matrix.api)
@@ -24,12 +24,12 @@ object ApplicationConfig {
* Cannot be empty.
* For Element, the value is "Element".
*/
const val PRODUCTION_APPLICATION_NAME: String = "Element"
const val PRODUCTION_APPLICATION_NAME: String = "HealthChat"
/**
* Used in the strings to reference the Element Desktop client, for instance Element Web.
* Cannot be empty.
* For Element, the value is "Element". We use the same name for desktop and mobile for now.
*/
const val DESKTOP_APPLICATION_NAME: String = "Element"
const val DESKTOP_APPLICATION_NAME: String = "HealthChat"
}
@@ -9,15 +9,8 @@
package io.element.android.appconfig
object AuthenticationConfig {
const val MATRIX_ORG_URL = "https://matrix.org"
/**
* URL with some docs that explain what's sliding sync and how to add it to your home server.
*/
const val SLIDING_SYNC_READ_MORE_URL = "https://github.com/matrix-org/sliding-sync/blob/main/docs/Landing.md"
/**
* Force a sliding sync proxy url, if not null, the proxy url in the .well-known file will be ignored.
*/
val SLIDING_SYNC_PROXY_URL: String? = null
}
@@ -9,9 +9,10 @@
package io.element.android.appconfig
object LearnMoreConfig {
const val ENCRYPTION_URL: String = "https://element.io/help#encryption"
const val DEVICE_VERIFICATION_URL: String = "https://element.io/help#encryption-device-verification"
const val SECURE_BACKUP_URL: String = "https://element.io/help#encryption5"
const val IDENTITY_CHANGE_URL: String = "https://element.io/help#encryption18"
const val HISTORY_VISIBLE_URL: String = "https://element.io/en/help#e2ee-history-sharing"
// HealthChat fork: every "learn more" link points at the HealthChat site instead of element.io/help.
const val ENCRYPTION_URL: String = "https://www.healthchat.ch"
const val DEVICE_VERIFICATION_URL: String = "https://www.healthchat.ch"
const val SECURE_BACKUP_URL: String = "https://www.healthchat.ch"
const val IDENTITY_CHANGE_URL: String = "https://www.healthchat.ch"
const val HISTORY_VISIBLE_URL: String = "https://www.healthchat.ch"
}
@@ -10,5 +10,19 @@ package io.element.android.appconfig
object MatrixConfiguration {
const val MATRIX_TO_PERMALINK_BASE_URL: String = "https://matrix.to/#/"
/**
* HealthChat's own matrix.to deployment (https://github.com/Novaloop-AG/healthchat-matrix.to).
* It uses the exact same URL format as matrix.to, so links can be translated by swapping the
* base URL. Used both to recognise incoming links and to build links shared outside of Matrix.
*/
const val HEALTHCHAT_TO_PERMALINK_BASE_URL: String = "https://to.healthchat.ch/#/"
/**
* Base URL of the HealthChat web client, which is what the healthchat:// custom scheme
* handed out by [HEALTHCHAT_TO_PERMALINK_BASE_URL] points to.
*/
const val HEALTHCHAT_WEB_BASE_URL: String = "https://app.healthchat.ch/#/"
val clientPermalinkBaseUrl: String? = null
}
@@ -1,16 +0,0 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.appconfig
object PushConfig {
/**
* Note: pusher_app_id cannot exceed 64 chars.
*/
const val PUSHER_APP_ID: String = "im.vector.app.android"
}
@@ -35,4 +35,11 @@ object RageshakeConfig {
* The maximum number of log lines a rageshake can contain.
*/
const val MAX_LOG_LINES_SIZE = 1_000_000
/**
* The maximum size of the description. The bug report server copies it verbatim into a GitHub issue body,
* which GitHub rejects above 65536 characters. The remaining budget is left to the other reported fields,
* mainly the list of the omitted log file names.
*/
const val MAX_DESCRIPTION_SIZE = 40 * 1024
}
+17
View File
@@ -0,0 +1,17 @@
/*
* Copyright (c) 2026 Novaloop AG
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
plugins {
id("io.element.android-compose-library")
}
android {
namespace = "io.element.android.appicon.healthchat"
buildTypes {
register("nightly")
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2026 Novaloop AG
~
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
~ Please see LICENSE files in the repository root for full details.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#12325C"
android:fillType="evenOdd"
android:pathData="m0,0h108v108h-108z" />
</vector>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2026 Novaloop AG
~
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
~ Please see LICENSE files in the repository root for full details.
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
<monochrome android:drawable="@mipmap/ic_launcher_monochrome" />
</adaptive-icon>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2026 Novaloop AG
~
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
~ Please see LICENSE files in the repository root for full details.
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
<monochrome android:drawable="@mipmap/ic_launcher_monochrome" />
</adaptive-icon>
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

+8
View File
@@ -49,6 +49,7 @@ dependencies {
implementation(libs.coil)
implementation(projects.features.announcement.api)
implementation(projects.features.callnative.api)
implementation(projects.features.ftue.api)
implementation(projects.features.linknewdevice.api)
implementation(projects.features.share.api)
@@ -60,16 +61,23 @@ dependencies {
testCommonDependencies(libs)
testImplementation(projects.features.login.test)
testImplementation(projects.features.share.test)
testImplementation(projects.libraries.accountselect.test)
testImplementation(projects.libraries.featureflag.test)
testImplementation(projects.libraries.matrix.test)
testImplementation(projects.libraries.matrixmedia.test)
testImplementation(projects.libraries.oauth.test)
testImplementation(projects.libraries.permissions.test)
testImplementation(projects.libraries.preferences.test)
testImplementation(projects.libraries.push.test)
testImplementation(projects.libraries.pushproviders.test)
testImplementation(projects.libraries.sessionStorage.test)
testImplementation(projects.features.announcement.test)
testImplementation(projects.features.forward.test)
testImplementation(projects.features.messages.test)
testImplementation(projects.features.networkmonitor.test)
testImplementation(projects.features.preferences.test)
testImplementation(projects.features.rageshake.test)
testImplementation(projects.features.signedout.test)
testImplementation(projects.services.apperror.test)
testImplementation(projects.services.appnavstate.test)
testImplementation(projects.services.analytics.test)
@@ -8,9 +8,15 @@
package io.element.android.appnav
import com.bumble.appyx.core.navigation.NavKey
import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.BackStackElement
import com.bumble.appyx.navmodel.backstack.BackStackElements
import com.bumble.appyx.navmodel.backstack.operation.BackStackOperation
import com.bumble.appyx.navmodel.backstack.operation.NewRoot
import com.bumble.appyx.navmodel.backstack.operation.Remove
import kotlinx.parcelize.Parcelize
import kotlinx.parcelize.RawValue
/**
* Don't process NewRoot if the nav target already exists in the stack.
@@ -32,3 +38,45 @@ fun <T : Any> BackStack<T>.removeLast(element: T) {
} ?: return
accept(Remove(lastExpectedNavElement.key))
}
/**
* Replace the root element of the backstack, and keep the elements on top of it, contrary to [safeRoot] which removes all the elements.
*/
fun <T : Any> BackStack<T>.replaceRoot(element: T) {
accept(ReplaceRoot(element))
}
/**
* Replace the root element of the backstack, keeping the elements on top of it.
*
* Operation:
*
* [A, B, C] + ReplaceRoot(D) = [D, B, C]
* [A] + ReplaceRoot(D) = [D]
*/
@Parcelize
internal data class ReplaceRoot<T : Any>(
private val element: @RawValue T
) : BackStackOperation<T> {
override fun isApplicable(elements: BackStackElements<T>): Boolean =
elements.rootElement()?.key?.navTarget != element
override fun invoke(elements: BackStackElements<T>): BackStackElements<T> {
val root = elements.rootElement() ?: return elements
if (root.targetState == BackStack.State.ACTIVE) {
// Nothing on top of the root, so this is the same as NewRoot
return NewRoot(element).invoke(elements)
}
// The root is stashed below other elements: drop it, as Remove does for a non active element,
// and add the new root, stashed, in place of it.
val newRoot = BackStackElement(
key = NavKey(element),
fromState = BackStack.State.STASHED,
targetState = BackStack.State.STASHED,
operation = this,
)
return elements.map { if (it == root) newRoot else it }
}
private fun BackStackElements<T>.rootElement() = firstOrNull { it.targetState != BackStack.State.DESTROYED }
}
@@ -33,7 +33,8 @@ import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.di.DependencyInjectionGraphOwner
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.ui.media.ImageLoaderHolder
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrixmedia.api.ImageLoaderHolder
import kotlinx.parcelize.Parcelize
/**
@@ -59,6 +60,7 @@ class LoggedInAppScopeFlowNode(
interface Callback : Plugin {
fun navigateToBugReport()
fun navigateToAddAccount()
fun navigateToSession(sessionId: SessionId)
}
private val callback: Callback = callback()
@@ -91,6 +93,10 @@ class LoggedInAppScopeFlowNode(
override fun navigateToAddAccount() {
callback.navigateToAddAccount()
}
override fun navigateToSession(sessionId: SessionId) {
callback.navigateToSession(sessionId)
}
}
return createNode<LoggedInFlowNode>(buildContext, listOf(callback))
}

Some files were not shown because too many files have changed in this diff Show More