Compare commits

..

436 commits
wallet ... main

Author SHA1 Message Date
Jorge Martin Espinosa
87b3a5d2f0
Add better logs to track token update failures (#6859)
1. Make some logs use `info` log level instead of `debug`, so they appear in most user's bug reports.
2. Make the anonymized tokens even harder to reverse.
3. Detect when the tokens we should be saving match the current ones, as that's an error.
2026-05-26 12:19:26 +02:00
Jorge Martin Espinosa
1e67c2f77b
Move empty day separator filtering to a timeline post-processor (#6866)
* Move empty day separator filtering to a timeline post-processor

* Split `FilterPublicMembershipChangesPostProcessor` from `RoomBeginningPostProcessor`
2026-05-26 08:28:32 +00:00
Jorge Martin Espinosa
0e2213a199
Fix public read receipts being sent by mistake (#6838)
When returning to the chat screen from the room details one or a member's profile, `TimelineEvent.OnScrollFinished` will be called immediately, and this would read the default value for `isSendPublicReadReceiptsEnabled`, which is `true`.

If you had public read receipts disabled, this is a mistake, and would send a public read receipt. Instead, what we want to do is wait until the updated value is emitted and use it to decide whether we want to send a public or private read receipt.
2026-05-26 10:10:42 +02:00
Benoit Marty
1f3d848c79
Image edition before sending: crop and rotate. (#6842)
* Add crop and rotate editing before sending images (#6363)

* feat(messages): add crop and rotate before image upload

* Update screenshots

* chore: trigger CI after screenshot update

* fix: resolve detekt violations in image editor and media viewer modules

* fix: require explicit edits param, use plurals for rotation a11y, remove redundant @Inject

* fix: require explicit edits param, use plurals for rotation a11y, remove redundant @Inject

* fix: use semantically correct RotateRight icon for image rotation action

* Update screenshots

* chore: trigger CI after screenshot update

---------

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

* Update design and UX

Update and add tests
Improve preview

* Update screenshots

* For quality issue and improve preview

* Update screenshots

* Remove default value of data class.

* Rename file.

* Fix tests.

* Allow detecting touch events outside the image by applying the drag detection to the parent node and offsetting the touch events

* Improve touch detection.

* Update screenshots

* Remove useless line.

---------

Co-authored-by: Gianluca Iavicoli <gianluca.iavicoli04@gmail.com>
Co-authored-by: ElementBot <android@element.io>
Co-authored-by: Jorge Martín <jorgem@element.io>
2026-05-26 10:07:05 +02:00
ElementBot
0aaa80cbdc
Sync Strings from Localazy (#6856)
Co-authored-by: bmarty <3940906+bmarty@users.noreply.github.com>
2026-05-26 08:05:07 +00:00
Jorge Martin Espinosa
7b93bfbba9
Use runBlocking for the token refresh logic (#6863)
* Use `runBlocking` for the token refresh logic

The `RustClientSessionDelegate` callbacks always run in a separate thread, so they don't block the main thread.

This ensures the token refresh is fully done (data saved/failed to) before the SDK continues sending the pending previously failed requests
2026-05-26 09:40:07 +02:00
Benoit Marty
6cef5cec1b Remove useless line. 2026-05-26 09:22:43 +02:00
renovate[bot]
f447560665
Update codecov/codecov-action action to v6.0.1 (#6864)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-26 07:59:33 +02:00
ElementBot
1f6824d285 Update screenshots 2026-05-25 19:34:34 +00:00
Benoit Marty
8a4ff4c456 Improve touch detection. 2026-05-25 21:16:12 +02:00
bxdxnn
3df85017af
Fix formatting inconsistencies in latest event summaries (#6855)
* Fix message type prefixes formatting inconsistencies

* Use new string for the poll summary prefix instead of the A11y text. Also add tests check for the bold spans.

---------

Co-authored-by: Jorge Martín <jorgem@element.io>
2026-05-25 11:40:25 +02:00
bxdxnn
b31dad4b26
Do not show membership/profile events in public rooms (#6360)
* Filter some membership/profile/topic events in public rooms: don't display join and leave membership events in publicly joinable rooms, and hide display name and avatar url changes in non encrypted and publicly joinable rooms.

* Add empty day post-processing to the timeline based on bxdxnn's code, tweaked.

---------

Co-authored-by: Jorge Martín <jorgem@element.io>
2026-05-25 10:31:53 +02:00
Jorge Martín
6e74446209 Allow detecting touch events outside the image by applying the drag detection to the parent node and offsetting the touch events 2026-05-22 16:57:00 +02:00
Benoit Marty
afa2a824b5 Fix tests. 2026-05-22 15:28:48 +02:00
Benoit Marty
94c3bb9c41 Rename file. 2026-05-22 13:59:21 +02:00
Benoit Marty
fe6a17e977 Remove default value of data class. 2026-05-22 13:56:50 +02:00
ElementBot
4876b7c930 Update screenshots 2026-05-22 10:29:31 +00:00
Benoit Marty
a158da1d18 For quality issue and improve preview 2026-05-22 12:13:09 +02:00
ElementBot
aa92bb61c1 Update screenshots 2026-05-22 08:45:58 +00:00
Benoit Marty
bb2779549e Update design and UX
Update and add tests
Improve preview
2026-05-22 10:29:36 +02:00
renovate[bot]
5277382e6d
Update dependencyAnalysis to v3.12.0 (#6840)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-22 07:57:00 +02:00
Gianluca Iavicoli
bcad1f9dce Add crop and rotate editing before sending images (#6363)
* feat(messages): add crop and rotate before image upload

* Update screenshots

* chore: trigger CI after screenshot update

* fix: resolve detekt violations in image editor and media viewer modules

* fix: require explicit edits param, use plurals for rotation a11y, remove redundant @Inject

* fix: require explicit edits param, use plurals for rotation a11y, remove redundant @Inject

* fix: use semantically correct RotateRight icon for image rotation action

* Update screenshots

* chore: trigger CI after screenshot update

---------

Co-authored-by: ElementBot <android@element.io>
Co-authored-by: Benoit Marty <benoitm@element.io>
2026-05-21 15:18:40 +02:00
renovate[bot]
00efd9a01c
Update peaceiris/actions-gh-pages action to v4.1.0 (#6820)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-21 15:01:35 +02:00
renovate[bot]
d6ef505260
Merge pull request #6816 from element-hq/renovate/media3
Update media3 to v1.10.1
2026-05-21 15:01:00 +02:00
renovate[bot]
7e42dd1529
Update dependency org.matrix.rustcomponents:sdk-android to v26.05.20 (#6831)
* Update dependency org.matrix.rustcomponents:sdk-android to v26.05.20

* Fix API breaks:

- Handle new `ClientBuildException.InvalidRawKey` variant.
- `RoomInfo` now has a `fullyReadEventId` .

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jorge Martín <jorgem@element.io>
2026-05-21 12:27:29 +02:00
Benoit Marty
989201eb68
Merge pull request #6705 from bxdxnn/misc/roommembermoderation-avatar-tap
Make the avatar in the room member moderation bottom sheet clickable
2026-05-21 11:59:28 +02:00
Benoit Marty
5be14df828
Merge pull request #6822 from element-hq/renovate/kotlin
Update kotlin to v2.3.8
2026-05-21 10:56:54 +02:00
Benoit Marty
88a90aeb5a
Merge pull request #6833 from element-hq/renovate/io.element.android-element-call-embedded-0.x
Update dependency io.element.android:element-call-embedded to v0.19.4
2026-05-21 10:55:48 +02:00
Benoit Marty
4526563668
Merge pull request #6817 from element-hq/feature/bma/automaticRetry
[Link new device] Rotate QrCode instead of showing an error
2026-05-21 10:29:29 +02:00
renovate[bot]
4607681c5f
Update dependency io.element.android:element-call-embedded to v0.19.4 2026-05-21 08:15:40 +00:00
renovate[bot]
eed183be53
Update metro to v1.1.1 (#6832)
* Update metro to v1.1.1

* Fix `@ContributesBinding` usage with `@AssistedInject` in `DefaultVideoMetadataExtractor`

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jorge Martín <jorgem@element.io>
2026-05-21 10:10:13 +02:00
Benoit Marty
b4f23d9d9e Fix wrong 'modifier' usage. 2026-05-21 09:14:18 +02:00
Benoit Marty
19397b6fa7 Fix warning 2026-05-21 09:10:43 +02:00
Benoit Marty
dea808ce8d
Merge pull request #6818 from element-hq/feature/bma/markAsUnreadInRoomDetails
Add mark as read / unread in room details
2026-05-21 09:01:52 +02:00
Benoit Marty
c3a4d64ec6
Merge pull request #6827 from element-hq/feature/bma/a11y/roomNameHeading
[a11y] Improve accessibility of screen headers.
2026-05-21 09:00:33 +02:00
Benoit Marty
0c3c9f48bb
Merge pull request #6830 from element-hq/feature/bma/a11y/videoPlayer
[a11y] Improve accessibility of video and audio player
2026-05-20 18:06:04 +02:00
Benoit Marty
44df2d2c17 [a11y] Improve accessibility of media controller 2026-05-20 17:23:30 +02:00
cizra
a33d717aa0
Don't compress images sent through the Files attachment picker (#6755)
* Don't compress images sent through the Files attachment picker

Images and videos picked through the "Attachment" picker are now
uploaded without re-encoding, regardless of the "Optimize media quality"
setting. The gallery and camera pickers keep the existing behaviour,
matching what Element Web/Desktop and most other messengers do.

Fixes #6365

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Make sure we select the right video preset for sending as file

Wait for the video size estimations to be calculated before preprocessing the video file

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Jorge Martín <jorgem@element.io>
2026-05-20 17:19:08 +02:00
ganfra
933b18f6c2
Merge pull request #6813 from element-hq/renovate/dependencyanalysis
Update dependencyAnalysis to v3.11.0
2026-05-20 17:09:13 +02:00
Benoit Marty
4641ae666e [a11y] Improve accessibility of media controller 2026-05-20 17:04:24 +02:00
Benoit Marty
bd01b27517 [a11y] Ensure that video overlay with controls is never hidden when screen reader is enabled. 2026-05-20 16:21:06 +02:00
Benoit Marty
0725c16471
Merge pull request #6826 from element-hq/feature/bma/a11y/avatarEdit
Hide edit pencil from accessibility
2026-05-20 15:58:05 +02:00
Benoit Marty
df9a3fe9c2 [a11y] Improve accessibility of screen headers. 2026-05-20 15:53:08 +02:00
Benoit Marty
c2e357cbcd [a11y] Improve accessibility of screen headers. 2026-05-20 15:52:46 +02:00
Benoit Marty
5b2970f789 [a11y] Improve accessibility of screen headers. 2026-05-20 15:49:18 +02:00
Benoit Marty
620f1865e8 [a11y] Improve accessibility of screen headers. 2026-05-20 15:39:11 +02:00
Jorge Martin Espinosa
42566b4b1f
Release proximity wakelock on Element Call when call ends (#6825)
The `CoroutineScope` that launched this logic was cancelled by that point, so the wakelock was never released.
2026-05-20 15:23:03 +02:00
Benoit Marty
67c668990e Hide edit pencil from accessibility
Closes #6378
2026-05-20 14:50:26 +02:00
ElementBot
260ea4a7d2 Update screenshots 2026-05-20 12:44:42 +00:00
Benoit Marty
3d85cf4fd2 Format 2026-05-20 14:27:38 +02:00
Jorge Martin Espinosa
64d9336901
Fix 'Conversation label cannot be empty' error (#6823)
This happens when building a `ShortcutInfoCompat` in `DefaultNotificationConversationService.onSendMessage` when the provided room name is not null but it's empty.
2026-05-20 14:26:44 +02:00
Jorge Martin Espinosa
42c141109f
Fix media viewer flickering (#6800)
* Fix media viewer flickering

This was caused by the data being loaded triggering an index update that got out of sync with the displayed items, and for an instant, the pager index pointed to the wrong data until it was refreshed

* Reuse the same 'displayer' function for both forwards and backwards pagination

* Make `dataFlow` a property so we don't create a new instance every time we access it

* Remove `pageDataComparator` as it prevented new items from being loaded when a pagination returned no valid items to display but has more items to load

Make sure we modify the current index when loading new data only if it was pointing to the input event id.

* Fix `MediaViewerDataSource` overriding the provided timestamp for `Loading` items

Test emitting different loading items from the data source results in the state displaying the different items, so they will trigger a new pagination attempt

* Add regression test to check loading -> error -> loading states will still trigger 2 separate `LoadMore` events
2026-05-20 11:32:32 +02:00
renovate[bot]
94392f3ebd
Update kotlin to v2.3.8 2026-05-20 09:16:18 +00:00
Benoit Marty
dc05388cca Update tests. 2026-05-20 10:53:50 +02:00
ganfra
52f12031c8
Merge pull request #6819 from element-hq/renovate/net.zetetic-sqlcipher-android-4.x
Update dependency net.zetetic:sqlcipher-android to v4.16.0
2026-05-19 21:15:19 +02:00
Benoit Marty
5dd897dae8 Use AnimatedContent instead of reinventing the wheel. 2026-05-19 19:00:58 +02:00
ElementBot
900b888044 Update screenshots 2026-05-19 18:43:48 +02:00
Benoit Marty
1473f5e806 [Link new device] and add tests. 2026-05-19 18:43:48 +02:00
Benoit Marty
33c7c75550 Fix detekt issue 2026-05-19 18:43:47 +02:00
Benoit Marty
75feaa7ee7 [Link new device] Fix and add tests. 2026-05-19 18:43:47 +02:00
Benoit Marty
18bc6a27c4 [Link new device] Improve UI transition between QRCodes. 2026-05-19 18:43:46 +02:00
Benoit Marty
a6f7c05458 [Link new device] Automatically rotate the QR Code - limit number of rotation to 10. 2026-05-19 18:43:46 +02:00
Benoit Marty
c59b96de66 [Link new device] Automatically rotate the QR Code 2026-05-19 18:43:45 +02:00
renovate[bot]
2248cd20f3
Update dependency net.zetetic:sqlcipher-android to v4.16.0 2026-05-19 16:39:00 +00:00
Benoit Marty
53fe12bdda "Mark as unread" navigate back to the room list.
#6398
2026-05-19 18:37:32 +02:00
Benoit Marty
882d5577a7 Add "Mark as read", "Mark as unread" in room settings.
#6398
2026-05-19 18:06:11 +02:00
Benoit Marty
5af57906b5 Changelog for version 26.05.2 2026-05-19 15:23:47 +02:00
Benoit Marty
28d4985435 Merge tag 'v26.05.2' into develop
tag
2026-05-19 14:44:13 +02:00
Benoit Marty
20d0d115d4 Merge branch 'release/26.05.2' 2026-05-19 14:44:04 +02:00
Benoit Marty
d8793ca8f6 Adding fastlane file for version 26.05.2 2026-05-19 14:44:02 +02:00
Benoit Marty
f373c73e60 Setting version for the release 26.05.2 2026-05-19 14:43:49 +02:00
renovate[bot]
2f406eaf41
Update dependency org.matrix.rustcomponents:sdk-android to v26.05.18 (#6805)
* Update dependency org.matrix.rustcomponents:sdk-android to v26.05.18

* Fix breaking API changes

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jorge Martín <jorgem@element.io>
2026-05-19 09:20:06 +02:00
renovate[bot]
c94ba068ed
Update dependencyAnalysis to v3.11.0 2026-05-19 07:18:13 +00:00
ganfra
6cc2e59436
Merge pull request #6811 from element-hq/feature/live_location_remove_feature_flag
Remove LiveLocationSharing feature flag
2026-05-19 09:16:18 +02:00
Jorge Martin Espinosa
e3d1a811d5
Disable biometric unlock when we disable pin code unlock (#6781)
* Disable biometric unlock when we disable pin code unlock
2026-05-18 22:19:22 +02:00
Jorge Martin Espinosa
4e3853a718
Attempt to fix room list item duplicates at midnight (#6793)
* Attempt to fix room list item duplicates at midnight

This seems to happen because of a race condition between `RoomListDataSource.observeDateTimeChanges` and `RoomListDataSource.replaceWith` being called at almost the same time and the first one using the newly received items from observing the timeline items but not updating the cache which will be later reused by `replaceWith`, containing incorrect indices
2026-05-18 22:18:52 +02:00
Benoit Marty
1774d1d39a
Merge pull request #6776 from element-hq/feature/bma/renovateConfig
Renovate: Keep Guava on the Android variant and ignore jre-only upgrades
2026-05-18 21:04:29 +02:00
ganfra
828337b343 Remove LiveLocationSharing feature flag 2026-05-18 20:25:36 +02:00
Jorge Martin Espinosa
174a6cad0d
Create a new room when inviting people in a DM (#6756)
* Create a new room when inviting people to a DM

* Improve screenshot tests

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
2026-05-18 19:01:11 +02:00
Benoit Marty
07668502fa
Merge pull request #6798 from element-hq/sync-localazy
Sync Strings
2026-05-18 18:45:27 +02:00
Benoit Marty
1111315c6a
Merge pull request #6802 from hughns/qr-other-device-not-signed-in
Show error message when using "Sign in with QR code" with a QR from a device that is also not signed in
2026-05-18 14:33:17 +02:00
Benoit Marty
653d9861dc
Remove SignInWithClassic FeatureFlag to enable the feature. (#6698)
Closes #6669
2026-05-18 13:54:54 +02:00
Hugh Nimmo-Smith
2771ce9442 Merge branch 'qr-other-device-not-signed-in' of https://github.com/hughns/element-x-android into qr-other-device-not-signed-in 2026-05-18 11:53:02 +01:00
Hugh Nimmo-Smith
fb17fc7e8a Iterate 2026-05-18 11:52:51 +01:00
Hugh Nimmo-Smith
efa61f1d24
Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-18 11:51:23 +01:00
Hugh Nimmo-Smith
458405b29f Iterate 2026-05-18 11:44:47 +01:00
Jorge Martin Espinosa
9b1735e0e9
Fix Maestro again after changes to the invite flow (#6796)
* Fix Maestro: tap on confirmation for inviting unknown users to a room

* Tap on back after inviting some user

* Tap on back again

* Confirm inviting someone to a DM

* Make fix conditional
2026-05-18 09:26:58 +00:00
Hugh Nimmo-Smith
668edbc679 Fix scanning code from signed out device when using "Sign in with QR code" 2026-05-18 09:50:54 +01:00
Jorge Martin Espinosa
2954174c56
Only load full media on media viewer when it's the visible item (#6794)
* Only load full media on media viewer when it's the visible item

* Allow cancelling ongoing media loading if scrolling fast
2026-05-18 10:29:14 +02:00
cizra
e6c3a8ff1d
Add MIDI playback (#6770)
* Add MIDI playback

* Implement PR suggestions
2026-05-18 10:06:47 +02:00
renovate[bot]
a89d37297f
Update dependency com.google.firebase:firebase-bom to v34.13.0 (#6789)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-18 09:22:20 +02:00
renovate[bot]
1c317d1ffd
Update dependency androidx.webkit:webkit to v1.16.0 (#6786)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-18 09:21:46 +02:00
renovate[bot]
ef7d1a10bf
Merge pull request #6783 from element-hq/renovate/camera
Update camera to v1.6.1
2026-05-18 09:17:07 +02:00
bmarty
dcc67f9fc6 Sync Strings from Localazy 2026-05-18 00:57:56 +00:00
Jenna Vassar
cbc677b80d
Fix room list duplicate-detection telemetry crashing before it can report (#6791)
* Add room dupe regression tests

* Fix telemetry path for dedupe discovery
2026-05-15 10:43:21 +02:00
renovate[bot]
432a7712c4
Update kotlin (#6790)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-15 09:03:50 +02:00
renovate[bot]
486754602d
Update dependency io.sentry:sentry-android to v8.41.0 (#6787)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-14 08:09:00 +02:00
renovate[bot]
e563fc7919
Update dependency androidx.compose:compose-bom to v2026.05.00 (#6784)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-14 08:08:21 +02:00
Jorge Martín
5e577e2360 Changelog for version 26.05.1 2026-05-13 13:59:48 +02:00
Jorge Martín
68d0cad3b7 Merge tag 'v26.05.1' into develop
v26.05.1
2026-05-13 13:12:51 +02:00
Jorge Martín
c1fcb0f6f6 Merge branch 'release/26.05.1' 2026-05-13 13:12:43 +02:00
Jorge Martín
4823267013 Adding fastlane file for version 26.05.1 2026-05-13 13:12:42 +02:00
Jorge Martín
7e6d3c60d2 Setting version for the release 26.05.1 2026-05-13 12:15:26 +02:00
Benoit Marty
d770a47904
Merge pull request #6780 from element-hq/feature/bma/pinIteration
Pin code: remove the key if there is no pin code
2026-05-13 11:58:55 +02:00
Benoit Marty
31d06391ed Pin code: remove the key if there is no pin code 2026-05-13 11:39:46 +02:00
renovate[bot]
737882d35e
Update dependency org.matrix.rustcomponents:sdk-android to v26.05.13 (#6779)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-13 10:18:59 +02:00
Timo
c959f50d53
Back button web view to esc (revive fixed version of: https://github.com/element-hq/element-x-android/pull/6724) (#6725)
* Change native back button behavior in EC view:
 - inject escape into webview instead of going back.
 - the webview will call back when no other modal is open.

* call down and up in the webview + make sure that we fall back to close
pip in case the webview did not handle the esc action.

* Tests and refactor to CallScreenBackPressPolicy

---------

Co-authored-by: Jorge Martín <jorgem@element.io>
2026-05-13 10:17:23 +02:00
Benoit Marty
960ff0fb75 Renovate: Keep Guava on the Android variant and ignore jre-only upgrades. 2026-05-12 21:39:48 +02:00
Benoit Marty
2ea23bcc3e
Merge pull request #6772 from element-hq/feature/bma/addMissingStrings
Add missing strings `theme.black`
2026-05-12 21:31:24 +02:00
Jorge Martin Espinosa
e8f1bf0085
Make Element Call screen work edge-to-edge (#6634)
* Update dependency io.element.android:element-call-embedded to v0.19.3

* Remove `Scaffold` component from CallScreenView

* Add immersive mode to calls in landscape orientation

* Add `consumeWindowInsets`, which fixes the webview not displaying any insets for the bottom nav bar

* Update screenshots

* Ignore compact height in PiP mode

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: ElementBot <android@element.io>
2026-05-12 17:33:02 +00:00
Benoit Marty
3ca06d71ef Rename keys on Localazy and add Black theme. 2026-05-12 17:57:55 +02:00
renovate[bot]
9eac27515e
Update dependency com.google.guava:guava to v33.6.0-jre (#6646)
* Update dependency com.google.guava:guava to v33.6.0-jre

* Use android version

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Benoit Marty <benoitm@element.io>
2026-05-12 15:35:51 +00:00
Benoit Marty
bad934d329
Do not close the MediaPlayer when navigating back from a thread. (#6771) 2026-05-12 17:00:48 +02:00
renovate[bot]
b4305b1749
Update plugin dependencycheck to v12.2.2 (#6760)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-12 16:56:34 +02:00
Valere Fedronic
9ca0b9e898
Merge pull request #6649 from element-hq/feature/valere/call/decline_timeline_rendering
feat: Update call started timeline item + declined support
2026-05-12 16:32:51 +02:00
Benoit Marty
3f02f527f0 Rename FF title 2026-05-12 16:17:10 +02:00
renovate[bot]
ae50f6b897
Update plugin sonarqube to v7.3.0.8198 (#6743)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-12 16:03:34 +02:00
renovate[bot]
c68e1b8845
Update tspascoal/get-user-teams-membership action to v4.0.1 (#6750)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-12 15:57:18 +02:00
Benoit Marty
2d4d4cdf25
Merge pull request #6720 from element-hq/renovate/major-metro
Update metro to v1 (major)
2026-05-12 15:56:18 +02:00
Benoit Marty
d299b722e3 Format code. 2026-05-12 15:50:47 +02:00
renovate[bot]
f04c836371
Update dependency io.element.android:element-call-embedded to v0.19.3 (#6766)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-12 15:42:06 +02:00
Benoit Marty
c30fee7f14 Do not close the MediaPlayer when navigating back from a thread. 2026-05-12 15:21:20 +02:00
Jorge Martin Espinosa
3234931d3c
Use the right analytics span as a parent in checkNetworkConnection (#6751)
`AnalyticsLongRunningTransaction.PushToWorkManager` was incorrectly used instead of `AnalyticsLongRunningTransaction.PushToNotification`, resulting in wrongly formatter analytic traces
2026-05-12 15:04:13 +02:00
Benoit Marty
b11fcec985
Merge pull request #6767 from element-hq/feature/bma/llsAndThread
Prevent user from starting LLS in thread
2026-05-12 14:24:16 +02:00
Benoit Marty
9326c30f05
Reduce FeatureFlags Knock effect on room creation and room edition form. (#6768)
Closes #6701
2026-05-12 14:18:49 +02:00
Benoit Marty
72be7ff1f9 Reduce FeatureFlags Knock effect on room creation and room edition form.
Closes #6701
2026-05-12 12:23:16 +02:00
Jorge Martin Espinosa
77b444581d
Improve FetchPushForegroundService's reliability (#6757)
* Improve `FetchPushForegroundService`'s reliability

- Don't use DI, we can just create the notification channel. This should speed up the creation of the service and reduce the number of `ForegroundServiceDidNotStartInTimeException` received. Also use `MainScope` instead of the app's coroutine scope.
- Move the wakelock releasing mechanism to `onDestroy` so it's always used. Previously, this would only happen when `stopService` was called, which would only happen when `stopSelf()` is called, but not when the OS or the service manager stops the service.

* Add fallback value for the notification channel title

* Replace the wrong string for the notification/channel title

---------

Co-authored-by: Benoit Marty <benoitm@element.io>
2026-05-12 11:40:46 +02:00
Valere
a227b830be add test to MessageSummaryFormatter 2026-05-11 18:08:28 +02:00
Jorge Martin Espinosa
11476c73cf
Adapt to new DM definition changes in the SDK (#6748)
* Set `DmRoomDefinition.TwoPeople` in `ClientBuilder`. This applies the 'direct and with at most 2 non-service members' rule to what the SDK should consider a DM.

* Map `RoomInfo.isDm` from the SDK

* Map `NotificationData.isDm` from `NotificationInfo.roomInfo.isDm`

* Remove `RoomIsDmCheck` file as its extension functions are now redundant. Move `Room.isDm` helper function to `BaseRoom`.

* Map `isDm` in `SpaceRoom` from the SDK too

* Replace `isDirect` with `isDm` where possible

* Map `RoomMember.isServiceMember` from the SDK and use it to tell apart normal members of a room from service members (i.e. `RoomMembersState.getDirectRoomMember`)
2026-05-11 17:22:16 +02:00
renovate[bot]
5e5e0bbc6e
Update dependency io.github.sergio-sastre.ComposablePreviewScanner:android to v0.9.0 (#6759)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-11 17:00:49 +02:00
Jorge Martin Espinosa
1ffc09e046
Stop removing the logs dir when clearing cache (#6765) 2026-05-11 16:55:40 +02:00
Valere
6bc8cd84e4 fixup test compilation 2026-05-11 16:54:30 +02:00
Benoit Marty
7f545079ad Prevent user from starting LLS in thread 2026-05-11 14:49:53 +02:00
renovate[bot]
90ce30d6e7
Update actions/add-to-project action to v2 (#6758)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-11 14:13:23 +02:00
ElementBot
c5a054b9e3 Update screenshots 2026-05-11 09:36:54 +00:00
Valere
a478d87fc3 Merge branch 'develop' into feature/valere/call/decline_timeline_rendering 2026-05-11 11:21:02 +02:00
Valere
f0f5e8be25 review: Refactor preview, show all variants 2026-05-11 11:20:10 +02:00
Valere
9ce8253086 review: Add unit test for notificationFormater 2026-05-11 11:16:45 +02:00
Valere
9a8046c02d review: Update signature and keep formatters grouped together 2026-05-11 10:48:50 +02:00
Valere
4437d4d9df review: Invert if for better readability 2026-05-11 10:47:46 +02:00
Valere
885d0f2cb9 review: better comment 2026-05-11 10:47:32 +02:00
Valere
f05ceb9f5d review: Use @stringRes annotation for Int 2026-05-11 10:47:17 +02:00
ganfra
e49e183178
Feature : share live location (#6741)
* First live location sharing sending implementation

* Simplify logic around canStop sharing

* Add some debug logs around LiveLocationSharingService

* Add LiveLocationException

* Expose beaconId to identify the current share

* Throttle live location instead of debouncing

* Keep sync alive when sharing live location

* Improve LiveLocation sharing

* Show LiveLocationDisclaimer

* Read minDistanceUpdate in LiveLocationSharingService

* Set minDistanceUpdate in AdvancedSettings

* Display banner in room when sharing live location

* Fix tests around LiveLocationSharing

* Ensure shares are properly restarted/stopped when app is re-launched

* Ensure LLS data is cleared when session is removed

* Update and fix LLS tests

* Handle Start LLS in ui

* Add check LLS permissions

* Remove hardcoded strings

* Fix quality and format

* Create DeviceLocationProvider so we can share location data between sources (presenter/live location service)

* Update screenshots

* Fix warning

* Do not try to stop if it was not sharing

* Revert "Create DeviceLocationProvider so we can share location data between sources (presenter/live location service)"

This reverts commit ba12bd968e82941cc231bdbb449310b24c97c5b8.

* Tweak location provider config values

* Address PR review remarks

* Fix ktlint

* Update screenshots

* Fix some tests after merging develop

* Adjust TimelineItemLocationView ui to match figma

* Update screenshots

* Documentation and cleanup

* Remove temporary resource

---------

Co-authored-by: ElementBot <android@element.io>
Co-authored-by: Benoit Marty <benoit@matrix.org>
Co-authored-by: Benoit Marty <benoitm@matrix.org>
2026-05-11 08:19:28 +00:00
ElementBot
0c657c258a
Sync Strings from Localazy (#6761)
Co-authored-by: bmarty <3940906+bmarty@users.noreply.github.com>
2026-05-11 09:37:59 +02:00
bxdxnn
071d98c66b
Render media captions formatting in the media viewer (#6729)
* Render media captions formatting in the media viewer

* Update screenshots

* Trigger actions

* Remove unused imports and reformat code

---------

Co-authored-by: ElementBot <android@element.io>
Co-authored-by: Jorge Martín <jorgem@element.io>
2026-05-08 16:30:32 +02:00
Jorge Martin Espinosa
4a4b3e07ef
Use just the other user's avatar for DM details (#6738)
* Use just the other user's avatar for DM details. Remove `DmAvatars` component and other no longer needed data.

* Improve selection indicator by clipping the avatar to a circle shape

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
2026-05-08 11:17:30 +02:00
ganfra
245c30c18a Changelog for version 26.05.0 2026-05-07 17:47:15 +02:00
ganfra
14096f3837 Merge tag '26.05.0' into develop 2026-05-07 17:45:54 +02:00
Benoit Marty
d50c04a420
Merge pull request #6746 from element-hq/renovate/org.matrix.rustcomponents-sdk-android-26.x
Update dependency org.matrix.rustcomponents:sdk-android to v26.05.7
2026-05-07 17:23:20 +02:00
Benoit Marty
4497d7da36
Merge pull request #6744 from element-hq/feature/bma/improvePinUX
Improve pin code ux
2026-05-07 17:04:43 +02:00
ganfra
cc65a0f114 Merge branch 'release/26.05.0' 2026-05-07 16:03:03 +02:00
ganfra
63edcd6989 Adding fastlane file for version 26.05.0 2026-05-07 16:01:40 +02:00
ganfra
0cb1a9fb14 Setting version for the release 26.05.0 2026-05-07 16:01:34 +02:00
renovate[bot]
95dac66869
Update tspascoal/get-user-teams-membership action to v4 (#6747)
* Update tspascoal/get-user-teams-membership action to v4

* Point the commit to the right tag v4.0.0

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jorge Martín <jorgem@element.io>
2026-05-07 15:39:26 +02:00
Jorge Martín
7e3acc03b9 Fix API breaks 2026-05-07 15:13:51 +02:00
renovate[bot]
a6bee757b4
Update dependency org.matrix.rustcomponents:sdk-android to v26.05.7 2026-05-07 12:11:36 +00:00
Benoit Marty
a7c9fedc8f
Merge pull request #6740 from element-hq/feature/bma/uiSample
Introduce UI sample
2026-05-07 12:28:08 +02:00
Benoit Marty
0abb04ea62
Merge pull request #6708 from escix/patch-3
Fix 2 x Crash the app in Developer Options - Update AppDeveloperSettingsView.kt
2026-05-07 11:56:01 +02:00
Benoit Marty
022d21b097
Remove trailing spaces 2026-05-07 11:54:27 +02:00
Benoit Marty
80a46fdb3d Fix test 2026-05-07 11:52:21 +02:00
ElementBot
790c30ca1f Update screenshots 2026-05-07 09:20:09 +00:00
Jorge Martin Espinosa
866c8375b3
Make send event state UI easier to click (#6739)
* Make send event state UI easier to click

Make it so the whole timestamp view can be clicked

* Update screenshots

* Simplify `clickableModifier`

---------

Co-authored-by: ElementBot <android@element.io>
2026-05-07 11:11:07 +02:00
Benoit Marty
ae044607c7 Add missing screenshot and improve UX when user enter wrong pin then correct pin. 2026-05-07 10:59:13 +02:00
Benoit Marty
d18b529285 Ensure that remaining pin code attempts is never bigger than the value in the config. 2026-05-07 10:59:13 +02:00
Benoit Marty
d558371798 Add test. 2026-05-07 10:59:12 +02:00
Benoit Marty
5269b9787e Fix quality issue. 2026-05-07 10:59:12 +02:00
Benoit Marty
44baa4a383 PinUnlockEvents -> PinUnlockEvent 2026-05-07 10:59:12 +02:00
Benoit Marty
668b03f8bc SetupPinEvents -> SetupPinEvent 2026-05-07 10:59:11 +02:00
Benoit Marty
90918cbb9d SetupBiometricEvents -> SetupBiometricEvent 2026-05-07 10:59:11 +02:00
Benoit Marty
bc60512e10 LockScreenSettingsEvents -> LockScreenSettingsEvent 2026-05-07 10:59:11 +02:00
Benoit Marty
94d37c68d5 Use test extension. 2026-05-07 10:59:11 +02:00
Benoit Marty
8799dda471 Force sign out if PIN code store is corrupted. 2026-05-07 10:59:10 +02:00
Benoit Marty
61374bca4e Improve detection of configure PIN code. 2026-05-07 10:59:10 +02:00
Benoit Marty
2f45ca8835
Merge pull request #6682 from element-hq/feature/bma/customMasScheme
Add a way to tweak MAS url.
2026-05-07 10:51:32 +02:00
ElementBot
861de339d8 Update screenshots 2026-05-07 08:43:48 +00:00
Benoit Marty
4948497be2 More replacements 2026-05-07 10:01:30 +02:00
Benoit Marty
b19a2c5a44 Use generic id for default 2026-05-07 09:50:12 +02:00
Benoit Marty
0349162694 More replacements 2026-05-07 09:50:00 +02:00
renovate[bot]
a27278da20
Update dependencyAnalysis to v3.10.0 (#6742)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-07 08:09:15 +02:00
ElementBot
a7c4a2b404 Update screenshots 2026-05-06 20:17:22 +00:00
Benoit Marty
223dd1f6b0 Fix invalid userId. 2026-05-06 21:59:59 +02:00
Benoit Marty
a77c2c0c7e Update ref to submodule. 2026-05-06 17:54:22 +02:00
Benoit Marty
15162e4e32
Merge pull request #6726 from element-hq/feature/bma/renameVerificationMethod
Rename verification methods
2026-05-06 17:50:11 +02:00
Benoit Marty
2a694f6dfd Create PreviewData with sample of UGC used for preview.
Fix preview issue where username was used for room/avatar name.
2026-05-06 17:45:50 +02:00
Copilot
6ef9315468
Remove RoomDirectorySearch feature flag — always enable the feature (#6736)
* Remove RoomDirectorySearch feature flag, always enable the feature

Co-authored-by: stefanceriu <637564+stefanceriu@users.noreply.github.com>

* Apply ktlint formatting

Co-authored-by: jmartinesp <480955+jmartinesp@users.noreply.github.com>

* Update screenshots

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: stefanceriu <637564+stefanceriu@users.noreply.github.com>
Co-authored-by: jmartinesp <480955+jmartinesp@users.noreply.github.com>
Co-authored-by: ElementBot <android@element.io>
2026-05-06 16:05:19 +02:00
Jorge Martin Espinosa
9b91fabbd6
Use 'Report a problem' string instead of 'Report bug' (#6735)
* Use 'Report a problem' instead of 'Report bug'. This old string will be deleted soon.

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
2026-05-06 15:33:17 +02:00
renovate[bot]
f4cf704335
Update dependency org.matrix.rustcomponents:sdk-android to v26.05.6 (#6734)
* Update dependency org.matrix.rustcomponents:sdk-android to v26.05.6

* Fix API breaks:

- Add `RoomMember.isServiceMember`.
- Add `beacon` and `beaconInfo` power levels.

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jorge Martín <jorgem@element.io>
2026-05-06 15:31:52 +02:00
Jorge Martin Espinosa
34f001d22c
Fix back button sometimes not working after exiting a thread (#6732)
When I reproduced the issue, it looked like the coroutine used to mark as read and then exit the room was canceled, leaving the `markingAsReadAndExiting` variable with `true` value and preventing the exit block from running again.
2026-05-06 14:35:08 +02:00
Jorge Martin Espinosa
70452842d3
Make icons in the Chat screen top bar 16dp (#6733)
* Make icons in the Chat screen top bar 16dp. This matches the designs in Figma.

* Fix the padding between the title and the icons

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
2026-05-06 09:16:13 +00:00
ganfra
f0b9ce567a
Merge pull request #6731 from element-hq/renovate/org.maplibre.gl-android-sdk-13.x
Update dependency org.maplibre.gl:android-sdk to v13.1.0
2026-05-06 10:49:49 +02:00
ganfra
bcc2eb3d34
Merge pull request #6727 from element-hq/renovate/net.zetetic-sqlcipher-android-4.x
Update dependency net.zetetic:sqlcipher-android to v4.15.0
2026-05-06 10:28:04 +02:00
ganfra
1fd02be0f4
Merge branch 'develop' into renovate/net.zetetic-sqlcipher-android-4.x 2026-05-06 10:10:57 +02:00
renovate[bot]
47ca5a5f2f
Update dependency org.maplibre.gl:android-sdk to v13.1.0 2026-05-06 01:36:24 +00:00
Kurban Sagitov
a7711b7d52
Fix low width image message (#6692)
* fix low width image message

* added previews for narrow images

* Update screenshots
---------

Co-authored-by: ElementBot <android@element.io>
2026-05-05 17:10:24 +02:00
Benoit Marty
65ddc4355e
Merge pull request #6700 from element-hq/feature/bma/lenientJson
Let our Json parser accept comments and trailing comma.
2026-05-05 17:08:21 +02:00
Benoit Marty
3b3b4813b6 Detekt and ktlint are confused with Kotlin context... 2026-05-05 16:50:22 +02:00
Benoit Marty
384c806131 Fix compilation warnings 2026-05-05 16:22:32 +02:00
renovate[bot]
54a6666988
Update dependency net.zetetic:sqlcipher-android to v4.15.0 2026-05-05 14:05:26 +00:00
bxdxnn
28e1062eed
Reimplement "Natural media viewer swiping order" (#6715) 2026-05-05 16:02:52 +02:00
Benoit Marty
0a9259604b Improve FakeSessionVerificationService 2026-05-05 15:29:32 +02:00
Jorge Martin Espinosa
2d203e83b9
Revert PR #6642 (#6724)
* Revert "Change native back button behavior in EC view (close settings in EC with os native back) (#6642)"

This reverts commit 6ba4679908.

* Fix API breaks from revert
2026-05-05 15:24:27 +02:00
Jorge Martin Espinosa
a12b519155
Allow cancelling room loading in Home screen (#6723)
Previously, this was disabled by mistake, since it's the default behavior.
2026-05-05 13:14:30 +02:00
Benoit Marty
51bcaca9db Rename methods around verification, to match SDK naming. 2026-05-05 12:17:51 +02:00
renovate[bot]
c40f916b4f
Merge pull request #6722 from element-hq/renovate/roborazzi
Update roborazzi to v1.60.0
2026-05-05 10:55:16 +02:00
renovate[bot]
ab85c554ff
Update metro to v1 2026-05-05 00:49:45 +00:00
Benoit Marty
d7be603bfc
Merge pull request #6654 from bxdxnn/fix/clip-room-summary
Add clipping to RoomSummaryRow
2026-05-04 22:38:51 +02:00
bxdxnn
6897cc5721 Add clipping to RoomSummaryRow 2026-05-04 18:29:37 +00:00
renovate[bot]
54525d855e
Update dependency org.matrix.rustcomponents:sdk-android to v26.05.4 (#6718)
* Update dependency org.matrix.rustcomponents:sdk-android to v26.05.4

* Fix `RoomInfo` fixture

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jorge Martín <jorgem@element.io>
2026-05-04 10:04:20 +00:00
Benoit Marty
26fe5b6492
Merge pull request #6650 from element-hq/feature/bma/a11yFixes
[a11y] Fix a set of issues
2026-05-04 11:50:15 +02:00
Benoit Marty
61f074ddc1 Let our Json parser accept comments and trailing comma. 2026-05-04 10:05:11 +02:00
Benoit Marty
e8e5a2d454
Merge pull request #6716 from element-hq/sync-localazy
Sync Strings
2026-05-04 09:59:34 +02:00
Valere
18fbe91fc7 Merge branch 'develop' into feature/valere/call/decline_timeline_rendering 2026-05-04 09:00:00 +02:00
Valere
a0646717a3 fix test compilation 2026-05-04 08:59:46 +02:00
Hi Dude!
27869c0e04
Fix calls on Huawei devices: skip addWebMessageListener on Chromium < 119 (#6640)
* Fix calls on Huawei: skip addWebMessageListener on Chromium < 119

* Fix lint issues, log webview version

---------

Co-authored-by: manfrommedan <manfrommedan@users.noreply.github.com>
Co-authored-by: Jorge Martín <jorgem@element.io>
2026-05-04 08:30:05 +02:00
renovate[bot]
5733c3149a
Update dependency com.posthog:posthog-android to v3.43.0 (#6704)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-04 08:07:11 +02:00
renovate[bot]
e81ab35f5b
Update dependency io.nlopez.compose.rules:detekt to v0.5.8 (#6711)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-04 08:06:30 +02:00
bmarty
0d2f71643a Sync Strings from Localazy 2026-05-04 00:54:27 +00:00
Strac Consulting Engineers Pty Ltd
715402d178
Update AppDeveloperSettingsView.kt
Fix 2 x Crash the app
Fixes #6707
2026-05-02 09:40:23 +10:00
bxdxnn
194945c49e Add .clickable to RoomMemberModerationView avatar 2026-05-01 12:19:17 +00:00
Benoit Marty
65a9a97735
Merge pull request #6668 from element-hq/feature/bma/removeExternalCallSupport
Remove external call support
2026-04-30 17:36:40 +02:00
Benoit Marty
6c7c48da69 Fix compilation issue 2026-04-30 16:59:14 +02:00
Benoit Marty
e21276f323 Merge branch 'develop' into feature/bma/removeExternalCallSupport 2026-04-30 16:58:11 +02:00
Benoit Marty
85ef12a401
Merge pull request #6697 from element-hq/feature/bma/fixDependencyError
Fix dependency error
2026-04-30 16:57:19 +02:00
Benoit Marty
4e38846342
Merge pull request #6693 from element-hq/renovate/compose.bom
Update dependency androidx.compose:compose-bom to v2026.04.01
2026-04-30 16:54:23 +02:00
Benoit Marty
11b9efa2c9 Migrate to v2 testing APIs 2026-04-30 16:04:07 +02:00
Jorge Martin Espinosa
30fd90abb9
Mitigate a deadlock when loading room timelines (#6674)
This may be happening because we were not destroying focused event timelines used for the media viewer/gallery when necessary, and having several of those back paginating *may* have caused a deadlock in the event cache.
2026-04-30 16:01:24 +02:00
Benoit Marty
13775f4fbd Update kotlin version 2026-04-30 14:09:41 +02:00
Benoit Marty
b8995e4356 Fix quality issues 2026-04-30 14:09:07 +02:00
ElementBot
0b04dec85c Update screenshots 2026-04-30 10:44:04 +00:00
Benoit Marty
d6f8c13c3f MediaPlayerControllerView: Use IconButton instead of Box and remove the clipping. 2026-04-30 12:20:53 +02:00
Benoit Marty
b1890de26a MediaViewerView: move TopBar to Scaffold topbar 2026-04-30 12:20:53 +02:00
Benoit Marty
8188ef1463 Improve MediaViewerBottomBar usage. 2026-04-30 12:19:32 +02:00
Benoit Marty
562e36b5ea Improve how the ThumbnailView is added to the composition 2026-04-30 12:19:32 +02:00
Benoit Marty
97cada7432 Move isTalkbackActive to a11y package. 2026-04-30 12:19:31 +02:00
Benoit Marty
5e963fc743 a11y: do not use Overlay if screen reader is active, or external keyboard is connected.
Related to #6399
2026-04-30 12:19:31 +02:00
Benoit Marty
dcd0a98c0c Declare Top bar first and use zIndex. 2026-04-30 12:19:30 +02:00
Benoit Marty
4e46f12a12 Remove useless Box 2026-04-30 12:17:25 +02:00
Benoit Marty
70583fc3fc Remove unused content parameter. 2026-04-30 12:07:07 +02:00
Benoit Marty
9912d763d4 a11y: use different content description when the red dot is displayed
Closes #6395
2026-04-30 12:07:06 +02:00
Benoit Marty
0c333235f8 a11y: set role = button for "learn more" texts.
Closes #6405
2026-04-30 12:07:06 +02:00
Benoit Marty
0a99b28963 a11y: let banner title be implemented as a heading.
Closes #6384
2026-04-30 12:07:06 +02:00
Benoit Marty
b815aaabc9 a11y: let section header be implemented as a heading.
Closes #6386
2026-04-30 12:07:05 +02:00
Benoit Marty
f5737e9d2b a11y: add alternative text to the info icon.
Closes #6379
2026-04-30 12:07:05 +02:00
ganfra
e5d134cd3b
Merge pull request #6687 from element-hq/renovate/kotlin
Update kotlin
2026-04-30 12:04:57 +02:00
Benoit Marty
f4b3ddfa0b Merge branch 'develop' into feature/bma/removeExternalCallSupport 2026-04-30 11:50:35 +02:00
ganfra
efe611eb4f
Merge pull request #6660 from element-hq/renovate/org.jsoup-jsoup-1.x
Update dependency org.jsoup:jsoup to v1.22.2
2026-04-30 11:30:08 +02:00
ganfra
be22ac8de9
Merge pull request #6691 from element-hq/renovate/io.sentry-sentry-android-8.x
Update dependency io.sentry:sentry-android to v8.40.0
2026-04-30 11:26:47 +02:00
ElementBot
795299ed0c Update screenshots 2026-04-30 09:20:58 +00:00
renovate[bot]
c9bc8791e9
Update kotlin 2026-04-30 09:19:59 +00:00
Benoit Marty
fabdcee520 Remove SignInWithClassic FeatureFlag to enable the feature.
Closes #6669
2026-04-30 11:19:10 +02:00
Jorge Martin Espinosa
4a79b272ef
Fix ANRs when receiving push notifications (#6696)
In Sentry there are some reports of methods called when notifications are fetched that end up having ANRs. This looked weird because everything is asynchronous... but it's still running with a `Main` dispatcher.

Using the `Default/computation` one instead should be the right call.
2026-04-30 10:51:29 +02:00
Benoit Marty
078e942a28 Cleanup dependencies 2026-04-30 09:54:54 +02:00
Benoit Marty
4d0be69b4c In the module :libraries:matrix.api, change the dependencies to:
- libraries.sessionStorage.api
- projects.libraries.architecture
from `api` to `implementation`.

Modules who need `:libraries:matrix.api` do not necessarily need to use the session storage api.
2026-04-30 09:52:10 +02:00
Benoit Marty
b50969437d Ensure clearing the cache delete the CacheStore. 2026-04-30 09:04:19 +02:00
Benoit Marty
c8773c890f Fix SQL query 2026-04-30 08:49:56 +02:00
Benoit Marty
3dd2a60b3a Do not use generic Exception 2026-04-29 22:51:54 +02:00
Benoit Marty
35a3a0ba06 Fix compilation issue. 2026-04-29 22:43:42 +02:00
Benoit Marty
8b8eedb419
Merge pull request #6681 from hughns/hughns/link-new-device-done
Improve detection of completion for Link new device flow
2026-04-29 22:32:22 +02:00
Benoit Marty
8512279d96 Add a cache for Element .well-known file. 2026-04-29 22:29:41 +02:00
Benoit Marty
29e0a08dd9 Add a CacheStore module. 2026-04-29 22:29:41 +02:00
Benoit Marty
c1e908a8e6 Fix tests. 2026-04-29 22:29:41 +02:00
Benoit Marty
a76b55e580 Add a way to tweak MAS url. 2026-04-29 22:29:40 +02:00
renovate[bot]
da36323006
Update dependency androidx.compose:compose-bom to v2026.04.01 2026-04-29 20:27:57 +00:00
renovate[bot]
7940b8f865
Update dependency io.sentry:sentry-android to v8.40.0 2026-04-29 16:11:36 +00:00
Timo
6ba4679908
Change native back button behavior in EC view (close settings in EC with os native back) (#6642)
* Change native back button behavior in EC view:
 - inject escape into webview instead of going back.
 - the webview will call back when no other modal is open.

* call down and up in the webview + make sure that we fall back to close
pip in case the webview did not handle the esc action.

---------

Co-authored-by: Jorge Martín <jorgem@element.io>
2026-04-29 18:08:33 +02:00
Valere
66f68fdccb fixup test compilation 2026-04-29 17:27:51 +02:00
Valere
531d9b3d47 fix: consist, use sealed interface instead of class 2026-04-29 16:29:13 +02:00
Valere
6154816796 cleanup of the RTCNotificationState enum 2026-04-29 16:21:36 +02:00
Valere
a6622c6787 fix deteckt | unneeded paranthesis 2026-04-29 16:03:19 +02:00
Valere
f0dc4eeace feat: Update call started timeline item + declined support 2026-04-29 15:43:58 +02:00
Hugh Nimmo-Smith
026c448e48
Merge branch 'develop' into hughns/link-new-device-done 2026-04-29 12:06:40 +01:00
Hugh Nimmo-Smith
3b21d698ee Fix tests
Co-Authored-By: Benoit Marty <3940906+bmarty@users.noreply.github.com>
2026-04-29 12:05:16 +01:00
Jorge Martin Espinosa
d215354e64
Remove legacy mx-reply from toPlainText formatted event contents (#6683) 2026-04-29 10:54:03 +00:00
Benoit Marty
1eeabb1e64
Merge pull request #6688 from element-hq/feature/bma/errorAlreadySignedIn
[Link new device] Add missing error case "already signed in"
2026-04-29 12:42:41 +02:00
ElementBot
6de6e13f25 Update screenshots 2026-04-29 10:06:55 +00:00
Jorge Martin Espinosa
367995303d
Rename OIDC components and variables to OAuth (#6686)
* Rename `OIDC` components and variables to `OAuth`. This matches the new behavior in the SDK.
2026-04-29 11:41:47 +02:00
Benoit Marty
c750fd102f Increase title and subtitle vertical padding on FlowStepPage. 2026-04-29 11:33:45 +02:00
Benoit Marty
083fc5c5fb [Link new device] Add missing screen for the error case OtherDeviceAlreadySignedIn
Closes #6678
2026-04-29 11:33:44 +02:00
Benoit Marty
7dd81dc838
Merge pull request #6677 from hughns/hughns/qr-grant-error-mapping
Update error mappings for Link new device flow
2026-04-29 11:33:01 +02:00
Benoit Marty
1d03cbfc06
Fix quality issues and formatting 2026-04-29 10:34:50 +02:00
Benoit Marty
3fd2f0991a
Merge pull request #6670 from bxdxnn/fix/room-summary-html
Strip formatting from media captions in room summary
2026-04-29 09:40:30 +02:00
Hugh Nimmo-Smith
7d27ff3c59
Merge branch 'develop' into hughns/qr-grant-error-mapping 2026-04-28 17:04:08 +01:00
Benoit Marty
7080cf77e6
Merge pull request #6680 from element-hq/feature/bma/qrCodeFix
[Link new device] Add missing screen to render digits that the user has to type on the other device
2026-04-28 16:59:16 +02:00
Hugh Nimmo-Smith
fd69bbc57a Delint 2026-04-28 15:42:24 +01:00
Hugh Nimmo-Smith
723b7486bc Improve detection of completion for Link new device flow
The SDK emits a Done progress once complete, but our listener might have been deallocated before receiving the done.
2026-04-28 15:19:03 +01:00
renovate[bot]
997227b020
Update dependency org.matrix.rustcomponents:sdk-android to v26.04.27 (#6666)
* Update dependency org.matrix.rustcomponents:sdk-android to v26.04.27

* Fix breaking API changes:

- OIDC components are now prefixed `OAuth`.
- `Room.startLiveLocationShare` now returns the event id of the beacon state event if it succeeds.
- `RoomInfo` now contains an `activeServiceMembersCount` property.

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jorge Martín <jorgem@element.io>
2026-04-28 14:49:37 +02:00
Jorge Martin Espinosa
90ed385745
Fix record screenshots action permissions (#6679) 2026-04-28 11:17:57 +00:00
ElementBot
cd61c6b3f0 Update screenshots 2026-04-28 11:10:56 +00:00
Hugh Nimmo-Smith
5c9d8d917c Update error mappings for Link new device 2026-04-28 11:22:25 +01:00
Benoit Marty
5508442222 [Link new device] Implement code confirmation screen. 2026-04-28 12:04:57 +02:00
renovate[bot]
5e8db74003
Update dependencyAnalysis to v3.9.0 (#6657)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-28 11:16:50 +02:00
bxdxnn
fff27ff3f1 Strip formatting from media captions in room summary 2026-04-28 08:27:28 +00:00
Benoit Marty
3878ce000c
Ensure that bottom sheet can scroll (#6661)
* RoomListDeclineInviteMenu: limit room name

Improve preview.
Closes #6659

* Ensure that all the ModalBottomSheet can scroll.

* Update screenshots

* Rename Preview.

* Preview the whole bottom sheet.

* Remove obsolete comment.

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
2026-04-28 09:43:41 +02:00
renovate[bot]
5bb0b92eeb
Update dependency io.element.android:element-call-embedded to v0.19.2 (#6662)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-27 17:36:41 +02:00
Benoit Marty
4bb33fc36a Use test extension on presenters. 2026-04-27 17:06:49 +02:00
Benoit Marty
944d8965f6 PictureInPictureEvents -> PictureInPictureEvent 2026-04-27 17:04:49 +02:00
Benoit Marty
2bcf10dd0b CallScreenEvents -> CallScreenEvent 2026-04-27 17:04:12 +02:00
Benoit Marty
8860647477 Element Call: remove support for SPA call links.
Closes #6578
2026-04-27 17:01:08 +02:00
ElementBot
21cae089ae Update screenshots 2026-04-27 13:39:42 +00:00
Benoit Marty
dca2dba938 Remove obsolete comment. 2026-04-27 15:16:42 +02:00
Benoit Marty
e289ec2af7 Preview the whole bottom sheet. 2026-04-27 15:15:23 +02:00
Benoit Marty
a35236b8b0 Rename Preview. 2026-04-27 15:12:23 +02:00
ElementBot
0d078a41ca Update screenshots 2026-04-27 08:40:35 +00:00
Benoit Marty
09ff3294d5 Ensure that all the ModalBottomSheet can scroll. 2026-04-27 10:23:56 +02:00
Benoit Marty
bf57223d05 RoomListDeclineInviteMenu: limit room name
Improve preview.
Closes #6659
2026-04-27 09:49:09 +02:00
renovate[bot]
14054344f4
Update dependency org.jsoup:jsoup to v1.22.2 2026-04-27 07:20:07 +00:00
ElementBot
9a2ad3928a
Merge pull request #6658 from element-hq/sync-localazy
Sync Strings
2026-04-27 09:17:54 +02:00
Benoit Marty
3c51732d35
Merge pull request #6643 from element-hq/feature/bma/updateMediaViewer
Update media viewer UI
2026-04-24 17:48:38 +02:00
ElementBot
22ef9f1a5e Update screenshots 2026-04-24 15:12:13 +00:00
Benoit Marty
fb50fce649 Ensure preview has a date 2026-04-24 16:53:53 +02:00
Benoit Marty
c283f0109b a11y: add heading to the title. 2026-04-24 16:16:15 +02:00
Benoit Marty
10c9112e03
Merge pull request #6651 from element-hq/feature/bma/mention-pill-cut-off
Mention pill cut off
2026-04-24 16:12:38 +02:00
ElementBot
5929806137 Update screenshots 2026-04-24 13:34:34 +00:00
Benoit Marty
de7b4002d8 MediaDetailsBottomSheet: update wording. 2026-04-24 15:16:26 +02:00
ElementBot
a276e67ce6 Update screenshots 2026-04-24 12:38:13 +00:00
Benoit Marty
1c229e7229 Replace " - " by " • " as it renders better. 2026-04-24 14:28:52 +02:00
renovate[bot]
112dc93fff
Update dependency io.sentry:sentry-android to v8.39.1 (#6648)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-24 14:23:26 +02:00
Kurban Sagitov
3379c61ad1
PR:Fix mention pill cut off (#6622)
* Fix mention pill cut off

* trigger CI after screenshots

* trigger CI after screenshots
2026-04-24 14:20:24 +02:00
Richard van der Hoff
289dfff50a
Promote "history sharing on invite" out of developer options (#6647)
* Enable history sharing by default, unconditionally

* Remove feature-flag dep from history viz icons in room header

* Remove feature-flag dep from warning on inviting new people

* Remove feature-flag dep from warning on starting chat with new people

* Remove `enableKeyShareOnInvite` feature flag

* Update screenshots

* Remove redundant `FakeFeatureFlagService()` invocation, per review comment

---------

Co-authored-by: ElementBot <android@element.io>
2026-04-24 10:52:21 +00:00
Jorge Martin Espinosa
92ee479e91
Set max lines for 'in reply to' view conditionally (#6612)
* Set max lines for 'in reply to' view conditionally. When there is enough screen space, use 2 lines as before. If the screen space is limited, use a single one.

* Reduce vertical padding for reply-to view in compose

* Add screenshot test with single line in reply to view

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
2026-04-23 15:15:52 +02:00
ElementBot
e37083669e Update screenshots 2026-04-23 09:40:14 +00:00
Benoit Marty
09e0d2d166 MediaDetailsBottomSheet: Add missing preview case. 2026-04-23 11:22:47 +02:00
Benoit Marty
bbb4a47eff MediaDetailsBottomSheet: iterate on design.
Closes #6645
2026-04-23 11:20:20 +02:00
Valere Fedronic
e036250539
Merge pull request #6636 from element-hq/feature/valere/fix_build_sdk_script_macos
devx: fix build sdk script options for macos
2026-04-23 10:12:04 +02:00
renovate[bot]
9dd61bbd37
fix(deps): update camera to v1.6.0 (#6514)
* fix(deps): update camera to v1.6.0

* Add dependency to com.google.guava:guava to fix compilation issue.

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Benoit Marty <benoit@matrix.org>
2026-04-23 09:48:15 +02:00
renovate[bot]
2c6f1888f4
Update dependency io.sentry:sentry-android to v8.38.0 (#6597)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-23 09:47:17 +02:00
renovate[bot]
d9080f5465
Update zizmorcore/zizmor-action action to v0.5.3 (#6630)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-23 09:46:37 +02:00
Jorge Martin Espinosa
b4f1627748
Remove distributed tracing of the 'timeline loading' flow (#6644)
* Remove distributed tracing of the 'timeline loading' flow. This is causing crashes in the app when a debug SDK build is used

* Discourage using the APIs related with distributed tracing, explaining the problem
2026-04-22 17:51:51 +02:00
ElementBot
92add85883 Update screenshots 2026-04-22 13:54:26 +00:00
Benoit Marty
b12a9ff2b9 Improve rendering when sender does not have a display name. 2026-04-22 14:57:18 +02:00
Benoit Marty
7bbdecc7a8 Rename sub classes of MediaBottomSheetState and improve preview of MediaDeleteConfirmationBottomSheet 2026-04-22 14:42:56 +02:00
ElementBot
c9b48d32b0 Update screenshots 2026-04-22 10:40:25 +00:00
Benoit Marty
c1678f22e6 Add more preview for MediaDetailsBottomSheetPreview 2026-04-22 12:21:20 +02:00
Benoit Marty
769341e4f2 Remove some dividers. 2026-04-22 12:16:52 +02:00
Benoit Marty
630b0c8098 Improve and add test 2026-04-22 12:07:56 +02:00
Benoit Marty
1e39736797 MediaGalleryEvents -> MediaGalleryEvent 2026-04-22 11:01:36 +02:00
Benoit Marty
83b4bfad96 Move "Open with" action to bottom sheet 2026-04-22 10:59:21 +02:00
ganfra
61a8d06a6a
Merge pull request #6594 from element-hq/renovate/io.nlopez.compose.rules-detekt-0.x
Update dependency io.nlopez.compose.rules:detekt to v0.5.7
2026-04-22 10:46:03 +02:00
renovate[bot]
3199a7dd8b
Update dependency io.nlopez.compose.rules:detekt to v0.5.7 2026-04-21 20:06:08 +00:00
Jorge Martín
24b24e511a Changelog for version 26.04.4 2026-04-21 17:37:15 +02:00
Jorge Martín
0224c7e2a5 Adding fastlane file for version 26.04.4 2026-04-21 17:37:15 +02:00
Jorge Martín
61c68f8d4a Setting version for the release 26.04.4 2026-04-21 17:37:15 +02:00
Benoit Marty
a0632b216c MediaDetailsBottomSheet: update wording and dividers. 2026-04-21 16:53:22 +02:00
Jorge Martin Espinosa
1e04a7345f
Add flag for automatic back pagination feature (#6637) 2026-04-21 14:42:44 +00:00
Jorge Martín
91d265e6bf Merge branch 'release/26.04.4' 2026-04-21 16:40:31 +02:00
Jorge Martín
d38ef5ba10 Adding fastlane file for version 26.04.4 2026-04-21 16:40:30 +02:00
Jorge Martín
5d270068d6 Setting version for the release 26.04.4 2026-04-21 16:38:53 +02:00
Benoit Marty
79afb1d9e0 MediaViewer: add Save action as a main action. 2026-04-21 16:21:01 +02:00
Benoit Marty
97ae775df5 MediaViewer: add Share action as a main action. 2026-04-21 16:14:07 +02:00
Jorge Martín
f6ed0a645a Add flag for automatic back pagination feature 2026-04-21 15:56:38 +02:00
renovate[bot]
75db550ca8
Update dependency org.matrix.rustcomponents:sdk-android to v26.04.21 (#6635)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-21 13:55:20 +00:00
Benoit Marty
54efb46294 MediaViewerEvents -> MediaViewerEvent 2026-04-21 15:50:59 +02:00
Benoit Marty
f3dd7b86d8
Merge pull request #6633 from element-hq/feature/bma/deactivateStrings
Update wording of deactivate account screen
2026-04-21 15:34:23 +02:00
Valere
283fd2970a devx: fix build sdk script options for macos 2026-04-21 15:34:01 +02:00
Jorge Martin Espinosa
2d4ed98738
Fix media viewer bottom sheets not being scrollable (#6631)
Both the info and confirm deletion bottom sheets were not scrollable, which made them useless on devices with low resolution or landscape orientation in most devices
2026-04-21 15:30:50 +02:00
Benoit Marty
a662a5a045 Use new action_delete and action_delete_account 2026-04-21 14:57:33 +02:00
Valere Fedronic
7f198155f2
Merge pull request #6627 from element-hq/feature/valere/call/fix_proximity_bug
Fix | When selecting earpiece twice in a row the proximity sensor get wrongly disabled
2026-04-21 13:52:32 +02:00
mxandreas
064c606b53
Updates to new features and some refactoring. (#6591)
* Updates to new features and some refactoring.

* Update CONTRIBUTING.md

* Typo fixes.

* Some final touches.

* Update the table of contents in `CONTRIBUTING.md`

---------

Co-authored-by: bxdxnn <267911624+bxdxnn@users.noreply.github.com>
Co-authored-by: Jorge Martín <jorgem@element.io>
2026-04-21 10:53:01 +00:00
Valere Fedronic
b080c01388
Merge pull request #6603 from element-hq/valere/call/fix_join_button_on_several_items
cleaning: Remove join button from call notify timelineItemView
2026-04-21 11:56:38 +02:00
Valere Fedronic
66ed52dee8
Merge pull request #6609 from element-hq/feature/valere/call/clean_up_call_controls
audio: Let EC decide alone what communication device to use
2026-04-21 11:53:29 +02:00
ElementBot
c343df8351 Update screenshots 2026-04-21 09:50:46 +00:00
Valere
434e592fed Merge branch 'develop' into feature/valere/call/clean_up_call_controls 2026-04-21 11:36:44 +02:00
Benoit Marty
168782e049 Update deactivate account wording. Closes #6608 2026-04-21 11:35:26 +02:00
ElementBot
3c06e0a260 Update screenshots 2026-04-21 09:26:51 +00:00
Benoit Marty
8eb5a55673 Introduce simplePluralStringResource methods, as Composable and in StringProvider. 2026-04-21 11:22:43 +02:00
Valere
af47e2b405 Merge branch 'develop' into valere/call/fix_join_button_on_several_items 2026-04-21 11:05:14 +02:00
Valere
e158be86aa review: Make call intent not optional in content 2026-04-21 11:03:48 +02:00
Valere
b0e9073efb fix test compilation 2026-04-21 10:49:08 +02:00
ganfra
e5f85592d9
Merge pull request #6611 from element-hq/feature/fga/live_location_rendering
WIP : live location rendering
2026-04-20 19:46:13 +02:00
renovate[bot]
96cbe7664b
Update dependencyAnalysis to v3.7.0 (#6616)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-20 19:20:26 +02:00
ganfra
51733ec6fe Merge branch 'develop' into feature/fga/live_location_rendering 2026-04-20 19:12:21 +02:00
ElementBot
e1b7171839
Sync Strings (#6626)
* Sync Strings from Localazy

* Use the previous plurals as plain strings

* Add extra case for 1 vs multiple users

* Update screenshots

---------

Co-authored-by: Jorge Martín <jorgem@element.io>
2026-04-20 17:09:10 +00:00
Jorge Martin Espinosa
f80a140cf9
Cleanup FetchPushForegroundService (#6577)
* Rename `PushHandlingWakeLock` to `FetchPushForegroundServiceManager`. Move the start/stop logic from `FetchPushForegroundService.Companion` to it.

* Add more tests using Robolectric.

* Remove `FeatureFlags.SyncNotificationsWithWorkManager` and associated code: this should have been removed in one of the previous refactors, since we don't have the 2 ways to sync notifications anymore, everything uses the `WorkManager`

---------

Co-authored-by: Benoit Marty <benoit@matrix.org>
2026-04-20 16:03:12 +02:00
ganfra
8853f160e2
Merge pull request #6615 from element-hq/renovate/peter-evans-create-pull-request-8.x
Update peter-evans/create-pull-request action to v8.1.1
2026-04-20 15:50:06 +02:00
ganfra
05e1048d20
Merge pull request #6606 from element-hq/renovate/actions-github-script-9.x
Update actions/github-script action to v9
2026-04-20 15:44:52 +02:00
bxdxnn
8611b9c690
Fix crash when going back to threads list (#6620)
* Fix crash when going back to threads list
2026-04-20 15:36:31 +02:00
ganfra
c35b5811d0 Restore comment on StaticMapView 2026-04-20 15:14:10 +02:00
renovate[bot]
447f4e5134
Update plugin dependencycheck to v12.2.1 (#6621)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-20 14:22:49 +02:00
renovate[bot]
f7eaedf805
Merge pull request #6614 from element-hq/renovate/actions-upload-artifact-7.x
Update actions/upload-artifact action to v7.0.1
2026-04-20 14:22:22 +02:00
Valere
18d4924171 fix: proximity sensor was wrongly disabled 2026-04-20 11:24:38 +02:00
Valere
58bde16b81 enforce selecting the EC preferred device 2026-04-20 11:19:37 +02:00
Valere
09e57e077a audio: Let EC decide alone what communication device to use 2026-04-20 11:19:37 +02:00
renovate[bot]
09720a01c9
Update peter-evans/create-pull-request action to v8.1.1 2026-04-17 18:50:20 +00:00
ganfra
41f86b492c Fix test after new property in LiveLocationShare 2026-04-17 16:58:06 +02:00
ElementBot
5fb0b3a93d Update screenshots 2026-04-17 13:45:30 +00:00
Benoit Marty
ae94e0c746
Merge pull request #6441 from timurgilfanov/feature-oled-black
Add Black theme option for battery saving on OLED displays
2026-04-17 15:41:49 +02:00
ganfra
3b813f137b Merge branch 'develop' into feature/fga/live_location_rendering 2026-04-17 15:13:13 +02:00
ganfra
8182a149d0 Live location : ensure it's not sorted randomly 2026-04-17 15:07:54 +02:00
Benoit Marty
269d367130
Merge pull request #6464 from kalix127/feat/reply-with-voice-message
Support replying to messages with voice recordings
2026-04-17 14:49:04 +02:00
Jorge Martin Espinosa
a341a1a59e
Replace rustls-platform-verifier-android.aar with single class (#6610)
* Replace the `rustls-platform-verifier-android.aar` with the actual source code

* Exclude the platform-verifier code from linters

* Add manual update instructions

* Exclude from Kover too
2026-04-17 14:48:50 +02:00
Benoit Marty
4e5542396f
Merge branch 'develop' into feature-oled-black 2026-04-17 14:47:15 +02:00
Valere
d7963ddb5a fixup: missing param 2026-04-17 12:52:25 +02:00
bxdxnn
6a4fed2baf
Natural media viewer swiping order (#6431) 2026-04-17 11:30:21 +02:00
Benoit Marty
47a430978f
Merge pull request #6602 from element-hq/feature/bma/updateSettingsUI
Settings UI update.
2026-04-17 11:30:08 +02:00
Valere
98ececf3d0 Merge branch 'develop' into valere/call/fix_join_button_on_several_items 2026-04-17 10:10:04 +02:00
Valere
6d134375f6 review: pass the RtcNotificationContent in the view to avoid casting 2026-04-17 10:07:22 +02:00
ElementBot
efe76281ad Update screenshots 2026-04-17 09:49:59 +02:00
Benoit Marty
a1c9994385 Settings UI update.
- Reorder items
- Minor UI update
- Improve the previews of the Composable
- Merge manage account and manage devices
- Add missing tests
2026-04-17 09:49:59 +02:00
renovate[bot]
f661ccf25a
Update dependency com.google.firebase:firebase-bom to v34.12.0 (#6604)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-17 09:43:32 +02:00
renovate[bot]
5b3f91ff78
Update dependency org.jetbrains.kotlinx:kotlinx-serialization-json to v1.11.0 (#6605)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-17 09:01:20 +02:00
renovate[bot]
555d7acbd3
Update actions/github-script action to v9 2026-04-17 00:03:24 +00:00
ElementBot
2966ccc96d Update screenshots 2026-04-16 15:25:42 +00:00
Valere
3f9b8b822e cleaning: Remove join button from call notify timelineItemView 2026-04-16 16:35:28 +02:00
ganfra
fbfeeae084 Fix formatting 2026-04-16 16:18:53 +02:00
ganfra
6b933b6506 Use "Shared live location" in formatter 2026-04-16 15:52:11 +02:00
ganfra
704ddc9132 Update live location shares when reaching timeout (before actual stop event) 2026-04-16 15:33:42 +02:00
Gianluca Iavicoli
67729f87c9
test: stabilize reply event ID test for voice message composer 2026-04-15 18:17:31 +02:00
ganfra
11866afb03 Remove hardcoded strings 2026-04-15 13:55:54 +02:00
ganfra
f5683f9c8b Improve live location bottomsheet interaction with map 2026-04-15 13:46:31 +02:00
Timur Gilfanov
f7cfa6cda8
Merge branch 'develop' into feature-oled-black 2026-04-14 10:13:46 +04:00
ganfra
e85b532d6a Merge branch 'develop' into feature/fga/live_location_rendering 2026-04-13 22:38:54 +02:00
ganfra
580e85d232 Fix live location share item description 2026-04-13 11:51:48 +02:00
ganfra
537063d899 Add focused location tracking when opening the map 2026-04-10 21:11:30 +02:00
ganfra
0e9af5f42a Refactor live location shares to use callbackFlow 2026-04-10 20:45:18 +02:00
ganfra
7c3b9523df Improve live location UI with empty state 2026-04-10 20:44:05 +02:00
ganfra
9ba8798175 Refactor LiveLocationShare to include structured LastLocation 2026-04-10 14:43:24 +02:00
ganfra
d6999b5334 Merge branch 'develop' into feature/fga/live_location_rendering 2026-04-10 09:50:44 +02:00
Timur Gilfanov
fd92a2eef2 Remove default value for allowBlackTheme in mapToTheme 2026-04-05 12:34:25 +04:00
Timur Gilfanov
43bef7f1df Add isBlackThemeAllowed as a key to theme remember block 2026-04-05 12:34:07 +04:00
Timur Gilfanov
f19295d63d
Merge branch 'develop' into feature-oled-black 2026-04-05 12:06:20 +04:00
Timur Gilfanov
5e6a6af409 Add "Allow black theme" feature flag 2026-04-05 12:03:50 +04:00
ganfra
4e0165458a Live location : start collecting live location 2026-04-03 18:21:37 +02:00
Gianluca Iavicoli
4586ee31ea
fix: re-focus text input after voice recording ends 2026-04-02 22:20:13 +02:00
Gianluca Iavicoli
9a81ec5569
refactor: remove keyboard dismissal logic during voice recording 2026-04-02 22:14:04 +02:00
Timur Gilfanov
104ae4752a Reorder imports to align with static analysis 2026-04-02 18:24:23 +04:00
Timur Gilfanov
d0dcbab750
Merge branch 'develop' into feature-oled-black 2026-03-30 11:08:53 +04:00
Gianluca Iavicoli
37da07a720
Revert unnecessary analytics refactor in voice message presenter 2026-03-27 21:18:55 +01:00
Timur Gilfanov
55d043788b Reorder theme options in AdvancedSettingsState 2026-03-26 09:11:05 +04:00
ganfra
a7e254cc84 Live location : format the endsAt timeline item content 2026-03-25 19:57:34 +01:00
Gianluca Iavicoli
d9a54fb716
fix: persist reply banner during voice recording and dismiss keyboard 2026-03-25 00:58:57 +01:00
Gianluca Iavicoli
4a5662a5e2
fix: reset composer mode after sending voice message reply 2026-03-25 00:55:57 +01:00
Gianluca Iavicoli
bf7ab31517
feat: support sending voice messages as replies 2026-03-25 00:53:57 +01:00
ganfra
b082f59f9c Start implementing LLS timeline item 2026-03-24 16:38:12 +01:00
ganfra
a22c9871e3 Map sdk timeline item for LiveLocation 2026-03-24 15:50:06 +01:00
ElementBot
cea003a929 Update screenshots 2026-03-23 12:23:50 +00:00
Timur Gilfanov
a96c146d30 Introduce "Black" theme and refactor theme handling
* Add `Theme.Black` to the `Theme` enum and update `isDark()` to include it.
* Refactor `ElementTheme` to accept a `Theme` object instead of a `darkTheme` boolean, allowing for more specific color mapping (e.g., setting `bgCanvasDefault` to `Color.Black` for the Black theme).
* Update `AdvancedSettingsPresenter` and `AdvancedSettingsState` to include "Black" as a user-selectable theme option.
* Adjust `ElementThemeApp` and `MaterialTextPreview` to handle the expanded theme selection.
* Add `ElementPreviewBlack` and update existing preview components to support the new theme.
* Update Konsist tests to ensure `@PreviewsDayNight` annotated functions don't have "BlackPreview" suffix.
2026-03-23 15:26:16 +04:00
2471 changed files with 28166 additions and 26919 deletions

View file

@ -1,40 +0,0 @@
# .forgejo/workflows/gitleaks.yml
#
# Sulkta canonical gitleaks workflow. Drop a copy into every public repo at
# `.forgejo/workflows/gitleaks.yml` after the Forgejo act_runner is registered
# (task #295).
#
# Pairs with the pre-receive hook installed on every bare repo — that one is
# the strict enforcement layer (rejects the push); this one provides the
# per-PR red ✗ that branch-protection rules can require before merge.
#
# Layer 1 (this workflow): visible per-PR status, can be a required check.
# Layer 2 (pre-receive hook): strict enforcement at the server.
# Layer 3 (johnny5 cron sweep): nightly full-history sweep across all repos.
name: gitleaks
on:
push:
pull_request:
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
# Full history — gitleaks needs depth to scan a commit range.
fetch-depth: 0
- name: install gitleaks
run: |
curl -sSL -o gl.tar.gz \
https://github.com/gitleaks/gitleaks/releases/download/v8.21.2/gitleaks_8.21.2_linux_x64.tar.gz
tar xzf gl.tar.gz gitleaks
chmod +x gitleaks
./gitleaks version
- name: scan
run: |
./gitleaks detect --source . --no-banner --redact --verbose

View file

@ -1,116 +0,0 @@
name: Upstream sync
# Daily check against the upstream mirror. Fast-forwards `main` to
# `upstream/develop` when upstream has advanced, then pings the Infra
# Matrix room so we know the wallet branch is due for a rebase.
#
# See SYNC.md on the wallet branch for the full topology + procedure
# this job implements.
on:
schedule:
# 12:00 UTC daily — quiet time for all our time zones, avoids the
# morning-meeting window where an unexpected Matrix ping is noise.
- cron: '0 12 * * *'
workflow_dispatch: # manual trigger from the Actions UI too
jobs:
sync-main:
runs-on: ubuntu-latest
env:
# The repo's .gitattributes (inherited from upstream) routes the
# screenshots/ tree through git-lfs. Gitea's LFS store doesn't hold
# those blobs, so on checkout the smudge filter tries to 404-download
# them and wedges git state for subsequent fetches. We don't need
# the image bytes here — leave LFS pointers as-is.
GIT_LFS_SKIP_SMUDGE: '1'
steps:
- name: Checkout main
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
lfs: false
# Gitea's built-in GITEA_TOKEN is read-only by default.
# GIT_PUSH_TOKEN is a repo secret with a write-scoped PAT, so
# the subsequent `git push origin main` actually lands.
token: ${{ secrets.GIT_PUSH_TOKEN }}
- name: Fetch upstream + wallet
run: |
set -euo pipefail
# Fetch directly from GitHub. We also have a Gitea pull-mirror
# at Sulkta-Coop/element-x-upstream that tracks this same repo,
# but sourcing from GitHub keeps the workflow independent of
# the mirror's health — one less moving part to diagnose.
git remote add upstream https://github.com/element-hq/element-x-android.git
git fetch --depth=500 upstream develop
git fetch origin wallet:refs/remotes/origin/wallet
- name: Fast-forward main
id: ff
run: |
set -euo pipefail
git config user.name "sulkta-bot"
git config user.email "bot@sulkta.com"
# git-lfs pre-push hook refuses incomplete pushes — which triggers
# here because we skipped LFS smudge on checkout, so local LFS
# objects are absent. We're only pushing branch pointers (no new
# LFS content), so allow incomplete.
git config lfs.allowincompletepush true
OLD=$(git rev-parse --short HEAD)
echo "main was at $OLD"
if git merge --ff-only upstream/develop; then
NEW=$(git rev-parse --short HEAD)
if [ "$OLD" = "$NEW" ]; then
echo "main already up to date with upstream/develop"
echo "advanced=false" >> "$GITHUB_OUTPUT"
else
echo "main advanced: $OLD -> $NEW"
git push origin main
echo "advanced=true" >> "$GITHUB_OUTPUT"
echo "old=$OLD" >> "$GITHUB_OUTPUT"
echo "new=$NEW" >> "$GITHUB_OUTPUT"
fi
else
echo "::warning::main could not fast-forward to upstream/develop — someone committed to main directly?"
echo "advanced=false" >> "$GITHUB_OUTPUT"
fi
- name: Measure wallet drift
if: steps.ff.outputs.advanced == 'true'
id: drift
run: |
set -euo pipefail
MB=$(git merge-base refs/remotes/origin/wallet main)
BEHIND=$(git rev-list --count "$MB..main")
NEW_ADDED=$(git rev-list --count "$MB..upstream/develop")
echo "behind=$BEHIND" >> "$GITHUB_OUTPUT"
echo "new_added=$NEW_ADDED" >> "$GITHUB_OUTPUT"
echo "wallet is $BEHIND commits behind main now; $NEW_ADDED new upstream commits this run"
- name: Matrix notification (Infra room)
# Best-effort — if the target bot isn't in the room or Matrix is
# flapping, don't fail the whole run. The advance + push is the
# critical path; notify is a convenience ping.
if: steps.ff.outputs.advanced == 'true'
continue-on-error: true
env:
MATRIX_TOKEN: ${{ secrets.MATRIX_HOUSE_BOT_TOKEN }}
run: |
set -euo pipefail
TXN=$(date +%s%N)
ROOM='!rvxiUrWpgvMTAwzjGm:sulkta.com' # Infra
BODY="element-x upstream advanced · main ${{ steps.ff.outputs.old }} → ${{ steps.ff.outputs.new }} (${{ steps.drift.outputs.new_added }} commits). wallet is ${{ steps.drift.outputs.behind }} commits behind — rebase before next build."
# jq keeps the body properly JSON-escaped; safer than shell interp
# shellcheck disable=SC2086
PAYLOAD=$(printf '%s' "$BODY" | jq -Rs '{msgtype: "m.text", body: .}')
curl --fail -s -X PUT \
-H "Authorization: Bearer $MATRIX_TOKEN" \
-H "Content-Type: application/json" \
"https://chat.sulkta.com/_matrix/client/v3/rooms/${ROOM}/send/m.room.message/${TXN}" \
-d "$PAYLOAD"
echo "notified"

View file

@ -34,6 +34,13 @@
"/^org.jetbrains.kotlinx:kotlinx-datetime/", "/^org.jetbrains.kotlinx:kotlinx-datetime/",
], ],
}, },
{
// Keep Guava on the Android variant and ignore jre-only upgrades.
"matchPackageNames": [
"com.google.guava:guava",
],
"allowedVersions": "/-android$/",
},
{ {
// Limit PostHog Android upgrade to one PR per month, the first day of the month // Limit PostHog Android upgrade to one PR per month, the first day of the month
"matchPackageNames": [ "matchPackageNames": [

110
.github/workflows/build.yml vendored Normal file
View file

@ -0,0 +1,110 @@
name: APK Build
on:
workflow_dispatch:
pull_request:
merge_group:
push:
branches: [ develop ]
permissions: {}
# 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
CI_GRADLE_ARG_PROPERTIES: --stacktrace -Dsonar.gradle.skipCompile=true --no-configuration-cache
jobs:
build:
name: Build APKs
runs-on: ubuntu-latest
permissions:
# For NejcZdovc/comment-pr
pull-requests: write
strategy:
matrix:
variant: [debug, release, nightly]
fail-fast: false
# Allow all jobs on develop. Just one per PR.
concurrency:
group: ${{ github.ref == 'refs/heads/develop' && format('build-develop-{0}-{1}', matrix.variant, github.sha) || format('build-{0}-{1}', matrix.variant, github.ref) }}
cancel-in-progress: true
steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
tool-cache: true
# All of these default to true, but we should only need the 'android' one (and maybe swap-storage?)
android: false
dotnet: true
haskell: true
# This takes way too long to run (~2 minutes) and it saves only ~5.5GB
large-packages: false
docker-images: true
swap-storage: false
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
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@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Assemble debug APKs
if: ${{ matrix.variant == 'debug' }}
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 }}
ELEMENT_ANDROID_SENTRY_DSN: ${{ secrets.ELEMENT_ANDROID_SENTRY_DSN }}
ELEMENT_SDK_SENTRY_DSN: ${{ secrets.ELEMENT_SDK_SENTRY_DSN }}
ELEMENT_CALL_SENTRY_DSN: ${{ secrets.ELEMENT_CALL_SENTRY_DSN }}
ELEMENT_CALL_POSTHOG_API_HOST: ${{ secrets.ELEMENT_CALL_POSTHOG_API_HOST }}
ELEMENT_CALL_POSTHOG_API_KEY: ${{ secrets.ELEMENT_CALL_POSTHOG_API_KEY }}
ELEMENT_CALL_RAGESHAKE_URL: ${{ secrets.ELEMENT_CALL_RAGESHAKE_URL }}
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
with:
name: elementx-debug
path: |
app/build/outputs/apk/gplay/debug/*-universal-debug.apk
app/build/outputs/apk/fdroid/debug/*-universal-debug.apk
- uses: rnkdsh/action-upload-diawi@4e1421305be7cfc510d05f47850262eeaf345108 # v1.5.12
id: diawi
# Do not fail the whole build if Diawi upload fails
continue-on-error: true
env:
token: ${{ secrets.DIAWI_TOKEN }}
if: ${{ matrix.variant == 'debug' && github.event_name == 'pull_request' && env.token != '' }}
with:
token: ${{ env.token }}
file: app/build/outputs/apk/gplay/debug/app-gplay-arm64-v8a-debug.apk
- name: Add or update PR comment with QR Code to download APK.
if: ${{ matrix.variant == 'debug' && github.event_name == 'pull_request' && steps.diawi.conclusion == 'success' }}
uses: NejcZdovc/comment-pr@a423635d183a8259308e80593c96fecf31539c26 # v2.1.0
with:
message: |
:iphone: Scan the QR code below to install the build (arm64 only) for this PR.
![QR code](${{ steps.diawi.outputs['qrcode'] }})
If you can't scan the QR code you can install the build via this link: ${{ steps.diawi.outputs['url'] }}
# Enables to identify and update existing Ad-hoc release message on new commit in the PR
identifier: "GITHUB_COMMENT_QR_CODE"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Compile release sources
if: ${{ matrix.variant == 'release' }}
run: ./gradlew bundleGplayRelease -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES
- name: Compile nightly sources
if: ${{ matrix.variant == 'nightly' }}
run: ./gradlew compileGplayNightlySources -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES

92
.github/workflows/build_enterprise.yml vendored Normal file
View file

@ -0,0 +1,92 @@
name: Enterprise APK Build
on:
workflow_dispatch:
pull_request:
merge_group:
push:
branches: [ develop ]
permissions: {}
# 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
CI_GRADLE_ARG_PROPERTIES: --stacktrace -Dsonar.gradle.skipCompile=true --no-configuration-cache
jobs:
build:
name: Build Enterprise APKs
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' }}
strategy:
matrix:
variant: [debug, release, nightly]
fail-fast: false
# Allow all jobs on develop. Just one per PR.
concurrency:
group: ${{ github.ref == 'refs/heads/develop' && format('build-develop-enterprise-{0}-{1}', matrix.variant, github.sha) || format('build-enterprise-{0}-{1}', matrix.variant, github.ref) }}
cancel-in-progress: true
steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
tool-cache: true
# All of these default to true, but we should only need the 'android' one (and maybe swap-storage?)
android: false
dotnet: true
haskell: true
# This takes way too long to run (~2 minutes) and it saves only ~5.5GB
large-packages: false
docker-images: true
swap-storage: false
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
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: 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
run: git submodule update --init --recursive
- name: Use JDK 21
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Assemble debug Gplay Enterprise APK
if: ${{ matrix.variant == 'debug' }}
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 }}
ELEMENT_ANDROID_SENTRY_DSN: ${{ secrets.ELEMENT_ANDROID_SENTRY_DSN }}
ELEMENT_SDK_SENTRY_DSN: ${{ secrets.ELEMENT_SDK_SENTRY_DSN }}
ELEMENT_CALL_SENTRY_DSN: ${{ secrets.ELEMENT_CALL_SENTRY_DSN }}
ELEMENT_CALL_POSTHOG_API_HOST: ${{ secrets.ELEMENT_CALL_POSTHOG_API_HOST }}
ELEMENT_CALL_POSTHOG_API_KEY: ${{ secrets.ELEMENT_CALL_POSTHOG_API_KEY }}
ELEMENT_CALL_RAGESHAKE_URL: ${{ secrets.ELEMENT_CALL_RAGESHAKE_URL }}
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
with:
name: elementx-enterprise-debug
path: |
app/build/outputs/apk/gplay/debug/*-universal-debug.apk
- name: Compile nightly and release sources
if: ${{ matrix.variant == 'release' }}
run: ./gradlew compileReleaseSources -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES
- name: Compile nightly sources
if: ${{ matrix.variant == 'nightly' }}
run: ./gradlew compileGplayNightlySources -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES

33
.github/workflows/danger.yml vendored Normal file
View file

@ -0,0 +1,33 @@
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
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@67ed2c1f42fd2fc198cc3c14b43c8f83351f4fe9 # 13.0.5
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 }}

38
.github/workflows/fork-pr-notice.yml vendored Normal file
View file

@ -0,0 +1,38 @@
name: Community PR notice
on:
workflow_dispatch:
pull_request_target: # zizmor: ignore[dangerous-triggers]
types:
- opened
- reopened
permissions: {}
jobs:
welcome:
runs-on: ubuntu-latest
permissions:
# Require to comment the PR.
pull-requests: write
name: Welcome comment
# Only display it if base repo (upstream) is different from HEAD repo (possibly a fork)
if: github.event.pull_request.base.repo.full_name != github.event.pull_request.head.repo.full_name
steps:
- name: Add auto-generated commit warning
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `Thank you for your contribution! Here are a few things to check in the PR to ensure it's reviewed as quickly as possible:
- If your pull request adds a feature or modifies the UI, this should have an equivalent pull request in the [Element X iOS repo](https://github.com/element-hq/element-x-ios) unless it only affects an Android-only behaviour or is behind a disabled feature flag, since we need parity in both clients to consider a feature done. It will also need to be approved by our product and design teams before being merged, so it's usually a good idea to discuss the changes in a Github issue first and then start working on them once the approach has been validated.
- Your branch should be based on \`origin/develop\`, at least when it was created.
- The title of the PR will be used for release notes, so it needs to describe the change visible to the user.
- The test pass locally running \`./gradlew test\`.
- The code quality check suite pass locally running \`./gradlew runQualityChecks\`.
- If you modified anything related to the UI, including previews, you'll have to run the \`Record screenshots\` GH action in your forked repo: that will generate compatible new screenshots. However, given Github Actions limitations, **it will prevent the CI from running temporarily**, until you upload a new commit after that one. To do so, just pull the latest changes and push [an empty commit](https://coderwall.com/p/vkdekq/git-commit-allow-empty).`
})

View file

@ -0,0 +1,42 @@
name: Generate GitHub Pages
on:
workflow_dispatch:
schedule:
# At 00:00 on every Tuesday UTC
- cron: '0 0 * * 2'
permissions: {}
jobs:
generate-github-pages:
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' }}
permissions:
contents: write
steps:
- name: ⏬ Checkout with LFS
uses: nschloe/action-cached-lfs-checkout@385a8ecc719e50b8c71af6ab01a624b486b7c3bc # v1.2.5
- name: Use JDK 21
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Set up Python 3.12
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: 3.14
- name: Run World screenshots generation script
run: |
./tools/test/generateWorldScreenshots.py
mkdir -p screenshots/en
cp tests/uitests/src/test/snapshots/images/* screenshots/en
- name: Deploy GitHub Pages
uses: peaceiris/actions-gh-pages@84c30a85c19949d7eee79c4ff27748b70285e453 # v4.1.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./screenshots

View file

@ -0,0 +1,30 @@
name: Update Gradle Wrapper
on:
workflow_dispatch:
schedule:
- cron: "0 0 * * *"
permissions: {}
jobs:
update-gradle-wrapper:
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
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'
- name: Update Gradle Wrapper
uses: gradle-update/update-gradle-wrapper-action@512b1875f3b6270828abfe77b247d5895a2da1e5 # v2.1.0
with:
repo-token: ${{ secrets.DANGER_GITHUB_API_TOKEN }}
target-branch: develop
labels: PR-Build

149
.github/workflows/maestro-local.yml vendored Normal file
View file

@ -0,0 +1,149 @@
name: Maestro (local)
# Run this flow only when APK Build workflow completes
on:
workflow_dispatch:
pull_request:
permissions: {}
# 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
CI_GRADLE_ARG_PROPERTIES: --stacktrace --no-daemon -Dsonar.gradle.skipCompile=true --no-configuration-cache
ARCH: x86_64
DEVICE: pixel_7_pro
API_LEVEL: 33
TARGET: google_apis
jobs:
build-apk:
name: Build APK
runs-on: ubuntu-latest
concurrency:
group: ${{ format('maestro-build-{0}', github.ref) }}
cancel-in-progress: true
steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
tool-cache: true
# All of these default to true, but we should only need the 'android' one (and maybe swap-storage?)
android: false
dotnet: true
haskell: true
# This takes way too long to run (~2 minutes) and it saves only ~5.5GB
large-packages: false
docker-images: true
swap-storage: false
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
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@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
name: Use JDK 21
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Assemble debug APK
run: ./gradlew :app:assembleGplayDebug $CI_GRADLE_ARG_PROPERTIES
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 }}
- name: Upload APK as artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: elementx-apk-maestro
path: |
app/build/outputs/apk/gplay/debug/app-gplay-x86_64-debug.apk
retention-days: 5
overwrite: true
if-no-files-found: error
maestro-cloud:
name: Maestro test suite
runs-on: ubuntu-latest
needs: [ build-apk ]
# Allow only one to run at a time, since they use the same environment.
# Otherwise, tests running in parallel can break each other.
concurrency:
group: maestro-test
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
if: (github.event_name == 'pull_request' && github.event.pull_request.fork == null) || github.event_name == 'workflow_dispatch'
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
- name: Download APK artifact from previous job
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: elementx-apk-maestro
- name: Enable KVM group perms
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: Install maestro
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
continue-on-error: true
env:
MAESTRO_USERNAME: maestroelement
MAESTRO_PASSWORD: ${{ secrets.MATRIX_MAESTRO_ACCOUNT_PASSWORD }}
MAESTRO_RECOVERY_KEY: ${{ secrets.MATRIX_MAESTRO_ACCOUNT_RECOVERY_KEY }}
MAESTRO_ROOM_NAME: MyRoom
MAESTRO_INVITEE1_MXID: "@maestroelement2:matrix.org"
MAESTRO_INVITEE2_MXID: "@maestroelement3:matrix.org"
MAESTRO_APP_ID: io.element.android.x.debug
with:
api-level: ${{ env.API_LEVEL }}
arch: ${{ env.ARCH }}
profile: ${{ env.DEVICE }}
target: ${{ env.TARGET }}
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
disk-size: 3G
script: |
.github/workflows/scripts/maestro/maestro-local-with-screen-recording.sh app-gplay-x86_64-debug.apk
- name: Upload test results
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: test-results
path: |
~/.maestro/tests/**
retention-days: 5
overwrite: true
if-no-files-found: error
- name: Update summary (success)
if: steps.maestro_test.outcome == 'success'
run: |
echo "### Maestro tests worked :rocket:!" >> $GITHUB_STEP_SUMMARY
- name: Update summary (failure)
if: steps.maestro_test.outcome != 'success'
run: |
LOG_FILE=$(find ~/.maestro/tests/ -name maestro.log)
echo "Log file: $LOG_FILE"
LOG_LINES="$(tail -n 30 $LOG_FILE)"
echo "### :x: Maestro tests failed...
\`\`\`
$LOG_LINES
\`\`\`" >> $GITHUB_STEP_SUMMARY
- name: Fail the workflow in case of error in test
if: steps.maestro_test.outcome != 'success'
run: |
echo "Maestro tests failed. Please check the logs."
exit 1

66
.github/workflows/nightly.yml vendored Normal file
View file

@ -0,0 +1,66 @@
name: Build and release nightly application
on:
workflow_dispatch:
schedule:
# Every nights at 4
- cron: "0 4 * * *"
permissions: {}
env:
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx4g
CI_GRADLE_ARG_PROPERTIES: --stacktrace --no-daemon -Dsonar.gradle.skipCompile=true --no-configuration-cache
jobs:
nightly:
name: Build and publish nightly bundle to Firebase
runs-on: ubuntu-latest
if: ${{ github.repository == 'element-hq/element-x-android' }}
steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
tool-cache: true
# All of these default to true, but we should only need the 'android' one (and maybe swap-storage?)
android: false
dotnet: true
haskell: true
# This takes way too long to run (~2 minutes) and it saves only ~5.5GB
large-packages: false
docker-images: true
swap-storage: false
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Use JDK 21
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Build and upload Nightly application
run: |
./gradlew assembleGplayNightly appDistributionUploadGplayNightly $CI_GRADLE_ARG_PROPERTIES
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 }}
ELEMENT_ANDROID_SENTRY_DSN: ${{ secrets.ELEMENT_ANDROID_SENTRY_DSN }}
ELEMENT_SDK_SENTRY_DSN: ${{ secrets.ELEMENT_SDK_SENTRY_DSN }}
ELEMENT_CALL_SENTRY_DSN: ${{ secrets.ELEMENT_CALL_SENTRY_DSN }}
ELEMENT_CALL_POSTHOG_API_HOST: ${{ secrets.ELEMENT_CALL_POSTHOG_API_HOST }}
ELEMENT_CALL_POSTHOG_API_KEY: ${{ secrets.ELEMENT_CALL_POSTHOG_API_KEY }}
ELEMENT_CALL_RAGESHAKE_URL: ${{ secrets.ELEMENT_CALL_RAGESHAKE_URL }}
ELEMENT_ANDROID_NIGHTLY_KEYID: ${{ secrets.ELEMENT_ANDROID_NIGHTLY_KEYID }}
ELEMENT_ANDROID_NIGHTLY_KEYPASSWORD: ${{ secrets.ELEMENT_ANDROID_NIGHTLY_KEYPASSWORD }}
ELEMENT_ANDROID_NIGHTLY_STOREPASSWORD: ${{ secrets.ELEMENT_ANDROID_NIGHTLY_STOREPASSWORD }}
FIREBASE_TOKEN: ${{ secrets.ELEMENT_ANDROID_NIGHTLY_FIREBASE_TOKEN }}
- name: Additionally upload Nightly APK to browserstack for testing
continue-on-error: true # don't block anything by this upload failing (for now)
run: |
curl -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_PASSWORD" -X POST "https://api-cloud.browserstack.com/app-automate/upload" -F "file=@app/build/outputs/apk/gplay/nightly/app-gplay-universal-nightly.apk" -F "custom_id=element-x-android-nightly"
env:
BROWSERSTACK_USERNAME: ${{ secrets.ELEMENT_ANDROID_BROWSERSTACK_USERNAME }}
BROWSERSTACK_PASSWORD: ${{ secrets.ELEMENT_ANDROID_BROWSERSTACK_ACCESS_KEY }}

98
.github/workflows/nightlyReports.yml vendored Normal file
View file

@ -0,0 +1,98 @@
name: Nightly reports
on:
workflow_dispatch:
schedule:
# Every nights at 5
- cron: "0 5 * * *"
permissions: {}
# 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
CI_GRADLE_ARG_PROPERTIES: --stacktrace -Dsonar.gradle.skipCompile=true --no-configuration-cache
jobs:
nightlyReports:
name: Create kover report artifact and upload sonar result.
runs-on: ubuntu-latest
if: ${{ github.repository == 'element-hq/element-x-android' }}
steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
tool-cache: true
# All of these default to true, but we should only need the 'android' one (and maybe swap-storage?)
android: false
dotnet: true
haskell: true
# This takes way too long to run (~2 minutes) and it saves only ~5.5GB
large-packages: false
docker-images: true
swap-storage: false
- name: ⏬ Checkout with LFS
uses: nschloe/action-cached-lfs-checkout@385a8ecc719e50b8c71af6ab01a624b486b7c3bc # v1.2.5
- name: Use JDK 21
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0
with:
cache-read-only: false
- name: ⚙️ Run unit tests, debug and release
run: ./gradlew test $CI_GRADLE_ARG_PROPERTIES
- name: 📸 Run screenshot tests
run: ./gradlew verifyPaparazziDebug $CI_GRADLE_ARG_PROPERTIES
- 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
with:
name: kover-results
path: |
**/build/reports/kover
- name: 🔊 Publish results to Sonar
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
ORG_GRADLE_PROJECT_SONAR_LOGIN: ${{ secrets.SONAR_TOKEN }}
if: ${{ always() && env.SONAR_TOKEN != '' && env.ORG_GRADLE_PROJECT_SONAR_LOGIN != '' }}
run: ./gradlew assembleDebug createFullJarDebugTestFixtures :app:createFullJarGplayDebugTestFixtures $CI_GRADLE_ARG_PROPERTIES
# Gradle dependency analysis using https://github.com/autonomousapps/dependency-analysis-android-gradle-plugin
dependency-analysis:
name: Dependency analysis
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Use JDK 21
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.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
with:
name: dependency-analysis
path: build/reports/dependency-check-report.html

30
.github/workflows/post-release.yml vendored Normal file
View file

@ -0,0 +1,30 @@
name: Post-release
on:
push:
tags:
- 'v*'
permissions: {}
jobs:
post-release:
runs-on: ubuntu-latest
# Skip in forks
if: github.repository == 'element-hq/element-x-android'
steps:
- name: Trigger pipeline
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
github-token: ${{ secrets.ENTERPRISE_ACTIONS_TOKEN }}
script: |
const tag = context.ref.replace('refs/tags/', '');
const inputs = { git_tag: tag };
await github.rest.actions.createWorkflowDispatch({
owner: 'element-hq',
repo: 'element-enterprise',
workflow_id: 'pipeline-android.yml',
ref: 'main',
inputs: inputs
});

82
.github/workflows/pull_request.yml vendored Normal file
View file

@ -0,0 +1,82 @@
name: Pull Request
on:
pull_request_target:
types: [ opened, edited, labeled, unlabeled, synchronize ]
workflow_call: # zizmor: ignore[dangerous-triggers]
secrets:
ELEMENT_BOT_TOKEN:
required: true
permissions: {}
jobs:
prevent-blocked:
name: Prevent blocked
runs-on: ubuntu-latest
permissions:
pull-requests: read
steps:
- name: Add notice
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
if: contains(github.event.pull_request.labels.*.name, 'X-Blocked')
with:
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@818140d631d5f29f26b151afbe4179f87d9ceb5e # v4.0.1
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
runs-on: ubuntu-latest
permissions:
# Require to comment and close the PR.
pull-requests: write
if: >
github.event.action == 'opened' &&
github.event.pull_request.head.ref == 'develop' &&
github.event.pull_request.head.repo.full_name != github.repository
steps:
- name: Close pull request
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: "Thanks for opening this pull request, unfortunately we do not accept contributions from the main" +
" branch of your fork, please re-open once you switch to an alternative branch for everyone's sanity.",
});
github.rest.pulls.update({
pull_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
state: 'closed'
});

369
.github/workflows/quality.yml vendored Normal file
View file

@ -0,0 +1,369 @@
name: Code Quality Checks
on:
workflow_dispatch:
pull_request:
merge_group:
push:
branches: [ main, develop ]
permissions: {}
# 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
CI_GRADLE_ARG_PROPERTIES: --stacktrace --no-daemon -Dsonar.gradle.skipCompile=true --no-configuration-cache
jobs:
checkScript:
name: Search for forbidden patterns
runs-on: ubuntu-latest
steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
tool-cache: true
# All of these default to true, but we should only need the 'android' one (and maybe swap-storage?)
android: false
dotnet: true
haskell: true
# This takes way too long to run (~2 minutes) and it saves only ~5.5GB
large-packages: false
docker-images: true
swap-storage: false
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
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
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: Run code quality check suite
run: ./tools/check/check_code_quality.sh
checkScreenshot:
name: Search for invalid screenshot files
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python 3.12
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: 3.14
- name: Search for invalid screenshot files
run: ./tools/test/checkInvalidScreenshots.py
checkDependencies:
name: Search for invalid dependencies
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Use JDK 21
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Set up Python 3.12
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: 3.14
- name: Search for invalid dependencies
run: ./tools/dependencies/checkDependencies.py
# Code checks
konsist:
name: Konsist tests
runs-on: ubuntu-latest
# 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
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: 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
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@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.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
with:
name: konsist-report
path: |
**/build/reports/**/*.*
compose:
name: Compose tests
runs-on: ubuntu-latest
# 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
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: 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
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@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Run compose tests
run: ./tools/compose/check_stability.sh
lint:
name: Android lint check
runs-on: ubuntu-latest
# 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
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: 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
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@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Build Gplay Debug
run: ./gradlew :app:compileGplayDebugKotlin $CI_GRADLE_ARG_PROPERTIES
- name: Build Fdroid Debug
run: ./gradlew :app:compileFdroidDebugKotlin $CI_GRADLE_ARG_PROPERTIES
- name: Run lint
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
with:
name: linting-report
path: |
**/build/reports/**/*.*
detekt:
name: Detekt checks
runs-on: ubuntu-latest
# 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
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: 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
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@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Run Detekt
run: ./gradlew detekt $CI_GRADLE_ARG_PROPERTIES --no-daemon
- name: Upload reports
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: detekt-report
path: |
**/build/reports/**/*.*
ktlint:
name: Ktlint checks
runs-on: ubuntu-latest
# 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
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: 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
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@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Run Ktlint check
run: ./gradlew ktlintCheck $CI_GRADLE_ARG_PROPERTIES
- name: Upload reports
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ktlint-report
path: |
**/build/reports/**/*.*
docs:
name: Doc checks
runs-on: ubuntu-latest
# Allow all jobs on main and develop. Just one per PR.
concurrency:
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
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: 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
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: Run docs check
# This is equivalent to `./gradlew checkDocs`, but we avoid having to install java and gradle
run: python3 ./tools/docs/generate_toc.py --verify ./*.md docs/**/*.md
# Note: to auto fix issues you can use the following command:
# shellcheck -f diff <files> | git apply
shellcheck:
name: Check shell scripts
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Run shellcheck
uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # v2.0.0
with:
severity: warning
zizmor:
name: Run zizmor
runs-on: ubuntu-latest
permissions:
security-events: write # Required for upload-sarif (used by zizmor-action) to upload SARIF files.
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: zizmorcore/zizmor-action@b1d7e1fb5de872772f31590499237e7cce841e8e # v0.5.3
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' }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
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@67ed2c1f42fd2fc198cc3c14b43c8f83351f4fe9 # 13.0.5
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 }}

72
.github/workflows/recordScreenshots.yml vendored Normal file
View file

@ -0,0 +1,72 @@
name: Record screenshots
on:
workflow_dispatch:
pull_request:
types: [ labeled ]
permissions: {}
# 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
jobs:
record:
permissions:
# Need write permissions on PRs to remove the label "Record-Screenshots"
pull-requests: write
contents: write
name: Record screenshots on branch ${{ github.event.pull_request.head.ref || github.ref_name }}
runs-on: ubuntu-latest
if: github.event_name == 'workflow_dispatch' || github.event.label.name == 'Record-Screenshots'
steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
tool-cache: true
# All of these default to true, but we should only need the 'android' one (and maybe swap-storage?)
android: false
dotnet: true
haskell: true
# This takes way too long to run (~2 minutes) and it saves only ~5.5GB
large-packages: false
docker-images: true
swap-storage: false
- name: Remove Record-Screenshots label
if: github.event.label.name == 'Record-Screenshots'
uses: actions-ecosystem/action-remove-labels@2ce5d41b4b6aa8503e285553f75ed56e0a40bae0 # v1.3.0
with:
labels: Record-Screenshots
- name: ⏬ Checkout with LFS (PR)
if: github.event.label.name == 'Record-Screenshots'
uses: nschloe/action-cached-lfs-checkout@385a8ecc719e50b8c71af6ab01a624b486b7c3bc # v1.2.5
with:
persist-credentials: false
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
with:
persist-credentials: false
- name: ☕️ Use JDK 21
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
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@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Record screenshots
id: record
run: ./.github/workflows/scripts/recordScreenshots.sh
env:
GITHUB_TOKEN: ${{ secrets.DANGER_GITHUB_API_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_REPOSITORY: ${{ secrets.GITHUB_REPOSITORY }}
GRADLE_ARGS: ${{ env.CI_GRADLE_ARG_PROPERTIES }}

146
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,146 @@
name: Create release App Bundle and APKs
on:
workflow_dispatch:
push:
branches: [ main ]
permissions: {}
# 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
CI_GRADLE_ARG_PROPERTIES: --stacktrace --no-daemon -Dsonar.gradle.skipCompile=true --no-configuration-cache
jobs:
gplay:
name: Create App Bundle (Gplay)
runs-on: ubuntu-latest
concurrency:
group: ${{ format('build-release-main-gplay-{0}', github.sha) }}
cancel-in-progress: true
steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
tool-cache: true
# All of these default to true, but we should only need the 'android' one (and maybe swap-storage?)
android: false
dotnet: true
haskell: true
# This takes way too long to run (~2 minutes) and it saves only ~5.5GB
large-packages: false
docker-images: true
swap-storage: false
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Use JDK 21
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0
- name: Create 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 }}
ELEMENT_ANDROID_SENTRY_DSN: ${{ secrets.ELEMENT_ANDROID_SENTRY_DSN }}
ELEMENT_SDK_SENTRY_DSN: ${{ secrets.ELEMENT_SDK_SENTRY_DSN }}
ELEMENT_CALL_SENTRY_DSN: ${{ secrets.ELEMENT_CALL_SENTRY_DSN }}
ELEMENT_CALL_POSTHOG_API_HOST: ${{ secrets.ELEMENT_CALL_POSTHOG_API_HOST }}
ELEMENT_CALL_POSTHOG_API_KEY: ${{ secrets.ELEMENT_CALL_POSTHOG_API_KEY }}
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
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
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@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.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
concurrency:
group: ${{ format('build-release-main-fdroid-{0}', github.sha) }}
cancel-in-progress: true
steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
tool-cache: true
# All of these default to true, but we should only need the 'android' one (and maybe swap-storage?)
android: false
dotnet: true
haskell: true
# This takes way too long to run (~2 minutes) and it saves only ~5.5GB
large-packages: false
docker-images: true
swap-storage: false
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Use JDK 21
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0
- name: Create APKs
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 assembleFdroidRelease $CI_GRADLE_ARG_PROPERTIES
- name: Upload apks as artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: elementx-app-fdroid-apks-unsigned
path: |
app/build/outputs/apk/fdroid/release/*.apk

View file

@ -0,0 +1,20 @@
#!/bin/sh
#
# Copyright (c) 2025 Element Creations Ltd.
# Copyright 2024 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only.
# Please see LICENSE in the repository root for full details.
#
COUNT=0
mkdir -p /data/local/tmp/recordings;
FILENAME=/data/local/tmp/recordings/testRecording$COUNT.mp4
while true
do
COUNT=$((COUNT+1))
FILENAME=/data/local/tmp/recordings/testRecording$COUNT.mp4
printf "\nRecording video file #%d\n" $COUNT
screenrecord --bugreport --bit-rate=16m --size 720x1280 $FILENAME
done

View file

@ -0,0 +1,46 @@
#!/bin/sh
#
# Copyright (c) 2025 Element Creations Ltd.
# Copyright 2024 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only.
# Please see LICENSE in the repository root for full details.
#
# First we disable the onboarding flow on Chrome, which is a source of issues
# (see https://stackoverflow.com/a/64629745)
echo "Disabling Chrome onboarding flow"
adb shell am set-debug-app --persistent com.android.chrome
adb shell 'echo "chrome --disable-fre --no-default-browser-check --no-first-run" > /data/local/tmp/chrome-command-line'
adb shell am start -n com.android.chrome/com.google.android.apps.chrome.Main
adb install -r $1
echo "Starting the screen recording..."
adb push .github/workflows/scripts/maestro/local-recording.sh /data/local/tmp/
adb shell "chmod +x /data/local/tmp/local-recording.sh"
mkdir -p ~/.maestro/tests
# Start logcat in the background and save the output to a file, use `org.matrix.rust.sdk` tag since the SDK handles the logging
adb logcat 'org.matrix.rust.sdk:D *:S' > ~/.maestro/tests/logcat.txt &
adb shell "/data/local/tmp/local-recording.sh & echo \$! > /data/local/tmp/screenrecord_pid.txt" &
set +e
~/.maestro/bin/maestro test .maestro/allTests.yaml
TEST_STATUS=$?
echo "Test run completed with status $TEST_STATUS"
# Stop the screen recording loop
SCRIPT_PID=$(adb shell "cat /data/local/tmp/screenrecord_pid.txt")
adb shell "kill -2 $SCRIPT_PID"
# Get the PID of the screen recording process
SCREENRECORD_PID=$(adb shell ps | grep screenrecord | awk '{print $2}')
# Wait for the screen recording process to exit
while [ ! -z $SCREENRECORD_PID ]; do
echo "Waiting for screen recording ($SCREENRECORD_PID) to finish..."
adb shell "kill -2 $SCREENRECORD_PID"
sleep 1
SCREENRECORD_PID=$(adb shell ps | grep screenrecord | awk '{print $2}')
done
adb pull /data/local/tmp/recordings/ ~/.maestro/tests/
exit $TEST_STATUS

View file

@ -0,0 +1,77 @@
#!/usr/bin/env python3
import xml.etree.ElementTree as ET
import sys
import glob
screenshot_test_failures = []
output = []
def parse_test_failures(xml_file):
"""Parse XML test results and print failures."""
tree = ET.parse(xml_file)
root = tree.getroot()
# Find all testcase elements with failure children
if root.get("failures", "0") == "0":
return
name = root.get('name', 'Test Suite')
is_screenshot_test = name.startswith('ui.Preview')
if not is_screenshot_test:
output.append(f"## {name}")
for testcase in root.findall('.//testcase'):
failure = testcase.find('failure')
if failure is not None:
# Get testcase attributes
classname = testcase.get('classname', '')
name = testcase.get('name', '')
if is_screenshot_test:
# For screenshot tests, we want to display the classname as well
screenshot_test_failures.append(f"{classname}.{name}")
else:
# Get failure content (text inside the failure element)
failure_message = failure.get('message', '')
failure_content = failure.text if failure.text else ''
# Print in the requested format
output.append(f"### {name}")
output.append("```")
output.append(failure_message)
output.append("```")
output.append("<details><summary>Stacktrace</summary>")
output.append(f"<pre><code>{failure_content}</code></pre>")
output.append("</details>")
output.append("\n")
if __name__ == "__main__":
if len(sys.argv) < 2:
output.append("Usage: parse_test_failures.py <file>", file=sys.stderr)
sys.exit(1)
file = sys.argv[1]
if file.endswith('xml'):
parse_test_failures(file)
else:
files = glob.glob("**/build/test-results/*UnitTest/*.xml", root_dir = file, recursive = True)
for file in files:
parse_test_failures(file)
if screenshot_test_failures:
output.append("## Screenshot Test Failures")
output.append("```")
for failure in screenshot_test_failures:
output.append(failure)
output.append("```")
text_output = '\n'.join(output)
# Trim output larger than 1MB to avoid GitHub Action log limits
while len(text_output.encode('utf-8')) > 1_040_000:
output.pop(-2)
output.append("## !!! Truncated output due to size limits. !!!")
text_output = '\n'.join(output)
print(text_output)

View file

@ -0,0 +1,90 @@
#!/bin/bash
# Copyright (c) 2025 Element Creations Ltd.
# Copyright 2023-2024 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.
set -e
TOKEN=$GITHUB_TOKEN
REPO=$GITHUB_REPOSITORY
SHORT=t:,r:
LONG=token:,repo:
OPTS=$(getopt -a -n recordScreenshots --options $SHORT --longoptions $LONG -- "$@")
eval set -- "$OPTS"
while :
do
case "$1" in
-t | --token )
TOKEN="$2"
shift 2
;;
-r | --repo )
REPO="$2"
shift 2
;;
--)
shift;
break
;;
*)
echo "Unexpected option: $1"
help
;;
esac
done
BRANCH=$(git rev-parse --abbrev-ref HEAD)
echo Branch used: $BRANCH
if [[ -z ${TOKEN} ]]; then
echo "No token specified, either set the env var GITHUB_TOKEN or use the --token option"
exit 1
fi
if [[ -z ${REPO} ]]; then
echo "No repo specified, either set the env var GITHUB_REPOSITORY or use the --repo option"
exit 1
fi
echo "Deleting previous screenshots"
./gradlew removeOldSnapshots --stacktrace --warn $GRADLE_ARGS
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
if [[ -z ${INPUT_AUTHOR_NAME} ]]; then
git config user.name "ElementBot"
else
git config --local user.name "${INPUT_AUTHOR_NAME}"
fi
if [[ -z ${INPUT_AUTHOR_EMAIL} ]]; then
git config user.email "android@element.io"
else
git config --local user.name "${INPUT_AUTHOR_EMAIL}"
fi
git add -A
git commit -m "Update screenshots"
GITHUB_REPO="https://$GITHUB_ACTOR:$TOKEN@github.com/$REPO.git"
echo "Pushing changes"
if [[ -z ${GITHUB_ACTOR} ]]; then
echo "No GITHUB_ACTOR env var"
GITHUB_REPO="https://$TOKEN@github.com/$REPO.git"
fi
git push $GITHUB_REPO "$BRANCH"
echo "Done!"

63
.github/workflows/sonar.yml vendored Normal file
View file

@ -0,0 +1,63 @@
name: Sonar
on:
workflow_dispatch:
pull_request:
merge_group:
push:
branches: [ main, develop ]
permissions: {}
# 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
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
# 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)
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
tool-cache: true
# All of these default to true, but we should only need the 'android' one (and maybe swap-storage?)
android: false
dotnet: true
haskell: true
# This takes way too long to run (~2 minutes) and it saves only ~5.5GB
large-packages: false
docker-images: true
swap-storage: false
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
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@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Build debug code and test fixtures
run: ./gradlew assembleGplayDebug createFullJarDebugTestFixtures :app:createFullJarGplayDebugTestFixtures $CI_GRADLE_ARG_PROPERTIES
- name: 🔊 Publish results to Sonar
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
ORG_GRADLE_PROJECT_SONAR_LOGIN: ${{ secrets.SONAR_TOKEN }}
if: ${{ always() && env.SONAR_TOKEN != '' && env.ORG_GRADLE_PROJECT_SONAR_LOGIN != '' }}
run: ./gradlew sonar $CI_GRADLE_ARG_PROPERTIES

24
.github/workflows/stale-issues.yml vendored Normal file
View file

@ -0,0 +1,24 @@
name: Close stale issues that are missing info.
on:
schedule:
- cron: "30 1 * * *"
permissions: {}
jobs:
stale:
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
with:
only-labels: "X-Needs-Info"
days-before-issue-stale: 30
days-before-issue-close: 7
days-before-pr-stale: -1
stale-issue-label: "stale"
labels-to-remove-when-unstale: "X-Needs-Info"
stale-issue-message: "This issue has been awaiting further information for the past 30 days so will now be marked as stale. Please provide the requested information within the next 7 days to keep it open."
close-issue-message: "This issue is being closed due to inactivity after further information was requested."

52
.github/workflows/sync-localazy.yml vendored Normal file
View file

@ -0,0 +1,52 @@
name: Sync Localazy
on:
workflow_dispatch:
schedule:
# At 00:00 on every Monday UTC
- cron: '0 0 * * 1'
permissions: {}
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Use JDK 21
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Set up Python 3.12
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: 3.14
- name: Setup Localazy
run: |
curl -sS https://dist.localazy.com/debian/pubkey.gpg | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/localazy.gpg
echo "deb [arch=amd64 signed-by=/etc/apt/trusted.gpg.d/localazy.gpg] https://maven.localazy.com/repository/apt/ stable main" | sudo tee /etc/apt/sources.list.d/localazy.list
sudo apt-get update && sudo apt-get install localazy
- name: Run Localazy script
run: |
./tools/localazy/downloadStrings.sh --all
./tools/localazy/importSupportedLocalesFromLocalazy.py
./tools/test/generateAllScreenshots.py
- name: Create Pull Request for Strings
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with:
token: ${{ secrets.DANGER_GITHUB_API_TOKEN }}
commit-message: Sync Strings from Localazy
title: Sync Strings
body: |
- Update Strings from Localazy
branch: sync-localazy
base: develop
labels: PR-i18n

40
.github/workflows/sync-sas-strings.yml vendored Normal file
View file

@ -0,0 +1,40 @@
name: Sync SAS strings
on:
workflow_dispatch:
schedule:
# At 00:00 on every Monday UTC
- cron: '0 0 * * 1'
permissions: {}
jobs:
sync-sas-strings:
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' }}
# No concurrency required, runs every time on a schedule.
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python 3.12
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: 3.14
- name: Install Prerequisite dependencies
run: |
pip install requests
- name: Run SAS String script
run: ./tools/sas/import_sas_strings.py
- name: Create Pull Request for SAS Strings
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with:
commit-message: Sync SAS Strings
title: Sync SAS Strings
body: |
- Update SAS Strings from matrix-doc.
branch: sync-sas-strings
base: develop
labels: PR-Misc

116
.github/workflows/tests.yml vendored Normal file
View file

@ -0,0 +1,116 @@
name: Test
on:
workflow_dispatch:
pull_request:
merge_group:
push:
branches: [ main, develop ]
permissions: {}
# 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
jobs:
tests:
name: Runs unit tests
runs-on: ubuntu-latest
# 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:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
tool-cache: true
# All of these default to true, but we should only need the 'android' one (and maybe swap-storage?)
android: false
dotnet: true
haskell: true
# This takes way too long to run (~2 minutes) and it saves only ~5.5GB
large-packages: false
docker-images: true
swap-storage: false
# Increase swapfile size to prevent screenshot tests getting terminated
# https://github.com/actions/runner-images/discussions/7188#discussioncomment-6750749
- name: 💽 Increase swapfile size
run: |
sudo swapoff -a
sudo fallocate -l 8G /mnt/swapfile
sudo chmod 600 /mnt/swapfile
sudo mkswap /mnt/swapfile
sudo swapon /mnt/swapfile
sudo swapon --show
- name: ⏬ Checkout with LFS
uses: nschloe/action-cached-lfs-checkout@385a8ecc719e50b8c71af6ab01a624b486b7c3bc # v1.2.5
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 }}
- 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
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@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21'
- name: Configure gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.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: 🚫 Upload kover failed coverage reports
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: kover-error-report
path: |
app/build/reports/kover
- name: ✅ Upload kover report (disabled)
if: always()
run: echo "This is now done only once a day, see nightlyReports.yml"
- name: 🚫 Upload test results on error
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: tests-and-screenshot-tests-results
path: |
**/build/paparazzi/failures/
**/build/roborazzi/failures/
**/build/reports/tests/*UnitTest/
- name: 🚫 Modify summary on error
if: failure()
run: |
echo """## Tests failed!
""" >> $GITHUB_STEP_SUMMARY
python3 .github/workflows/scripts/parse_test_failures.py . >> $GITHUB_STEP_SUMMARY
echo "---" >> $GITHUB_STEP_SUMMARY
# https://github.com/codecov/codecov-action
- name: ☂️ Upload coverage reports to codecov
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
with:
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}
files: build/reports/kover/reportMerged.xml
verbose: true

16
.github/workflows/triage-incoming.yml vendored Normal file
View file

@ -0,0 +1,16 @@
name: Move new issues onto issue triage board v2
on:
issues:
types: [ opened ]
permissions: {}
jobs:
triage-new-issues:
runs-on: ubuntu-latest
steps:
- uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd # v2
with:
project-url: https://github.com/orgs/element-hq/projects/91
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}

87
.github/workflows/triage-labelled.yml vendored Normal file
View file

@ -0,0 +1,87 @@
name: Move labelled issues to correct boards and columns
on:
issues:
types: [labeled]
permissions: {}
jobs:
move_element_x_issues:
name: ElementX issues to ElementX project board
runs-on: ubuntu-latest
# Skip in forks
if: >
github.repository == 'element-hq/element-x-android'
steps:
- uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd # v2
with:
project-url: https://github.com/orgs/element-hq/projects/43
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
move_needs_info:
name: Move triaged needs info issues on board
runs-on: ubuntu-latest
steps:
- uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd # v2
id: addItem
with:
project-url: https://github.com/orgs/element-hq/projects/91
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
labeled: X-Needs-Info
- name: Print itemId
run: echo ${STEPS_ADDITEM_OUTPUTS_ITEMID}
env:
STEPS_ADDITEM_OUTPUTS_ITEMID: ${{ steps.addItem.outputs.itemId }}
- uses: kalgurn/update-project-item-status@31e54df46a2cdaef4f85c31ac839fbcd2fd7c3a2 # 0.0.3
if: ${{ steps.addItem.outputs.itemId }}
with:
project-url: https://github.com/orgs/element-hq/projects/91
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
item-id: ${{ steps.addItem.outputs.itemId }}
status: "Needs info"
ex_plorers:
name: Add labelled issues to X-Plorer project
runs-on: ubuntu-latest
if: >
contains(github.event.issue.labels.*.name, 'Team: Element X Feature')
steps:
- uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd # v2
with:
project-url: https://github.com/orgs/element-hq/projects/73
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
verticals_feature:
name: Add labelled issues to Verticals Feature project
runs-on: ubuntu-latest
if: >
contains(github.event.issue.labels.*.name, 'Team: Verticals Feature')
steps:
- uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd # v2
with:
project-url: https://github.com/orgs/element-hq/projects/57
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
qa:
name: Add labelled issues to QA project
runs-on: ubuntu-latest
if: >
contains(github.event.issue.labels.*.name, 'Team: QA') ||
contains(github.event.issue.labels.*.name, 'X-Needs-Signoff')
steps:
- uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd # v2
with:
project-url: https://github.com/orgs/element-hq/projects/69
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
signoff:
name: Add labelled issues to signoff project
runs-on: ubuntu-latest
if: >
contains(github.event.issue.labels.*.name, 'X-Needs-Signoff')
steps:
- uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd # v2
with:
project-url: https://github.com/orgs/element-hq/projects/89
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}

15
.github/workflows/validate-lfs.yml vendored Normal file
View file

@ -0,0 +1,15 @@
name: Validate Git LFS
on: [pull_request, merge_group]
permissions: {}
jobs:
build:
runs-on: ubuntu-latest
name: Validate
steps:
- uses: nschloe/action-cached-lfs-checkout@385a8ecc719e50b8c71af6ab01a624b486b7c3bc # v1.2.5
- run: |
./tools/git/validate_lfs.sh

View file

@ -1,38 +0,0 @@
# gitleaks config — element-x-ada
#
# Element X is a Matrix client fork with Cardano ADA integration.
# Patterns flagged are all public-by-design or doc/test fixtures:
# - PostHog apiKey: client-side analytics token, public on every PostHog-
# integrated mobile app. Identifies the project, doesn't grant write.
# - MapTiler API_KEY: client-side maps token, ships in every release
# - google-services.json: Firebase config — Google explicitly documents
# this as public-by-design (all real auth goes through FirebaseAuth)
# - Segment readKey: client-side write key
# - user_signing_key in KDoc comments: example values in doc-strings
# - docs/ + *Test.kt files: scratch + test fixtures, never live credentials
[extend]
useDefault = true
[allowlist]
description = "Public client keys (PostHog, MapTiler, Firebase, Segment) + docs + test fixtures"
paths = [
'''docs/.*''',
'''.*/google-services\.json''',
'''.*Test\.kt''',
'''localazy\.json''',
'''tools/localazy/.*''',
]
regexTarget = "line"
regexes = [
# PostHog client keys — match any variable name ending in apiKey
'''[a-zA-Z]*[Aa]piKey\s*=\s*"phc_[A-Za-z0-9_-]{20,}"''',
# MapTiler / similar public client keys named API_KEY constant
'''const\s+val\s+API_KEY\s*=\s*"''',
# Segment write keys (Kotlin style)
'''readKey\s*=\s*"''',
# Localazy / Segment readKey (JSON style)
'''"readKey"\s*:\s*"''',
# Matrix protocol KDoc examples (* prefix is the KDoc comment shape)
'''^\s*\*\s*"user_signing_key"\s*:\s*"''',
]

3
.idea/kotlinc.xml generated
View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="KotlinJpsPluginSettings"> <component name="KotlinJpsPluginSettings">
<option name="version" value="2.3.20" /> <option name="externalSystemId" value="Gradle" />
<option name="version" value="2.3.21" />
</component> </component>
</project> </project>

View file

@ -7,7 +7,7 @@ appId: ${MAESTRO_APP_ID}
- tapOn: - tapOn:
text: ${MAESTRO_INVITEE1_MXID} text: ${MAESTRO_INVITEE1_MXID}
index: 1 index: 1
- tapOn: "Send invite" - tapOn: "Continue"
- takeScreenshot: build/maestro/330-createAndDeleteDM - takeScreenshot: build/maestro/330-createAndDeleteDM
- tapOn: "maestroelement2" - tapOn: "maestroelement2"
- scroll - scroll

View file

@ -24,8 +24,16 @@ appId: ${MAESTRO_APP_ID}
text: ${MAESTRO_INVITEE2_MXID} text: ${MAESTRO_INVITEE2_MXID}
index: 1 index: 1
- tapOn: "Invite" - tapOn: "Invite"
- runFlow:
when:
visible: 'Invite new contact to this room?'
commands:
- tapOn:
id: "confirm_invite_unknown"
# Close the keyboard if it's still open
- tapOn: "Back"
# Go back to the room details screen
- tapOn: "Back" - tapOn: "Back"
- tapOn: "aRoomName"
- scrollUntilVisible: - scrollUntilVisible:
direction: DOWN direction: DOWN
element: element:

View file

@ -1,221 +0,0 @@
# BLOCKERS.md - Phase 1 Implementation Status
## Task 1: Module Scaffolding ✅ COMPLETE
### Completed
- ✅ Module structure created (api/impl/test)
- ✅ Metro DI setup following Element X patterns
- ✅ WalletEntryPoint and WalletState APIs defined
- ✅ PaymentFlowNode placeholder with Appyx navigation
- ✅ FakeWalletEntryPoint for testing
- ✅ Cardano client library dependencies added
- ✅ ProGuard rules configured
- ✅ Basic unit tests added
- ✅ Pushed to Gitea phase1-dev branch
---
## Task 2: Key Generation + Storage ✅ COMPLETE
### Completed
- ✅ **CardanoNetworkConfig.kt** - Single object for testnet/mainnet config swap
- Currently configured for TESTNET (preprod)
- Change `NETWORK` to `CardanoNetwork.MAINNET` for production
- All derived values (Koios URL, explorer URL, address prefix) auto-switch
- ✅ **CardanoKeyStorage** (interface + implementation)
- Per-session wallet isolation (key alias: `cardano_wallet_{sessionId}`)
- 24-word BIP-39 mnemonic generation using cardano-client-lib
- AES-GCM-256 encryption with Android Keystore-backed key
- `setUserAuthenticationRequired(true)` - biometric/PIN for every operation
- `setUserAuthenticationValidityDurationSeconds(-1)` - no grace period
- `setInvalidatedByBiometricEnrollment(true)` - invalidate on biometric change
- Methods: `generateWallet`, `importWallet`, `getMnemonic`, `getBaseAddress`, `getStakeAddress`, `deleteWallet`
- ✅ **CardanoWalletManager** (interface + implementation)
- Key derivation using CIP-1852 via cardano-client-lib's Account class
- Path `m/1852'/1815'/0'/0/0` for external receiving address
- Path `m/1852'/1815'/0'/2/0` for staking key
- Shelley base address generation (payment + staking key hash)
- Uses CardanoNetworkConfig for network selection
- Exposes: `getAddress(sessionId)`, `getStakeAddress(sessionId)`, `getSpendingKey(sessionId)`
- ✅ **SeedPhraseManager** (interface + implementation)
- 24-word mnemonic generation (256-bit entropy)
- Support for 12/15/18/21/24 word counts
- BIP-39 validation (checksum + wordlist)
- Word suggestions for autocomplete
- Normalization (whitespace, case)
- ⚠️ UI must apply `FLAG_SECURE` when displaying seed phrases (documented)
- ✅ **FakeCardanoKeyStorage** for testing
- ✅ Unit tests for SeedPhraseManager, CardanoNetworkConfig, CardanoWalletManager
### Decisions Made (per instructions)
- Wallet scope: **PER SESSION** (each Matrix account has its own wallet)
- Biometric change: **INVALIDATE** key + require wallet re-import/creation
- Network: **TESTNET** (preprod) - single config constant for easy mainnet swap
### Not Verified (No Android SDK in build environment)
- ⚠️ Compilation with `./gradlew :features:wallet:impl:assemble`
- ⚠️ Unit tests with `./gradlew :features:wallet:impl:test`
- ⚠️ ktlint compliance
- ⚠️ Actual Android Keystore behavior (requires device/emulator)
- ⚠️ Biometric prompt integration (requires Activity context)
### Security Notes
1. **Mnemonic never stored in plaintext** - Always encrypted with Keystore key
2. **Key material cleared after use** - `ByteArray.fill(0)` called where possible
3. **Per-session isolation** - Different Matrix accounts cannot access each other's wallets
4. **Biometric invalidation** - If user adds/removes fingerprints, wallet key becomes invalid
5. **No screenshots** - UI must apply FLAG_SECURE when showing seed phrase
---
## Task 3: Koios Client ✅ COMPLETE
### Completed
- ✅ **CardanoClient.kt** interface in `api/` module:
- `getBalance(address: String): Result<Long>` — balance in lovelace
- `getUtxos(address: String): Result<List<Utxo>>` — unspent outputs
- `submitTx(signedTxCbor: String): Result<String>` — returns tx hash
- `getTxStatus(txHash: String): Result<TxStatus>` — PENDING/CONFIRMED/FAILED
- ✅ **Data models** in `api/`:
- `Utxo.kt` — txHash, outputIndex, amount, address
- `TxStatus.kt` — enum PENDING/CONFIRMED/FAILED
- `CardanoException.kt` — typed exceptions (NetworkException, RateLimitException, InvalidAddressException, TransactionNotFoundException, SubmissionFailedException, InsufficientFundsException, ApiException)
- ✅ **KoiosCardanoClient.kt** implementation:
- Uses `BackendFactory.getKoiosBackendService()` from cardano-client-lib
- Testnet URL: `https://preprod.koios.rest/api/v1` (via CardanoNetworkConfig)
- Mainnet URL: `https://api.koios.rest/api/v1` (via CardanoNetworkConfig)
- 3 retries with exponential backoff (1s → 2s → 4s, max 10s)
- Basic rate limiting (100ms min between requests for Koios 100 req/10s limit)
- DI: `@ContributesBinding(SessionScope::class)`
- Error parsing: 429 → RateLimitException, 5xx → NetworkException, etc.
- ✅ **FakeCardanoClient.kt** for testing:
- Configurable balances, UTxOs, transaction statuses
- Error simulation (network errors, rate limits, submit failures)
- Transaction lifecycle simulation (pending → confirmed → failed)
- Call counters for test verification
- Helper: `setupWallet(address, balance)` creates realistic UTxO set
- ✅ **KoiosCardanoClientTest.kt** — 15+ unit tests:
- getBalance success, unknown address, network error, rate limit
- getUtxos success, empty result
- submitTx success, failure
- getTxStatus pending, confirmed, failed
- reset/state management
- ✅ **CardanoWalletManager updated** to use CardanoClient:
- `refreshBalance()` now fetches real balance via Koios
- Updates WalletState with lovelace + formatted ADA string
### Design Notes
- **No API key required** — Koios public API is free
- **Network config centralized** — Change `CardanoNetworkConfig.NETWORK` to swap testnet/mainnet
- **Hex CBOR for submitTx** — Accepts hex-encoded signed transaction bytes
- **UTxO pagination** — Limited to first 100 UTxOs (sufficient for typical wallets)
### Potential Issues
- ⚠️ `getTxStatus` returns PENDING for unknown hashes (could be never-submitted or truly pending)
- ⚠️ Koios rate limit (100 req/10s) may need adjustment for heavy usage patterns
- ⚠️ No getProtocolParameters yet (needed for Task 4 fee calculation)
---
## Task 4-6: See PHASE1-PLAN.md
---
## Task 7: Timeline Payment Card ✅ COMPLETE
### Completed
- ✅ **PaymentCardStatus.kt** — Enum for PENDING/CONFIRMED/FAILED states
- ✅ **TimelineItemPaymentContent.kt** — Data class implementing TimelineItemEventContent
- amountLovelace, addresses, txHash, status, network, isSentByMe
- Computed properties: amountAda, isTestnet, truncatedTxHash, explorerUrl
- Companion formatAda() helper
- ✅ **TimelineItemPaymentView.kt** — Compose UI for payment card
- Cardano icon (₳ symbol)
- Amount in ADA (formatted from lovelace)
- Status chip with spinner (pending), checkmark (confirmed), X (failed)
- Testnet badge when applicable
- Truncated tx hash (tappable → CardanoScan)
- View on explorer link for confirmed transactions
- @PreviewsDayNight with multiple preview states
- ✅ **TimelineItemPaymentContentTest.kt** — Unit tests for content model
- ✅ **Integration with TimelineItemEventContentView.kt**
### Design Notes
- Payment cards use different colors for sent (primary) vs received (surface)
- Explorer URLs: preprod.cardanoscan.io for testnet, cardanoscan.io for mainnet
- Tx hash truncated to first 8 + last 8 chars for display
---
## Task 8: Raw Event Handling ✅ COMPLETE (UPGRADED)
### ✅ RESOLVED: SDK Raw Event API
**Previous blocker:** Matrix Rust SDK did not expose raw event sending or raw JSON access.
**Resolution:** The SDK (version 26.03.24) now provides:
- `Timeline.sendRaw(eventType: String, content: String)` — Sends custom event types
- `MsgLikeKind.Other` with `eventType` field — Receives custom events
- `TimelineItemDebugInfo.originalJson` — Access to raw event JSON via debug info provider
**Implementation updated to use proper raw events instead of text markers.**
### Completed
- ✅ **PaymentEventSender.kt** — Interface for sending payment events
- ✅ **DefaultPaymentEventSender.kt** — Implementation using raw events
- Uses `timeline.sendRaw(eventType, content)` to send custom events
- Event type: `co.sulkta.payment.request` (reverse-domain format)
- Status updates: `co.sulkta.payment.status`
- No text marker hack — proper Matrix custom events
- ✅ **TimelineItemContentPaymentFactory.kt** — Parser for payment events
- `isPaymentEventType(eventType)` — Checks for payment event type
- `isStatusUpdateEventType(eventType)` — Checks for status update type
- `createFromRaw(json, isSentByMe)` — Parses raw JSON from custom events
- Supports both camelCase and snake_case field names
- Graceful error handling — returns null on malformed JSON
- ✅ **TimelineEventContentMapper.kt** — Maps `MsgLikeKind.Other` to `CustomEventContent`
- ✅ **TimelineItemContentFactory.kt** — Handles `CustomEventContent` for payments
- Gets raw JSON via `timelineItemDebugInfoProvider().originalJson`
- Delegates to paymentFactory for payment event types
- ✅ **CustomEventContent.kt** — New EventContent type for custom events
- ✅ **Timeline.sendRaw()** — Added to Timeline interface and RustTimeline implementation
- ✅ **FakePaymentEventSender.kt** — Test fake
- ✅ **TimelineItemContentPaymentFactoryTest.kt** — Updated unit tests
### m.replace Status Updates
**Decision:** Status updates are sent as separate events of type `co.sulkta.payment.status`.
**Future improvement:** When SDK exposes event relations, refactor to use m.replace for cleaner status update thread.
### Benefits of Raw Event Approach
- ✅ Proper Matrix protocol compliance (custom event types, not hacked text)
- ✅ Non-wallet clients see "Unknown event" instead of JSON-in-text
- ✅ Clean separation of payment events from regular messages
- ✅ Events won't be indexed by message search
- ✅ No message length limits concern
---
## Known Issues
### Issue 1: Biometric Prompt Activity Context
The `CardanoKeyStorageImpl` uses `setUserAuthenticationRequired(true)` which will cause `UserNotAuthenticatedException` when accessing the key. The biometric prompt UI must be triggered from an Activity/Fragment context before calling `getMnemonic()`, `getSpendingKey()`, etc.
**Solution:** Task 6 (Payment Flow UI) must call BiometricPrompt before invoking storage operations.
### Issue 2: KeyPermanentlyInvalidatedException
If user changes biometric enrollment, the Keystore key is invalidated. Current behavior: throws exception, user must delete and recreate wallet.
**Enhancement (future):** Show user-friendly message explaining why wallet became invalid and offer to re-import.
---
*Last updated: 2026-03-27 - Task 2 complete*

View file

@ -1,3 +1,213 @@
Changes in Element X v26.05.2
=============================
<!-- Release notes generated using configuration in .github/release.yml at v26.05.2 -->
## What's Changed
### ✨ Features
* Remove SignInWithClassic FeatureFlag to enable the feature. by @bmarty in https://github.com/element-hq/element-x-android/pull/6698
* Create a new room when inviting people in a DM by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6756
* Remove LiveLocationSharing feature flag by @ganfra in https://github.com/element-hq/element-x-android/pull/6811
### 🙌 Improvements
* Disable biometric unlock when we disable pin code unlock by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6781
### 🐛 Bugfixes
* Fix room list duplicate-detection telemetry crashing before it can report by @jennaharris7 in https://github.com/element-hq/element-x-android/pull/6791
* Only load full media on media viewer when it's the visible item by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6794
* Attempt to fix room list item duplicates at midnight by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6793
### 🗣 Translations
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/6798
### 🧱 Build
* Fix Maestro again after changes to the invite flow by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6796
* Renovate: Keep Guava on the Android variant and ignore jre-only upgrades by @bmarty in https://github.com/element-hq/element-x-android/pull/6776
### Dependency upgrades
* Update dependency androidx.compose:compose-bom to v2026.05.00 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6784
* Update dependency io.sentry:sentry-android to v8.41.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6787
* Update kotlin by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6790
* Update camera to v1.6.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6783
* Update dependency androidx.webkit:webkit to v1.16.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6786
* Update dependency com.google.firebase:firebase-bom to v34.13.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6789
* Update dependency org.matrix.rustcomponents:sdk-android to v26.05.18 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6805
### Others
* Add MIDI playback by @cizra in https://github.com/element-hq/element-x-android/pull/6770
* Show error message when using "Sign in with QR code" with a QR from a device that is also not signed in by @hughns in https://github.com/element-hq/element-x-android/pull/6802
## New Contributors
* @jennaharris7 made their first contribution in https://github.com/element-hq/element-x-android/pull/6791
* @cizra made their first contribution in https://github.com/element-hq/element-x-android/pull/6770
**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v26.05.1...v26.05.2
Changes in Element X v26.05.1
=============================
<!-- Release notes generated using configuration in .github/release.yml at v26.05.1 -->
## What's Changed
### ✨ Features
* Make Element Call screen work edge-to-edge by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6634
### 🙌 Improvements
* Stop removing the `logs` dir when clearing cache by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6765
* Adapt to new DM definition changes in the SDK by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6748
* feat: Update call started timeline item + declined support by @BillCarsonFr in https://github.com/element-hq/element-x-android/pull/6649
### 🐛 Bugfixes
* Improve pin code UX by @bmarty in https://github.com/element-hq/element-x-android/pull/6744
* Use just the other user's avatar for DM details by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6738
* Improve `FetchPushForegroundService`'s reliability by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6757
* Prevent user from starting Live Location Sharing in thread by @bmarty in https://github.com/element-hq/element-x-android/pull/6767
* Fix media playback from the timeline broken when exiting a thread by @bmarty in https://github.com/element-hq/element-x-android/pull/6771
* Pin code: remove the key if there is no pin code by @bmarty in https://github.com/element-hq/element-x-android/pull/6780
### 🗣 Translations
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/6761
### 🚧 In development 🚧
* Feature : share live location by @ganfra in https://github.com/element-hq/element-x-android/pull/6741
### Dependency upgrades
* Update dependency org.matrix.rustcomponents:sdk-android to v26.05.7 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6746
* Update actions/add-to-project action to v2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6758
* Update dependency io.github.sergio-sastre.ComposablePreviewScanner:android to v0.9.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6759
* Update dependency io.element.android:element-call-embedded to v0.19.3 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6766
* Update metro to v1 (major) by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6720
* Update tspascoal/get-user-teams-membership action to v4.0.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6750
* Update plugin sonarqube to v7.3.0.8198 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6743
* Update plugin dependencycheck to v12.2.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6760
* Update dependency com.google.guava:guava to v33.6.0-android by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6646
* Update dependency org.matrix.rustcomponents:sdk-android to v26.05.13 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6779
### Others
* Render media captions formatting in the media viewer by @bxdxnn in https://github.com/element-hq/element-x-android/pull/6729
* Reduce FeatureFlag `Knock` effect on room creation and room edition forms by @bmarty in https://github.com/element-hq/element-x-android/pull/6768
* Use the right analytics span as a parent in `checkNetworkConnection` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6751
* Add missing strings `theme.black` by @bmarty in https://github.com/element-hq/element-x-android/pull/6772
* Map back button in web view to esc (revive fixed version of: https://github.com/element-hq/element-x-android/pull/6724) by @toger5 in https://github.com/element-hq/element-x-android/pull/6725
**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v26.05.0...v26.05.1
Changes in Element X v26.05.0
=============================
<!-- Release notes generated using configuration in .github/release.yml at v26.05.0 -->
## What's Changed
### ✨ Features
* Add flag for automatic back pagination feature by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6637
* Promote "history sharing on invite" out of developer options by @richvdh in https://github.com/element-hq/element-x-android/pull/6647
* Remove RoomDirectorySearch feature flag — always enable the feature by @Copilot in https://github.com/element-hq/element-x-android/pull/6736
### 🙌 Improvements
* Change native back button behavior in EC view (close settings in EC with os native back) by @toger5 in https://github.com/element-hq/element-x-android/pull/6642
* Revert PR #6642 by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6724
* Use 'Report a problem' string instead of 'Report bug' by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6735
### 🐛 Bugfixes
* Remove distributed tracing of the 'timeline loading' flow by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6644
* Set max lines for 'in reply to' view conditionally by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6612
* Mention pill cut off by @bmarty in https://github.com/element-hq/element-x-android/pull/6651
* Ensure that bottom sheet can scroll by @bmarty in https://github.com/element-hq/element-x-android/pull/6661
* Remove legacy `mx-reply` from `toPlainText` formatted event contents by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6683
* Fix ANRs when receiving push notifications by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6696
* Mitigate a deadlock when loading room timelines by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6674
* Fix calls on Huawei devices: skip addWebMessageListener on Chromium < 119 by @manfrommedan in https://github.com/element-hq/element-x-android/pull/6640
* Allow cancelling room loading in Home screen by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6723
* Let our Json parser accept comments and trailing comma. by @bmarty in https://github.com/element-hq/element-x-android/pull/6700
* Fix low width image message by @krbns in https://github.com/element-hq/element-x-android/pull/6692
* Make icons in the Chat screen top bar 16dp by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6733
* Fix back button sometimes not working after exiting a thread by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6732
* Make send event state UI easier to click by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6739
### 🗣 Translations
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/6658
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/6716
### 🧱 Build
* Fix record screenshots action permissions by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6679
* Fix dependency error by @bmarty in https://github.com/element-hq/element-x-android/pull/6697
### 🚧 In development 🚧
* [Link new device] Add missing screen to render digits that the user has to type on the other device by @bmarty in https://github.com/element-hq/element-x-android/pull/6680
### Dependency upgrades
* Update dependency io.nlopez.compose.rules:detekt to v0.5.7 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6594
* Update zizmorcore/zizmor-action action to v0.5.3 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6630
* Update dependency io.sentry:sentry-android to v8.38.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6597
* fix(deps): update camera to v1.6.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6514
* Update dependency io.sentry:sentry-android to v8.39.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6648
* Update dependency io.element.android:element-call-embedded to v0.19.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6662
* Update dependencyAnalysis to v3.9.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6657
* Update dependency org.matrix.rustcomponents:sdk-android to v26.04.27 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6666
* Update dependency io.sentry:sentry-android to v8.40.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6691
* Update dependency org.jsoup:jsoup to v1.22.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6660
* Update kotlin by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6687
* Update dependency androidx.compose:compose-bom to v2026.04.01 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6693
* Update dependency io.nlopez.compose.rules:detekt to v0.5.8 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6711
* Update dependency com.posthog:posthog-android to v3.43.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6704
* Update dependency org.matrix.rustcomponents:sdk-android to v26.05.4 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6718
* Update roborazzi to v1.60.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6722
* Update dependency net.zetetic:sqlcipher-android to v4.15.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6727
* Update dependency org.maplibre.gl:android-sdk to v13.1.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6731
* Update dependency org.matrix.rustcomponents:sdk-android to v26.05.6 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6734
* Update dependencyAnalysis to v3.10.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6742
* Update tspascoal/get-user-teams-membership action to v4 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6747
### Others
* devx: fix build sdk script options for macos by @BillCarsonFr in https://github.com/element-hq/element-x-android/pull/6636
* PR:Fix mention pill cut off by @krbns in https://github.com/element-hq/element-x-android/pull/6622
* Update media viewer UI by @bmarty in https://github.com/element-hq/element-x-android/pull/6643
* Strip formatting from media captions in room summary by @bxdxnn in https://github.com/element-hq/element-x-android/pull/6670
* Update error mappings for Link new device flow by @hughns in https://github.com/element-hq/element-x-android/pull/6677
* Rename `OIDC` components and variables to `OAuth` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6686
* [Link new device] Add missing error case "already signed in" by @bmarty in https://github.com/element-hq/element-x-android/pull/6688
* Improve detection of completion for Link new device flow by @hughns in https://github.com/element-hq/element-x-android/pull/6681
* Remove external call support by @bmarty in https://github.com/element-hq/element-x-android/pull/6668
* [a11y] Fix a set of issues by @bmarty in https://github.com/element-hq/element-x-android/pull/6650
* Add clipping to RoomSummaryRow by @bxdxnn in https://github.com/element-hq/element-x-android/pull/6654
* Fix media viewer flickering and crashing by @bxdxnn in https://github.com/element-hq/element-x-android/pull/6715
* Rename verification methods by @bmarty in https://github.com/element-hq/element-x-android/pull/6726
* Add a way to tweak MAS url. by @bmarty in https://github.com/element-hq/element-x-android/pull/6682
* Fix 2 x Crash the app in Developer Options - Update AppDeveloperSettingsView.kt by @escix in https://github.com/element-hq/element-x-android/pull/6708
* Introduce UI sample by @bmarty in https://github.com/element-hq/element-x-android/pull/6740
## New Contributors
* @krbns made their first contribution in https://github.com/element-hq/element-x-android/pull/6622
* @toger5 made their first contribution in https://github.com/element-hq/element-x-android/pull/6642
* @manfrommedan made their first contribution in https://github.com/element-hq/element-x-android/pull/6640
* @Copilot made their first contribution in https://github.com/element-hq/element-x-android/pull/6736
**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v26.04.4...v26.05.0
Changes in Element X v26.04.4
=============================
<!-- Release notes generated using configuration in .github/release.yml at v26.04.4 -->
## What's Changed
### 🙌 Improvements
* Natural media viewer swiping order by @bxdxnn in https://github.com/element-hq/element-x-android/pull/6431
* Replace `rustls-platform-verifier-android.aar` with single class by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6610
* Cleanup FetchPushForegroundService by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6577
* cleaning: Remove join button from call notify timelineItemView by @BillCarsonFr in https://github.com/element-hq/element-x-android/pull/6603
### 🐛 Bugfixes
* Fix crash when going back to threads list by @bxdxnn in https://github.com/element-hq/element-x-android/pull/6620
* audio: Let EC decide alone what communication device to use by @BillCarsonFr in https://github.com/element-hq/element-x-android/pull/6609
* Fix media viewer bottom sheets not being scrollable by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6631
### 🗣 Translations
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/6626
### 📄 Documentation
* Updates to new features and some refactoring. by @mxandreas in https://github.com/element-hq/element-x-android/pull/6591
### 🚧 In development 🚧
* WIP : live location rendering by @ganfra in https://github.com/element-hq/element-x-android/pull/6611
### Dependency upgrades
* Update dependency io.element.android:element-call-embedded to v0.19.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6593
* Update dependency androidx.annotation:annotation-jvm to v1.10.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6596
* Update dependency org.jetbrains.kotlinx:kotlinx-serialization-json to v1.11.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6605
* Update dependency com.google.firebase:firebase-bom to v34.12.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6604
* Update actions/upload-artifact action to v7.0.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6614
* Update plugin dependencycheck to v12.2.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6621
* Update actions/github-script action to v9 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6606
* Update peter-evans/create-pull-request action to v8.1.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6615
* Update dependencyAnalysis to v3.7.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6616
* Update dependency org.matrix.rustcomponents:sdk-android to v26.04.21 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6635
### Others
* Settings UI update. by @bmarty in https://github.com/element-hq/element-x-android/pull/6602
* Support replying to messages with voice recordings by @kalix127 in https://github.com/element-hq/element-x-android/pull/6464
* Add Black theme option for battery saving on OLED displays by @timurgilfanov in https://github.com/element-hq/element-x-android/pull/6441
* Fix | When selecting earpiece twice in a row the proximity sensor get wrongly disabled by @BillCarsonFr in https://github.com/element-hq/element-x-android/pull/6627
* Update wording of deactivate account screen by @bmarty in https://github.com/element-hq/element-x-android/pull/6633
**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v26.04.3...v26.04.4
Changes in Element X v26.04.3 Changes in Element X v26.04.3
============================= =============================

View file

@ -2,16 +2,16 @@
<!--- TOC --> <!--- TOC -->
* [Developer onboarding](#developer-onboarding) * [Contributing to Element](#contributing-to-element)
* [Contributing code to Matrix](#contributing-code-to-matrix)
* [Android Studio settings](#android-studio-settings)
* [Compilation](#compilation)
* [Strings](#strings)
* [I want to add new strings to the project](#i-want-to-add-new-strings-to-the-project)
* [I want to help translating Element](#i-want-to-help-translating-element) * [I want to help translating Element](#i-want-to-help-translating-element)
* [I want to fix a bug](#i-want-to-fix-a-bug)
* [I want to add a new feature or enhancement](#i-want-to-add-a-new-feature-or-enhancement)
* [Developer onboarding](#developer-onboarding)
* [Submitting the PRs](#submitting-the-prs)
* [Android Studio settings](#android-studio-settings)
* [Compilation](#compilation)
* [Strings](#strings)
* [Element X Android Gallery](#element-x-android-gallery) * [Element X Android Gallery](#element-x-android-gallery)
* [I want to add a new feature to Element X Android](#i-want-to-add-a-new-feature-to-element-x-android)
* [I want to submit a PR to fix an issue](#i-want-to-submit-a-pr-to-fix-an-issue)
* [Kotlin](#kotlin) * [Kotlin](#kotlin)
* [Changelog](#changelog) * [Changelog](#changelog)
* [Code quality](#code-quality) * [Code quality](#code-quality)
@ -29,69 +29,67 @@
<!--- END --> <!--- END -->
## Developer onboarding ## Contributing to Element
For a detailed overview of the project, see [Developer Onboarding](./docs/_developer_onboarding.md).
## Contributing code to Matrix
If instead of contributing to the Element X Android project, you want to contribute to Synapse, the homeserver implementation, please read the [Synapse contribution guide](https://element-hq.github.io/synapse/latest/development/contributing_guide.html).
Element X Android support can be found in this room: [![Element X Android Matrix room #element-x-android:matrix.org](https://img.shields.io/matrix/element-x-android:matrix.org.svg?label=%23element-x-android:matrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#element-x-android:matrix.org). Element X Android support can be found in this room: [![Element X Android Matrix room #element-x-android:matrix.org](https://img.shields.io/matrix/element-x-android:matrix.org.svg?label=%23element-x-android:matrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#element-x-android:matrix.org).
The rest of the document contains specific rules for Matrix Android projects. The rest of the document contains specific rules for Matrix Android projects.
## Android Studio settings
Please set the "hard wrap" setting of Android Studio to 160 chars, this is the setting we use internally to format the source code (Menu `Settings/Editor/Code Style` then `Hard wrap at`).
Please ensure that you're using the project formatting rules (which are in the project at .idea/codeStyles/), and format the file before committing them.
## Compilation
This project should compile without any special action. Just clone it and open it with Android Studio, or compile from command line using `gradlew`.
## Strings
The strings of the project are managed externally using [https://localazy.com](https://localazy.com) and shared with Element X iOS.
### I want to add new strings to the project
Only the core team can modify or add English strings to Localazy. As an external contributor, if you want to add new strings, feel free to add an Android resource file to the project (for instance a file named `temporary.xml`), with a note in the description of the PR for the reviewer to integrate the String into `Localazy`. If accepted, the reviewer will add the String(s) for you, and then you can download them on your branch (following these [instructions](./tools/localazy/README.md#download-translations)) and remove the temporary file.
Please follow the naming rules for the key. More details in [the dedicated section in this README.md](./tools/localazy/README.md#key-naming-rules)
### I want to help translating Element ### I want to help translating Element
To help translating, please go to [https://localazy.com/p/element](https://localazy.com/p/element). To help translating, please go to [https://localazy.com/p/element](https://localazy.com/p/element).
- If you want to fix an issue with an English string, please open an issue on the github project of Element X (Android or iOS). Only the core team can modify or add English strings.
- If you want to fix an issue in other languages, or add a missing translation, or even add a new language, please go to [https://localazy.com/p/element](https://localazy.com/p/element). - If you want to fix an issue in other languages, or add a missing translation, or even add a new language, please go to [https://localazy.com/p/element](https://localazy.com/p/element).
- If you want to fix an issue with an English string, please open an issue on the github project of Element X (Android or iOS). Only the core team can modify or add English strings. As an external contributor, if you want to add new strings, feel free to add an Android resource file to the project (for instance a file named `temporary.xml`), with a note in the description of the PR for the reviewer to integrate the String into `Localazy`. If accepted, the reviewer will add the String(s) for you, and then you can download them on your branch (following these [instructions](./tools/localazy/README.md#download-translations)) and remove the temporary file. Please follow the naming rules for the key. More details in [the dedicated section in this README.md](./tools/localazy/README.md#key-naming-rules) More information can be found [in this README.md](./tools/localazy/README.md).
More information can be found [in this README.md](./tools/localazy/README.md).
Once a language is sufficiently translated, it will be added to the app. The core team will decide when a language is sufficiently translated. Once a language is sufficiently translated, it will be added to the app. The core team will decide when a language is sufficiently translated.
### I want to fix a bug
Please check if a corresponding issue exists, if not please create one. In both cases, let us know in the comment that you've started working on it.
### I want to add a new feature or enhancement
To make a great product with a great user experience, all the small efforts need to go in the same direction and be aligned and consistent with each other.
Before making your contribution, please consider the following:
* One product cant do everything well. Element is focusing on private end-to-end encrypted messaging and voice - this can either be for consumers (e.g. friends and family) or for professional teams and organizations. Public forums and other types of chats without E2EE remain supported but are not the primary use case in case UX compromises need to be made.
* There are 3 platforms - Android, [iOS](https://github.com/element-hq/element-x-ios) and [Web/Desktop](https://github.com/element-hq/element-web). These platforms need to have feature parity and design consistency. For some features, supporting all platforms is a must have, in some cases exceptions can be made to have it on one platform only.
* To make sure your idea fits both from a design/solution and use case perspective, please open a new issue (or find an existing issue) in [element-meta](https://github.com/element-hq/element-meta/issues) repository describing the use case and how you plan to tackle it. Do not just describe what feature is missing, explain why the users need it with a couple of real life examples from the field.
* In case of an existing issue, please comment that you're planning to contribute. If you create a new issue, please specify that in the issue. In such a case we will try to review the issue ASAP and provide you with initial feedback so you can be confident if and at which conditions your contributions will be accepted.
Once we know that you want to contribute and have confirmed that the new feature is overall aligned with the product direction, the designers of the core team will help you with the designs and any other type of guidance when it comes to the user experience. We will try to unblock you as quickly as we can, but it may not be instant. Having a clear understanding of the use case and the impact of the feature will help us with the prioritization and faster responses.
Only once all of the above is met should you open a PR with your proposed changes.
## Developer onboarding
For a detailed overview of the project, see [Developer Onboarding](./docs/_developer_onboarding.md).
### Submitting the PRs
Please have a look in the [dedicated documentation](./docs/pull_request.md) about pull request.
### Android Studio settings
Please set the "hard wrap" setting of Android Studio to 160 chars, this is the setting we use internally to format the source code (Menu `Settings/Editor/Code Style` then `Hard wrap at`).
Please ensure that you're using the project formatting rules (which are in the project at .idea/codeStyles/), and format the file before committing them.
### Compilation
This project should compile without any special action. Just clone it and open it with Android Studio, or compile from command line using `gradlew`.
### Strings
The strings of the project are managed externally using [https://localazy.com](https://localazy.com) and shared with Element X iOS.
### Element X Android Gallery ### Element X Android Gallery
Once added to Localazy, translations can be checked screen per screen using our tool Element X Android Gallery, available at https://element-hq.github.io/element-x-android/. Once added to Localazy, translations can be checked screen per screen using our tool Element X Android Gallery, available at https://element-hq.github.io/element-x-android/.
Localazy syncs occur every Monday and the screenshots on this page are generated every Tuesday, so you'll have to wait to see your change appearing on Element X Android Gallery. Localazy syncs occur every Monday and the screenshots on this page are generated every Tuesday, so you'll have to wait to see your change appearing on Element X Android Gallery.
## I want to add a new feature to Element X Android
Thank you for contributing to the project! Please have a look in the [dedicated documentation](./docs/pull_request.md) about pull request.
Also, please keep in mind that any feature added to Element X Android needs to be added to [the iOS client](https://github.com/element-hq/element-x-ios) too, unless it's related to an Android OS only behaviour.
**IMPORTANT:** if you are adding new screens or modifying existing ones, this needs acceptance from the product and design teams before being merged. For this, it's better to start with a [feature request issue](https://github.com/element-hq/element-x-android/issues/new?template=enhancement.yml) describing the change you want to make and the motivation behind it instead of directly creating a pull request. This will allow the product and design teams to give feedback on the change before you start working on it, and avoid you doing work that might end up being rejected.
## I want to submit a PR to fix an issue
Please have a look in the [dedicated documentation](./docs/pull_request.md) about pull request.
Please check if a corresponding issue exists. If yes, please let us know in a comment that you're working on it.
If an issue does not exist yet, it may be relevant to open a new issue and let us know that you're implementing it.
### Kotlin ### Kotlin
This project is full Kotlin. Please do not write Java classes. This project is full Kotlin. Please do not write Java classes.

View file

@ -1,34 +0,0 @@
# Phase 1 Status — COMPLETE ✅
## Verification Date
2026-03-28
## What Was Verified
- APK: `app-gplay-x86_64-debug.apk` built from `phase1-dev` branch
- Installed on Android emulator `budtmo/docker-android:emulator_14.0` (emulator-5554)
- Signed in as `@testbot-elementx:sulkta.com` via OIDC (MAS at mas.sulkta.com)
- Opened DM room with `@cobb:sulkta.com`
- Typed `/pay` in message composer
## Result
✅ Slash command autocomplete appeared showing:
- Command: `/pay`
- Description: "Send ADA to someone"
## Phase 1 Bar (Option A) — All Conditions Met
- [x] App launches without crash
- [x] `/pay` appears in slash command autocomplete
- [x] Payment screens navigable (wired in DI graph)
- [x] No live testnet transaction required
## Build Info
- Gradle task: `:app:assembleGplayDebug`
- Branch: `phase1-dev`
- Final commit: `ad89eddfea`
- Build image: `mingc/android-build-box:latest` (Java 21)
## Key Fixes Applied
1. Metro DI scope mismatch: CardanoWalletManager removed CardanoClient dep (AppScope vs SessionScope)
2. WalletState constructor: all required fields populated
3. Packaging conflict: moshi-kotlin-codegen/lombok META-INF pickFirst
4. Build flavor: assembleGplayDebug (not fdroid, not plain assembleDebug)

78
SYNC.md
View file

@ -1,78 +0,0 @@
# Repo topology + upstream sync procedure
This repo is a fork of [`element-hq/element-x-android`](https://github.com/element-hq/element-x-android)
with a native Cardano wallet module added. The history is structured so that
staying current with upstream — and one day proposing our additions back —
stays possible.
## Branches
| Branch | Role |
|--------|------|
| `main` | Tracks the upstream commit we are currently based on. Fast-forwarded to `upstream/develop` when we deliberately pull in changes. Nothing coop-specific lives here. |
| `wallet` | `main` + all our wallet work. This is what we build APKs from. Linear history on top of `main`; rebased whenever `main` moves. |
| `archive/project-docs` | Frozen snapshot of the planning docs and screenshots that lived on the original orphan `main` branch. Not part of the active graph. |
When we ever want a clean "everything we'd propose upstream" branch, we cherry-pick
the wallet commits off `wallet` onto a fresh branch rooted at `main`. Because every
current commit on `wallet` is wallet-module work, that split is simple.
## Remotes
`origin` → this Gitea repo (LAN, via the Rackham SSH tunnel when working remotely).
Add upstream on any local clone:
```bash
git remote add upstream https://github.com/element-hq/element-x-android.git
git fetch upstream
```
## Sync with upstream
When you want to pick up the latest from `element-hq/element-x-android`:
```bash
# 1. Get the latest from upstream
git fetch upstream
# 2. Fast-forward main to upstream/develop
git checkout main
git merge --ff-only upstream/develop
git push origin main
# 3. Rebase wallet onto the new main
git checkout wallet
git rebase main
# → resolve conflicts, one commit at a time
# → conflict surface is small but real: our integration touches
# libraries/matrix/{api,impl}, libraries/textcomposer/impl,
# libraries/eventformatter/impl, libraries/mediaviewer/impl
# 4. Build + test the APK before force-pushing
./gradlew :app:assembleFdroidDebug # or mainnet variant
# 5. Push the rebased wallet branch (force-with-lease, not plain force)
git push --force-with-lease origin wallet
```
If the rebase gets ugly, abort and try merging instead:
```bash
git rebase --abort
git merge upstream/develop
# resolves in one shot, one merge commit, less clean history
```
## Why not a Gitea mirror?
Gitea only lets you configure a pull-mirror at repo-creation time, and mirroring
a whole repo also means we can't commit to it. We want to keep our own commits,
so upstream stays as a git remote you fetch from manually.
## License
Upstream is **AGPL-3.0**. Every binary we hand out must be accompanied by the
corresponding source under the same license. Keeping this Gitea repo accessible
to recipients of the APK satisfies that. Don't ship binaries without also making
the source reachable.

View file

@ -103,13 +103,13 @@ android {
logger.warnInBox("Building ${defaultConfig.applicationId} ($baseAppName) [$buildType]") logger.warnInBox("Building ${defaultConfig.applicationId} ($baseAppName) [$buildType]")
buildTypes { buildTypes {
val oidcRedirectSchemeBase = BuildTimeConfig.METADATA_HOST_REVERSED ?: "io.element.android" val oAuthRedirectSchemeBase = BuildTimeConfig.METADATA_HOST_REVERSED ?: "io.element.android"
getByName("debug") { getByName("debug") {
resValue("string", "app_name", "$baseAppName dbg") resValue("string", "app_name", "$baseAppName dbg")
resValue( resValue(
"string", "string",
"login_redirect_scheme", "login_redirect_scheme",
"$oidcRedirectSchemeBase.debug", "$oAuthRedirectSchemeBase.debug",
) )
applicationIdSuffix = ".debug" applicationIdSuffix = ".debug"
signingConfig = signingConfigs.getByName("debug") signingConfig = signingConfigs.getByName("debug")
@ -120,7 +120,7 @@ android {
resValue( resValue(
"string", "string",
"login_redirect_scheme", "login_redirect_scheme",
oidcRedirectSchemeBase, oAuthRedirectSchemeBase,
) )
signingConfig = signingConfigs.getByName("debug") signingConfig = signingConfigs.getByName("debug")
@ -157,7 +157,7 @@ android {
resValue( resValue(
"string", "string",
"login_redirect_scheme", "login_redirect_scheme",
"$oidcRedirectSchemeBase.nightly", "$oAuthRedirectSchemeBase.nightly",
) )
matchingFallbacks += listOf("release") matchingFallbacks += listOf("release")
signingConfig = signingConfigs.getByName("nightly") signingConfig = signingConfigs.getByName("nightly")
@ -208,7 +208,6 @@ android {
packaging { packaging {
resources.pickFirsts += setOf( resources.pickFirsts += setOf(
"META-INF/versions/9/OSGI-INF/MANIFEST.MF", "META-INF/versions/9/OSGI-INF/MANIFEST.MF",
"META-INF/gradle/incremental.annotation.processors",
) )
jniLibs { jniLibs {
@ -316,11 +315,6 @@ licensee {
allowUrl("https://asm.ow2.io/license.html") allowUrl("https://asm.ow2.io/license.html")
allowUrl("https://www.gnu.org/licenses/agpl-3.0.txt") allowUrl("https://www.gnu.org/licenses/agpl-3.0.txt")
allowUrl("https://github.com/mhssn95/compose-color-picker/blob/main/LICENSE") allowUrl("https://github.com/mhssn95/compose-color-picker/blob/main/LICENSE")
allowUrl("https://opensource.org/licenses/mit-license.php")
allowUrl("https://github.com/javaee/javax.annotation/blob/master/LICENSE")
allowUrl("https://www.bouncycastle.org/licence.html")
allowUrl("https://projectlombok.org/LICENSE")
allow("CC0-1.0")
ignoreDependencies("com.github.matrix-org", "matrix-analytics-events") ignoreDependencies("com.github.matrix-org", "matrix-analytics-events")
// Ignore dependency that are not third-party licenses to us. // Ignore dependency that are not third-party licenses to us.
ignoreDependencies(groupId = "io.element.android") ignoreDependencies(groupId = "io.element.android")

View file

@ -75,7 +75,7 @@
android:scheme="elementx" /> android:scheme="elementx" />
</intent-filter> </intent-filter>
<!-- <!--
Oidc redirection OAuth redirection
--> -->
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />

View file

@ -71,6 +71,7 @@ class MainActivity : NodeActivity() {
}.collectAsState(SemanticColorsLightDark.default) }.collectAsState(SemanticColorsLightDark.default)
ElementThemeApp( ElementThemeApp(
appPreferencesStore = appBindings.preferencesStore(), appPreferencesStore = appBindings.preferencesStore(),
featureFlagService = appBindings.featureFlagService(),
compoundLight = colors.light, compoundLight = colors.light,
compoundDark = colors.dark, compoundDark = colors.dark,
buildMeta = appBindings.buildMeta() buildMeta = appBindings.buildMeta()

View file

@ -10,14 +10,14 @@ package io.element.android.x.oidc
import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.ContributesBinding
import io.element.android.libraries.matrix.api.auth.OidcRedirectUrlProvider import io.element.android.libraries.matrix.api.auth.OAuthRedirectUrlProvider
import io.element.android.services.toolbox.api.strings.StringProvider import io.element.android.services.toolbox.api.strings.StringProvider
import io.element.android.x.R import io.element.android.x.R
@ContributesBinding(AppScope::class) @ContributesBinding(AppScope::class)
class DefaultOidcRedirectUrlProvider( class DefaultOAuthRedirectUrlProvider(
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
) : OidcRedirectUrlProvider { ) : OAuthRedirectUrlProvider {
override fun provide() = buildString { override fun provide() = buildString {
append(stringProvider.getString(R.string.login_redirect_scheme)) append(stringProvider.getString(R.string.login_redirect_scheme))
append(":/") append(":/")

View file

@ -2,6 +2,7 @@
<locale-config xmlns:android="http://schemas.android.com/apk/res/android"> <locale-config xmlns:android="http://schemas.android.com/apk/res/android">
<locale android:name="be"/> <locale android:name="be"/>
<locale android:name="bg"/> <locale android:name="bg"/>
<locale android:name="ca"/>
<locale android:name="cs"/> <locale android:name="cs"/>
<locale android:name="cy"/> <locale android:name="cy"/>
<locale android:name="da"/> <locale android:name="da"/>

View file

@ -13,13 +13,13 @@ import io.element.android.services.toolbox.test.strings.FakeStringProvider
import io.element.android.x.R import io.element.android.x.R
import org.junit.Test import org.junit.Test
class DefaultOidcRedirectUrlProviderTest { class DefaultOAuthRedirectUrlProviderTest {
@Test @Test
fun `test provide`() { fun `test provide`() {
val stringProvider = FakeStringProvider( val stringProvider = FakeStringProvider(
defaultResult = "str" defaultResult = "str"
) )
val sut = DefaultOidcRedirectUrlProvider( val sut = DefaultOAuthRedirectUrlProvider(
stringProvider = stringProvider, stringProvider = stringProvider,
) )
val result = sut.provide() val result = sut.provide()

View file

@ -48,6 +48,8 @@ android {
} }
dependencies { dependencies {
implementation(libs.coroutines.core)
implementation(libs.androidx.annotationjvm) implementation(libs.androidx.annotationjvm)
implementation(libs.androidx.corektx)
implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrix.api)
} }

View file

@ -0,0 +1,15 @@
/*
* 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.appconfig
object ProtectionConfig {
/**
* The maximum length of a room name, to limit attack vectors in room invite.
*/
const val MAX_ROOM_NAME_LENGTH = 128
}

View file

@ -33,13 +33,14 @@ dependencies {
implementation(projects.libraries.deeplink.api) implementation(projects.libraries.deeplink.api)
implementation(projects.libraries.featureflag.api) implementation(projects.libraries.featureflag.api)
implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrix.api)
implementation(projects.libraries.oidc.api) implementation(projects.libraries.oauth.api)
implementation(projects.libraries.preferences.api) implementation(projects.libraries.preferences.api)
implementation(projects.libraries.push.api) implementation(projects.libraries.push.api)
implementation(projects.libraries.pushproviders.api) implementation(projects.libraries.pushproviders.api)
implementation(projects.libraries.designsystem) implementation(projects.libraries.designsystem)
implementation(projects.libraries.matrixui) implementation(projects.libraries.matrixui)
implementation(projects.libraries.matrixmedia.api) implementation(projects.libraries.matrixmedia.api)
implementation(projects.libraries.sessionStorage.api)
implementation(projects.libraries.uiCommon) implementation(projects.libraries.uiCommon)
implementation(projects.libraries.uiStrings) implementation(projects.libraries.uiStrings)
implementation(projects.features.login.api) implementation(projects.features.login.api)
@ -59,7 +60,7 @@ dependencies {
testImplementation(projects.features.login.test) testImplementation(projects.features.login.test)
testImplementation(projects.features.share.test) testImplementation(projects.features.share.test)
testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.matrix.test)
testImplementation(projects.libraries.oidc.test) testImplementation(projects.libraries.oauth.test)
testImplementation(projects.libraries.preferences.test) testImplementation(projects.libraries.preferences.test)
testImplementation(projects.libraries.push.test) testImplementation(projects.libraries.push.test)
testImplementation(projects.libraries.pushproviders.test) testImplementation(projects.libraries.pushproviders.test)

View file

@ -54,6 +54,7 @@ import io.element.android.features.ftue.api.state.FtueService
import io.element.android.features.ftue.api.state.FtueState import io.element.android.features.ftue.api.state.FtueState
import io.element.android.features.home.api.HomeEntryPoint import io.element.android.features.home.api.HomeEntryPoint
import io.element.android.features.linknewdevice.api.LinkNewDeviceEntryPoint import io.element.android.features.linknewdevice.api.LinkNewDeviceEntryPoint
import io.element.android.features.location.api.live.ActiveLiveLocationShareManager
import io.element.android.features.networkmonitor.api.NetworkMonitor import io.element.android.features.networkmonitor.api.NetworkMonitor
import io.element.android.features.networkmonitor.api.NetworkStatus import io.element.android.features.networkmonitor.api.NetworkStatus
import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorContainer import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorContainer
@ -77,6 +78,7 @@ import io.element.android.libraries.designsystem.theme.ElementThemeApp
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.di.annotations.SessionCoroutineScope import io.element.android.libraries.di.annotations.SessionCoroutineScope
import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
@ -144,11 +146,13 @@ class LoggedInFlowNode(
private val syncService: SyncService, private val syncService: SyncService,
private val enterpriseService: EnterpriseService, private val enterpriseService: EnterpriseService,
private val appPreferencesStore: AppPreferencesStore, private val appPreferencesStore: AppPreferencesStore,
private val featureFlagService: FeatureFlagService,
private val buildMeta: BuildMeta, private val buildMeta: BuildMeta,
snackbarDispatcher: SnackbarDispatcher, snackbarDispatcher: SnackbarDispatcher,
private val analyticsService: AnalyticsService, private val analyticsService: AnalyticsService,
private val analyticsRoomListStateWatcher: AnalyticsRoomListStateWatcher, private val analyticsRoomListStateWatcher: AnalyticsRoomListStateWatcher,
private val createRoomEntryPoint: CreateRoomEntryPoint, private val createRoomEntryPoint: CreateRoomEntryPoint,
private val activeLiveLocationShareManager: ActiveLiveLocationShareManager,
) : BaseFlowNode<LoggedInFlowNode.NavTarget>( ) : BaseFlowNode<LoggedInFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
initialElement = NavTarget.Placeholder, initialElement = NavTarget.Placeholder,
@ -209,6 +213,7 @@ class LoggedInFlowNode(
super.onBuilt() super.onBuilt()
lifecycleScope.launch { lifecycleScope.launch {
sessionEnterpriseService.init() sessionEnterpriseService.init()
activeLiveLocationShareManager.setup()
} }
lifecycle.subscribe( lifecycle.subscribe(
onCreate = { onCreate = {
@ -217,7 +222,6 @@ class LoggedInFlowNode(
loggedInFlowProcessor.observeEvents(sessionCoroutineScope) loggedInFlowProcessor.observeEvents(sessionCoroutineScope)
matrixClient.sessionVerificationService.setListener(verificationListener) matrixClient.sessionVerificationService.setListener(verificationListener)
mediaPreviewConfigMigration() mediaPreviewConfigMigration()
sessionCoroutineScope.launch { sessionCoroutineScope.launch {
// Wait for the network to be connected before pre-fetching the max file upload size // Wait for the network to be connected before pre-fetching the max file upload size
networkMonitor.connectivity.first { networkStatus -> networkStatus == NetworkStatus.Connected } networkMonitor.connectivity.first { networkStatus -> networkStatus == NetworkStatus.Connected }
@ -378,9 +382,13 @@ class LoggedInFlowNode(
} }
is NavTarget.Room -> { is NavTarget.Room -> {
val joinedRoomCallback = object : JoinedRoomLoadedFlowNode.Callback { val joinedRoomCallback = object : JoinedRoomLoadedFlowNode.Callback {
override fun navigateToRoom(roomId: RoomId, serverNames: List<String>) { override fun onDone() {
backstack.pop()
}
override fun navigateToRoom(roomId: RoomId, serverNames: List<String>, clearBackStack: Boolean) {
lifecycleScope.launch { lifecycleScope.launch {
attachRoom(roomIdOrAlias = roomId.toRoomIdOrAlias(), serverNames = serverNames, clearBackstack = false) attachRoom(roomIdOrAlias = roomId.toRoomIdOrAlias(), serverNames = serverNames, clearBackstack = clearBackStack)
} }
} }
@ -671,6 +679,7 @@ class LoggedInFlowNode(
}.collectAsState(SemanticColorsLightDark.default) }.collectAsState(SemanticColorsLightDark.default)
ElementThemeApp( ElementThemeApp(
appPreferencesStore = appPreferencesStore, appPreferencesStore = appPreferencesStore,
featureFlagService = featureFlagService,
compoundLight = colors.light, compoundLight = colors.light,
compoundDark = colors.dark, compoundDark = colors.dark,
buildMeta = buildMeta, buildMeta = buildMeta,

View file

@ -63,8 +63,8 @@ import io.element.android.libraries.matrix.api.core.ThreadId
import io.element.android.libraries.matrix.api.core.asEventId import io.element.android.libraries.matrix.api.core.asEventId
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.api.permalink.PermalinkData
import io.element.android.libraries.oidc.api.OidcAction import io.element.android.libraries.oauth.api.OAuthAction
import io.element.android.libraries.oidc.api.OidcActionFlow import io.element.android.libraries.oauth.api.OAuthActionFlow
import io.element.android.libraries.sessionstorage.api.LoggedInState import io.element.android.libraries.sessionstorage.api.LoggedInState
import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.libraries.sessionstorage.api.SessionStore
import io.element.android.libraries.ui.common.nodes.emptyNode import io.element.android.libraries.ui.common.nodes.emptyNode
@ -95,7 +95,7 @@ class RootFlowNode(
private val signedOutEntryPoint: SignedOutEntryPoint, private val signedOutEntryPoint: SignedOutEntryPoint,
private val accountSelectEntryPoint: AccountSelectEntryPoint, private val accountSelectEntryPoint: AccountSelectEntryPoint,
private val intentResolver: IntentResolver, private val intentResolver: IntentResolver,
private val oidcActionFlow: OidcActionFlow, private val oAuthActionFlow: OAuthActionFlow,
private val featureFlagService: FeatureFlagService, private val featureFlagService: FeatureFlagService,
private val announcementService: AnnouncementService, private val announcementService: AnnouncementService,
private val analyticsService: AnalyticsService, private val analyticsService: AnalyticsService,
@ -392,7 +392,7 @@ class RootFlowNode(
navigateTo(resolvedIntent.deeplinkData) navigateTo(resolvedIntent.deeplinkData)
} }
is ResolvedIntent.Login -> onLoginLink(resolvedIntent.params) is ResolvedIntent.Login -> onLoginLink(resolvedIntent.params)
is ResolvedIntent.Oidc -> onOidcAction(resolvedIntent.oidcAction) is ResolvedIntent.OAuth -> onOAuthAction(resolvedIntent.oAuthAction)
is ResolvedIntent.Permalink -> navigateTo(resolvedIntent.permalinkData) is ResolvedIntent.Permalink -> navigateTo(resolvedIntent.permalinkData)
is ResolvedIntent.IncomingShare -> onIncomingShare(resolvedIntent.shareIntentData) is ResolvedIntent.IncomingShare -> onIncomingShare(resolvedIntent.shareIntentData)
} }
@ -529,8 +529,8 @@ class RootFlowNode(
} }
} }
private fun onOidcAction(oidcAction: OidcAction) { private fun onOAuthAction(oAuthAction: OAuthAction) {
oidcActionFlow.post(oidcAction) oAuthActionFlow.post(oAuthAction)
} }
private suspend fun attachSession(sessionId: SessionId): LoggedInFlowNode { private suspend fun attachSession(sessionId: SessionId): LoggedInFlowNode {

View file

@ -89,16 +89,14 @@ class SyncOrchestrator(
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
internal fun observeStates() = coroutineScope.launch { internal fun observeStates() = coroutineScope.launch {
Timber.tag(tag).d("start observing the app and network state") Timber.tag(tag).d("start observing the app and network state")
val isAppActiveFlows = listOf(
val isAppActiveFlow = combine(
appForegroundStateService.isInForeground, appForegroundStateService.isInForeground,
appForegroundStateService.isInCall, appForegroundStateService.isInCall,
appForegroundStateService.isSyncingNotificationEvent, appForegroundStateService.isSyncingNotificationEvent,
appForegroundStateService.hasRingingCall, appForegroundStateService.hasRingingCall,
) { isInForeground, isInCall, isSyncingNotificationEvent, hasRingingCall -> appForegroundStateService.isSharingLiveLocation
isInForeground || isInCall || isSyncingNotificationEvent || hasRingingCall )
} val isAppActiveFlow = combine(isAppActiveFlows) { actives -> actives.any { it } }
combine( combine(
// small debounce to avoid spamming startSync when the state is changing quickly in case of error. // small debounce to avoid spamming startSync when the state is changing quickly in case of error.
syncService.syncState.debounce(100.milliseconds), syncService.syncState.debounce(100.milliseconds),

View file

@ -18,13 +18,13 @@ import io.element.android.libraries.deeplink.api.DeeplinkData
import io.element.android.libraries.deeplink.api.DeeplinkParser import io.element.android.libraries.deeplink.api.DeeplinkParser
import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.api.permalink.PermalinkData
import io.element.android.libraries.matrix.api.permalink.PermalinkParser import io.element.android.libraries.matrix.api.permalink.PermalinkParser
import io.element.android.libraries.oidc.api.OidcAction import io.element.android.libraries.oauth.api.OAuthAction
import io.element.android.libraries.oidc.api.OidcIntentResolver import io.element.android.libraries.oauth.api.OAuthIntentResolver
import timber.log.Timber import timber.log.Timber
sealed interface ResolvedIntent { sealed interface ResolvedIntent {
data class Navigation(val deeplinkData: DeeplinkData) : ResolvedIntent data class Navigation(val deeplinkData: DeeplinkData) : ResolvedIntent
data class Oidc(val oidcAction: OidcAction) : ResolvedIntent data class OAuth(val oAuthAction: OAuthAction) : ResolvedIntent
data class Permalink(val permalinkData: PermalinkData) : ResolvedIntent data class Permalink(val permalinkData: PermalinkData) : ResolvedIntent
data class Login(val params: LoginParams) : ResolvedIntent data class Login(val params: LoginParams) : ResolvedIntent
data class IncomingShare(val shareIntentData: ShareIntentData) : ResolvedIntent data class IncomingShare(val shareIntentData: ShareIntentData) : ResolvedIntent
@ -34,7 +34,7 @@ sealed interface ResolvedIntent {
class IntentResolver( class IntentResolver(
private val deeplinkParser: DeeplinkParser, private val deeplinkParser: DeeplinkParser,
private val loginIntentResolver: LoginIntentResolver, private val loginIntentResolver: LoginIntentResolver,
private val oidcIntentResolver: OidcIntentResolver, private val oAuthIntentResolver: OAuthIntentResolver,
private val permalinkParser: PermalinkParser, private val permalinkParser: PermalinkParser,
private val shareIntentHandler: ShareIntentHandler, private val shareIntentHandler: ShareIntentHandler,
) { ) {
@ -45,9 +45,9 @@ class IntentResolver(
val deepLinkData = deeplinkParser.getFromIntent(intent) val deepLinkData = deeplinkParser.getFromIntent(intent)
if (deepLinkData != null) return ResolvedIntent.Navigation(deepLinkData) if (deepLinkData != null) return ResolvedIntent.Navigation(deepLinkData)
// Coming during login using Oidc? // Coming during login using OAuth?
val oidcAction = oidcIntentResolver.resolve(intent) val oAuthAction = oAuthIntentResolver.resolve(intent)
if (oidcAction != null) return ResolvedIntent.Oidc(oidcAction) if (oAuthAction != null) return ResolvedIntent.OAuth(oAuthAction)
val actionViewData = intent val actionViewData = intent
.takeIf { it.action == Intent.ACTION_VIEW } .takeIf { it.action == Intent.ACTION_VIEW }

View file

@ -31,7 +31,6 @@ import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.encryption.EncryptionService
import io.element.android.libraries.matrix.api.encryption.RecoveryState import io.element.android.libraries.matrix.api.encryption.RecoveryState
import io.element.android.libraries.matrix.api.oidc.AccountManagementAction
import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.roomlist.RoomListService
import io.element.android.libraries.matrix.api.sync.SlidingSyncVersion import io.element.android.libraries.matrix.api.sync.SlidingSyncVersion
import io.element.android.libraries.matrix.api.sync.SyncService import io.element.android.libraries.matrix.api.sync.SyncService
@ -177,7 +176,6 @@ class LoggedInPresenter(
} }
private fun CoroutineScope.preloadAccountManagementUrl() = launch { private fun CoroutineScope.preloadAccountManagementUrl() = launch {
matrixClient.getAccountManagementUrl(AccountManagementAction.Profile) matrixClient.getAccountManagementUrl(null)
matrixClient.getAccountManagementUrl(AccountManagementAction.DevicesList)
} }
} }

View file

@ -82,7 +82,8 @@ class JoinedRoomLoadedFlowNode(
plugins = plugins, plugins = plugins,
), DependencyInjectionGraphOwner { ), DependencyInjectionGraphOwner {
interface Callback : Plugin { interface Callback : Plugin {
fun navigateToRoom(roomId: RoomId, serverNames: List<String>) fun onDone()
fun navigateToRoom(roomId: RoomId, serverNames: List<String>, clearBackStack: Boolean = false)
fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean)
fun navigateToGlobalNotificationSettings() fun navigateToGlobalNotificationSettings()
fun navigateToDeveloperSettings() fun navigateToDeveloperSettings()
@ -142,6 +143,10 @@ class JoinedRoomLoadedFlowNode(
private fun createRoomDetailsNode(buildContext: BuildContext, initialTarget: RoomDetailsEntryPoint.InitialTarget): Node { private fun createRoomDetailsNode(buildContext: BuildContext, initialTarget: RoomDetailsEntryPoint.InitialTarget): Node {
val callback = object : RoomDetailsEntryPoint.Callback { val callback = object : RoomDetailsEntryPoint.Callback {
override fun onDone() {
callback.onDone()
}
override fun navigateToGlobalNotificationSettings() { override fun navigateToGlobalNotificationSettings() {
callback.navigateToGlobalNotificationSettings() callback.navigateToGlobalNotificationSettings()
} }
@ -150,7 +155,7 @@ class JoinedRoomLoadedFlowNode(
callback.navigateToDeveloperSettings() callback.navigateToDeveloperSettings()
} }
override fun navigateToRoom(roomId: RoomId, serverNames: List<String>) { override fun navigateToRoom(roomId: RoomId, serverNames: List<String>, clearBackStack: Boolean) {
callback.navigateToRoom(roomId, serverNames) callback.navigateToRoom(roomId, serverNames)
} }

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="banner_migrate_to_native_sliding_sync_action">"Tanca sessió i actualitza"</string>
<string name="banner_migrate_to_native_sliding_sync_app_force_logout_title">"%1$s ja no admet el protocol antic. Tanca sessió i torna a entrar per continuar utilitzant l\'aplicació."</string>
<string name="banner_migrate_to_native_sliding_sync_force_logout_title">"El servidor utilitzat ja no admet el protocol antic. Tanca sessió i torna-la a iniciar per continuar utilitzant l\'aplicació."</string>
</resources>

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="banner_migrate_to_native_sliding_sync_action">"登出并升级"</string> <string name="banner_migrate_to_native_sliding_sync_action">"注销并升级"</string>
<string name="banner_migrate_to_native_sliding_sync_app_force_logout_title">"%1$s 不再支持旧协议。请注销并重新登录以继续使用该应用程序。"</string> <string name="banner_migrate_to_native_sliding_sync_app_force_logout_title">"%1$s 不再支持旧协议。请注销并重新登录以继续使用该应用程序。"</string>
<string name="banner_migrate_to_native_sliding_sync_force_logout_title">"您的服务器不再支持旧协议。请登出并重新登录以继续使用此应用。"</string> <string name="banner_migrate_to_native_sliding_sync_force_logout_title">"你的主服务器不再支持旧协议。请注销并重新登录以继续使用此 app。"</string>
</resources> </resources>

View file

@ -26,8 +26,8 @@ import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_SESSION_ID
import io.element.android.libraries.matrix.test.A_THREAD_ID import io.element.android.libraries.matrix.test.A_THREAD_ID
import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser
import io.element.android.libraries.oidc.api.OidcAction import io.element.android.libraries.oauth.api.OAuthAction
import io.element.android.libraries.oidc.test.FakeOidcIntentResolver import io.element.android.libraries.oauth.test.FakeOAuthIntentResolver
import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaError
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@ -170,9 +170,9 @@ class IntentResolverTest {
} }
@Test @Test
fun `test resolve oidc`() { fun `test resolve OAuth`() {
val sut = createIntentResolver( val sut = createIntentResolver(
oidcIntentResolverResult = { OidcAction.GoBack() }, oAuthIntentResolverResult = { OAuthAction.GoBack() },
) )
val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply { val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply {
action = Intent.ACTION_VIEW action = Intent.ACTION_VIEW
@ -180,8 +180,8 @@ class IntentResolverTest {
} }
val result = sut.resolve(intent) val result = sut.resolve(intent)
assertThat(result).isEqualTo( assertThat(result).isEqualTo(
ResolvedIntent.Oidc( ResolvedIntent.OAuth(
oidcAction = OidcAction.GoBack() oAuthAction = OAuthAction.GoBack()
) )
) )
} }
@ -194,7 +194,7 @@ class IntentResolverTest {
val sut = createIntentResolver( val sut = createIntentResolver(
loginIntentResolverResult = { null }, loginIntentResolverResult = { null },
permalinkParserResult = { permalinkData }, permalinkParserResult = { permalinkData },
oidcIntentResolverResult = { null }, oAuthIntentResolverResult = { null },
) )
val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply { val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply {
action = Intent.ACTION_VIEW action = Intent.ACTION_VIEW
@ -213,7 +213,7 @@ class IntentResolverTest {
val sut = createIntentResolver( val sut = createIntentResolver(
permalinkParserResult = { PermalinkData.FallbackLink(Uri.parse("https://matrix.org")) }, permalinkParserResult = { PermalinkData.FallbackLink(Uri.parse("https://matrix.org")) },
loginIntentResolverResult = { null }, loginIntentResolverResult = { null },
oidcIntentResolverResult = { null }, oAuthIntentResolverResult = { null },
) )
val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply { val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply {
action = Intent.ACTION_VIEW action = Intent.ACTION_VIEW
@ -230,7 +230,7 @@ class IntentResolverTest {
) )
val sut = createIntentResolver( val sut = createIntentResolver(
permalinkParserResult = { permalinkData }, permalinkParserResult = { permalinkData },
oidcIntentResolverResult = { null }, oAuthIntentResolverResult = { null },
) )
val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply { val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply {
action = Intent.ACTION_BATTERY_LOW action = Intent.ACTION_BATTERY_LOW
@ -244,7 +244,7 @@ class IntentResolverTest {
fun `test incoming share simple`() { fun `test incoming share simple`() {
val shareIntentData = ShareIntentData.PlainText("Hello") val shareIntentData = ShareIntentData.PlainText("Hello")
val sut = createIntentResolver( val sut = createIntentResolver(
oidcIntentResolverResult = { null }, oAuthIntentResolverResult = { null },
onIncomingShareIntent = { shareIntentData }, onIncomingShareIntent = { shareIntentData },
) )
val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply { val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply {
@ -260,7 +260,7 @@ class IntentResolverTest {
val fileUri = "content://com.example.app/file1.jpg".toUri() val fileUri = "content://com.example.app/file1.jpg".toUri()
val shareIntentData = ShareIntentData.Uris(text = "Hello", uris = listOf(UriToShare(fileUri, "image/jpg"))) val shareIntentData = ShareIntentData.Uris(text = "Hello", uris = listOf(UriToShare(fileUri, "image/jpg")))
val sut = createIntentResolver( val sut = createIntentResolver(
oidcIntentResolverResult = { null }, oAuthIntentResolverResult = { null },
onIncomingShareIntent = { shareIntentData }, onIncomingShareIntent = { shareIntentData },
) )
val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply { val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply {
@ -277,7 +277,7 @@ class IntentResolverTest {
val sut = createIntentResolver( val sut = createIntentResolver(
permalinkParserResult = { PermalinkData.FallbackLink(Uri.parse("https://matrix.org")) }, permalinkParserResult = { PermalinkData.FallbackLink(Uri.parse("https://matrix.org")) },
loginIntentResolverResult = { null }, loginIntentResolverResult = { null },
oidcIntentResolverResult = { null }, oAuthIntentResolverResult = { null },
) )
val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply { val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply {
action = Intent.ACTION_VIEW action = Intent.ACTION_VIEW
@ -292,7 +292,7 @@ class IntentResolverTest {
val aLoginParams = LoginParams("accountProvider", null) val aLoginParams = LoginParams("accountProvider", null)
val sut = createIntentResolver( val sut = createIntentResolver(
loginIntentResolverResult = { aLoginParams }, loginIntentResolverResult = { aLoginParams },
oidcIntentResolverResult = { null }, oAuthIntentResolverResult = { null },
) )
val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply { val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply {
action = Intent.ACTION_VIEW action = Intent.ACTION_VIEW
@ -306,7 +306,7 @@ class IntentResolverTest {
deeplinkParserResult: DeeplinkData? = null, deeplinkParserResult: DeeplinkData? = null,
permalinkParserResult: (String) -> PermalinkData = { lambdaError() }, permalinkParserResult: (String) -> PermalinkData = { lambdaError() },
loginIntentResolverResult: (String) -> LoginParams? = { lambdaError() }, loginIntentResolverResult: (String) -> LoginParams? = { lambdaError() },
oidcIntentResolverResult: (Intent) -> OidcAction? = { lambdaError() }, oAuthIntentResolverResult: (Intent) -> OAuthAction? = { lambdaError() },
onIncomingShareIntent: (Intent) -> ShareIntentData? = { null }, onIncomingShareIntent: (Intent) -> ShareIntentData? = { null },
): IntentResolver { ): IntentResolver {
return IntentResolver( return IntentResolver(
@ -314,8 +314,8 @@ class IntentResolverTest {
loginIntentResolver = FakeLoginIntentResolver( loginIntentResolver = FakeLoginIntentResolver(
parseResult = loginIntentResolverResult, parseResult = loginIntentResolverResult,
), ),
oidcIntentResolver = FakeOidcIntentResolver( oAuthIntentResolver = FakeOAuthIntentResolver(
resolveResult = oidcIntentResolverResult, resolveResult = oAuthIntentResolverResult,
), ),
permalinkParser = FakePermalinkParser( permalinkParser = FakePermalinkParser(
result = permalinkParserResult result = permalinkParserResult

View file

@ -21,7 +21,7 @@ import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.encryption.EncryptionService
import io.element.android.libraries.matrix.api.encryption.RecoveryState import io.element.android.libraries.matrix.api.encryption.RecoveryState
import io.element.android.libraries.matrix.api.oidc.AccountManagementAction import io.element.android.libraries.matrix.api.oauth.AccountManagementAction
import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.roomlist.RoomListService
import io.element.android.libraries.matrix.api.sync.SlidingSyncVersion import io.element.android.libraries.matrix.api.sync.SlidingSyncVersion
import io.element.android.libraries.matrix.api.sync.SyncState import io.element.android.libraries.matrix.api.sync.SyncState
@ -71,7 +71,7 @@ class LoggedInPresenterTest {
} }
@Test @Test
fun `present - ensure that account urls are preloaded`() = runTest { fun `present - ensure that account url is preloaded`() = runTest {
val accountManagementUrlResult = lambdaRecorder<AccountManagementAction?, Result<String?>> { Result.success("aUrl") } val accountManagementUrlResult = lambdaRecorder<AccountManagementAction?, Result<String?>> { Result.success("aUrl") }
val matrixClient = FakeMatrixClient( val matrixClient = FakeMatrixClient(
accountManagementUrlResult = accountManagementUrlResult, accountManagementUrlResult = accountManagementUrlResult,
@ -81,11 +81,8 @@ class LoggedInPresenterTest {
).test { ).test {
awaitItem() awaitItem()
advanceUntilIdle() advanceUntilIdle()
accountManagementUrlResult.assertions().isCalledExactly(2) accountManagementUrlResult.assertions().isCalledOnce()
.withSequence( .with(value(null))
listOf(value(AccountManagementAction.Profile)),
listOf(value(AccountManagementAction.DevicesList)),
)
} }
} }

View file

@ -13,7 +13,8 @@ import io.element.android.libraries.matrix.api.permalink.PermalinkData
import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaError
class FakeJoinedRoomLoadedFlowNodeCallback : JoinedRoomLoadedFlowNode.Callback { class FakeJoinedRoomLoadedFlowNodeCallback : JoinedRoomLoadedFlowNode.Callback {
override fun navigateToRoom(roomId: RoomId, serverNames: List<String>) = lambdaError() override fun onDone() = lambdaError()
override fun navigateToRoom(roomId: RoomId, serverNames: List<String>, clearBackStack: Boolean) = lambdaError()
override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) = lambdaError() override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) = lambdaError()
override fun navigateToGlobalNotificationSettings() = lambdaError() override fun navigateToGlobalNotificationSettings() = lambdaError()
override fun navigateToDeveloperSettings() = lambdaError() override fun navigateToDeveloperSettings() = lambdaError()

View file

@ -46,12 +46,15 @@ allprojects {
config.from(files("$rootDir/tools/detekt/detekt.yml")) config.from(files("$rootDir/tools/detekt/detekt.yml"))
} }
dependencies { dependencies {
detektPlugins("io.nlopez.compose.rules:detekt:0.5.6") detektPlugins("io.nlopez.compose.rules:detekt:0.5.8")
detektPlugins(project(":tests:detekt-rules")) detektPlugins(project(":tests:detekt-rules"))
} }
tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach { tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach {
exclude("io/element/android/tests/konsist/failures/**") exclude("io/element/android/tests/konsist/failures/**")
// This file comes from another project and we want to keep it as close to the original as possible
exclude("org/rustls/platformverifier/**")
} }
// KtLint // KtLint
@ -79,6 +82,9 @@ allprojects {
// This file comes from another project and we want to keep it as close to the original as possible // This file comes from another project and we want to keep it as close to the original as possible
exclude("**/SafeChildrenTransitionScope.kt") exclude("**/SafeChildrenTransitionScope.kt")
// This file comes from another project and we want to keep it as close to the original as possible
exclude("org/rustls/platformverifier/**")
} }
} }
// Dependency check // Dependency check

View file

@ -144,6 +144,11 @@ Prerequisites:
export ANDROID_HOME=$HOME/android/sdk export ANDROID_HOME=$HOME/android/sdk
``` ```
* On macos ensure gnu-getopt is installed
```
brew install gnu-getopt
```
You can then build the Rust SDK by running the script You can then build the Rust SDK by running the script
[`tools/sdk/build-rust-sdk`](../tools/sdk/build-rust-sdk). Type [`tools/sdk/build-rust-sdk`](../tools/sdk/build-rust-sdk). Type
`./tools/sdk/build-rust-sdk --help` for help. `./tools/sdk/build-rust-sdk --help` for help.

View file

@ -1,4 +1,4 @@
This file contains some rough notes about Oidc implementation, with some examples of actual data. This file contains some rough notes about OAuth implementation, with some examples of actual data.
[ios implementation](https://github.com/element-hq/element-x-ios/compare/develop...doug/oidc-temp) [ios implementation](https://github.com/element-hq/element-x-ios/compare/develop...doug/oidc-temp)
@ -25,7 +25,7 @@ tosUri = "https://element.io/user-terms-of-service",
policyUri = "https://element.io/privacy" policyUri = "https://element.io/privacy"
Example of OidcData (from presentUrl callback): Example of OAuthData (from presentUrl callback):
url: https://auth-oidc.lab.element.dev/authorize?response_type=code&client_id=01GYCAGG3PA70CJ97ZVP0WFJY3&redirect_uri=io.element%3A%2Fcallback&scope=openid+urn%3Amatrix%3Aorg.matrix.msc2967.client%3Aapi%3A*+urn%3Amatrix%3Aorg.matrix.msc2967.client%3Adevice%3AYAgcPW4mcG&state=ex6mNJVFZ5jn9wL8&nonce=NZ93DOyIGQd9exPQ&code_challenge_method=S256&code_challenge=FFRcPALNSPCh-ZgpyTRFu_h8NZJVncfvihbfT9CyX8U&prompt=consent url: https://auth-oidc.lab.element.dev/authorize?response_type=code&client_id=01GYCAGG3PA70CJ97ZVP0WFJY3&redirect_uri=io.element%3A%2Fcallback&scope=openid+urn%3Amatrix%3Aorg.matrix.msc2967.client%3Aapi%3A*+urn%3Amatrix%3Aorg.matrix.msc2967.client%3Adevice%3AYAgcPW4mcG&state=ex6mNJVFZ5jn9wL8&nonce=NZ93DOyIGQd9exPQ&code_challenge_method=S256&code_challenge=FFRcPALNSPCh-ZgpyTRFu_h8NZJVncfvihbfT9CyX8U&prompt=consent
Formatted url: Formatted url:
@ -43,8 +43,8 @@ https://auth-oidc.lab.element.dev/authorize?
state: ex6mNJVFZ5jn9wL8 state: ex6mNJVFZ5jn9wL8
Oidc client example: https://github.com/matrix-org/matrix-rust-sdk/blob/39ad8a46801fb4317a777ebf895822b3675b709c/examples/oidc_cli/src/main.rs OAuth client example: https://github.com/matrix-org/matrix-rust-sdk/blob/39ad8a46801fb4317a777ebf895822b3675b709c/examples/oidc_cli/src/main.rs
Oidc sdk doc: https://github.com/matrix-org/matrix-rust-sdk/blob/39ad8a46801fb4317a777ebf895822b3675b709c/crates/matrix-sdk/src/oidc.rs OAuth sdk doc: https://github.com/matrix-org/matrix-rust-sdk/blob/39ad8a46801fb4317a777ebf895822b3675b709c/crates/matrix-sdk/src/oidc.rs
Test server: Test server:

@ -1 +1 @@
Subproject commit cdde60c158ecd0987a3ba6fd79a4617551aff463 Subproject commit 6781da90aae61cf77dcdbc543e18d76411d578b4

View file

@ -0,0 +1,2 @@
Main changes in this version: several bug fixes.
Full changelog: https://github.com/element-hq/element-x-android/releases

View file

@ -0,0 +1,2 @@
Main changes in this version: bug fixes and improvements.
Full changelog: https://github.com/element-hq/element-x-android/releases

View file

@ -0,0 +1,2 @@
Main changes in this version: improvements in Element Call, room knocking and room directory are now available, improvements on DMs.
Full changelog: https://github.com/element-hq/element-x-android/releases

View file

@ -0,0 +1,2 @@
Main changes in this version: bug fixes and improvements.
Full changelog: https://github.com/element-hq/element-x-android/releases

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_analytics_settings_help_us_improve">"Comparteix dades d\'ús anònimes per ajudar-nos a identificar problemes."</string>
<string name="screen_analytics_settings_read_terms">"Pots llegir tots els nostres termes %1$s."</string>
<string name="screen_analytics_settings_read_terms_content_link">"aquí"</string>
<string name="screen_analytics_settings_share_data">"Comparteix dades analítiques"</string>
</resources>

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_analytics_settings_help_us_improve">"問題発見のため、匿名の使用データの共有にご協力ください。"</string> <string name="screen_analytics_settings_help_us_improve">"改善のため、匿名の使用データの共有にご協力ください。"</string>
<string name="screen_analytics_settings_read_terms">"利用規約の全文を%1$sから確認することができます。"</string> <string name="screen_analytics_settings_read_terms">"規約の全文は%1$sから確認することができます。"</string>
<string name="screen_analytics_settings_read_terms_content_link">"こちら"</string> <string name="screen_analytics_settings_read_terms_content_link">"こちら"</string>
<string name="screen_analytics_settings_share_data">"使用データを共有"</string> <string name="screen_analytics_settings_share_data">"使用データを共有"</string>
</resources> </resources>

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_analytics_settings_help_us_improve">"共享匿名使用数据以帮助我们排查问题。"</string> <string name="screen_analytics_settings_help_us_improve">"共享匿名使用数据以帮助我们排查问题。"</string>
<string name="screen_analytics_settings_read_terms">"您可以阅读我们的所有条款 %1$s。"</string> <string name="screen_analytics_settings_read_terms">"你可以点击 %1$s 阅读我们的所有条款。"</string>
<string name="screen_analytics_settings_read_terms_content_link">"此处"</string> <string name="screen_analytics_settings_read_terms_content_link">"此处"</string>
<string name="screen_analytics_settings_share_data">"共享分析数据"</string> <string name="screen_analytics_settings_share_data">"共享分析数据"</string>
</resources> </resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_analytics_prompt_data_usage">"No registrarem ni elaborarem perfils de cap dada personal"</string>
<string name="screen_analytics_prompt_help_us_improve">"Comparteix dades d\'ús anònimes per ajudar-nos a identificar problemes."</string>
<string name="screen_analytics_prompt_read_terms">"Pots llegir tots els nostres termes %1$s."</string>
<string name="screen_analytics_prompt_read_terms_content_link">"aquí"</string>
<string name="screen_analytics_prompt_settings">"Ho pots desactivar en qualsevol moment"</string>
<string name="screen_analytics_prompt_third_party_sharing">"No compartirem les teves dades amb tercers"</string>
<string name="screen_analytics_prompt_title">"Ajuda\'ns a millorar %1$s"</string>
</resources>

View file

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_analytics_prompt_data_usage">"いかなる個人情報も記録, 分析されることはありません"</string> <string name="screen_analytics_prompt_data_usage">"いかなる個人情報も記録, 分析されることはありません"</string>
<string name="screen_analytics_prompt_help_us_improve">"問題発見のため、匿名の使用データの共有にご協力ください。"</string> <string name="screen_analytics_prompt_help_us_improve">"改善のため、匿名の使用データの共有にご協力ください。"</string>
<string name="screen_analytics_prompt_read_terms">"利用規約の全文を%1$sから確認することができます。"</string> <string name="screen_analytics_prompt_read_terms">"規約の全文は%1$sから確認することができます。"</string>
<string name="screen_analytics_prompt_read_terms_content_link">"こちら"</string> <string name="screen_analytics_prompt_read_terms_content_link">"こちら"</string>
<string name="screen_analytics_prompt_settings">"いつでも設定は変更できます"</string> <string name="screen_analytics_prompt_settings">"いつでも設定は変更できます"</string>
<string name="screen_analytics_prompt_third_party_sharing">"情報が第三者に共有されることはありません"</string> <string name="screen_analytics_prompt_third_party_sharing">"情報が第三者に共有されることはありません"</string>

View file

@ -2,9 +2,9 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_analytics_prompt_data_usage">"我们不会记录或分析任何个人数据"</string> <string name="screen_analytics_prompt_data_usage">"我们不会记录或分析任何个人数据"</string>
<string name="screen_analytics_prompt_help_us_improve">"共享匿名使用数据以帮助我们排查问题。"</string> <string name="screen_analytics_prompt_help_us_improve">"共享匿名使用数据以帮助我们排查问题。"</string>
<string name="screen_analytics_prompt_read_terms">"您可以阅读我们的所有条款 %1$s。"</string> <string name="screen_analytics_prompt_read_terms">"你可以点击 %1$s 阅读我们的所有条款。"</string>
<string name="screen_analytics_prompt_read_terms_content_link">"此处"</string> <string name="screen_analytics_prompt_read_terms_content_link">"此处"</string>
<string name="screen_analytics_prompt_settings">"可以随时关闭此功能"</string> <string name="screen_analytics_prompt_settings">"可以随时关闭此功能"</string>
<string name="screen_analytics_prompt_third_party_sharing">"我们不会与第三方共享的数据"</string> <string name="screen_analytics_prompt_third_party_sharing">"我们不会与第三方共享的数据"</string>
<string name="screen_analytics_prompt_title">"帮助改进 %1$s"</string> <string name="screen_analytics_prompt_title">"帮助改进 %1$s"</string>
</resources> </resources>

View file

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_space_announcement_item4">"Присъединете се към обществени пространства"</string>
</resources>

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_space_announcement_item1">"Zobrazit prostory, které jste vytvořili nebo ke kterým jste se připojili"</string>
<string name="screen_space_announcement_item2">"Přijmout nebo odmítnout pozvánky do prostorů"</string>
<string name="screen_space_announcement_item3">"Objevte všechny místnosti, do kterých můžete vstoupit ve svých prostorech"</string>
<string name="screen_space_announcement_item4">"Připojit se k veřejným prostorům"</string>
<string name="screen_space_announcement_item5">"Opustit všechny prostory, ke kterým jste se připojili"</string>
<string name="screen_space_announcement_notice">"Filtrování, vytváření a správa prostorů bude brzy k dispozici."</string>
<string name="screen_space_announcement_subtitle">"Vítejte v beta verzi prostorů! S touto první verzí můžete:"</string>
<string name="screen_space_announcement_title">"Představujeme prostory"</string>
</resources>

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_space_announcement_item1">"Se klynger, du har oprettet eller tilmeldt dig"</string>
<string name="screen_space_announcement_item2">"Acceptere eller afvise invitationer til klynger"</string>
<string name="screen_space_announcement_item3">"Finde alle rum, du kan deltage i, i dine klynger"</string>
<string name="screen_space_announcement_item4">"Deltage i offentlige klynger"</string>
<string name="screen_space_announcement_item5">"Forlade de klynger, du har tilsluttet dig"</string>
<string name="screen_space_announcement_notice">"Filtrering, oprettelse og administration af klynger kommer snart."</string>
<string name="screen_space_announcement_subtitle">"Velkommen til betaversionen af Klynger! Med denne første version kan du:"</string>
<string name="screen_space_announcement_title">"Introduktion til Klynger"</string>
</resources>

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_space_announcement_item1">"Von dir erstellte oder beigetretene Spaces anzeigen"</string>
<string name="screen_space_announcement_item2">"Einladungen zu Spaces annehmen oder ablehnen"</string>
<string name="screen_space_announcement_item3">"Chats innerhalb deiner Spaces entdecken, um ihnen beizutreten"</string>
<string name="screen_space_announcement_item4">"Öffentlichen Spaces beitreten"</string>
<string name="screen_space_announcement_item5">"Spaces verlassen, bei denen du Mitglied bist"</string>
<string name="screen_space_announcement_notice">"Das Filtern, Erstellen und Verwalten von Spaces ist bald verfügbar."</string>
<string name="screen_space_announcement_subtitle">"Willkommen bei der Beta-Version von Spaces! Mit dieser ersten Version kannst du:"</string>
<string name="screen_space_announcement_title">"Einführung in Spaces"</string>
</resources>

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_space_announcement_item1">"Δείτε τους χώρους που έχετε δημιουργήσει ή στους οποίους έχετε εγγραφεί"</string>
<string name="screen_space_announcement_item2">"Να αποδεχθείτε ή να απορρίψετε προσκλήσεις σε χώρους"</string>
<string name="screen_space_announcement_item3">"Να ανακαλύψτε όλες τις αίθουσες που μπορείτε να συμμετάσχετε στους χώρους σας"</string>
<string name="screen_space_announcement_item4">"Να συμμετάσχετε σε δημόσιους χώρους"</string>
<string name="screen_space_announcement_item5">"Να αποχωρήστε από χώρους στους οποίους έχετε συμμετάσχει"</string>
<string name="screen_space_announcement_notice">"Το φιλτράρισμα, η δημιουργία και η διαχείριση χώρων θα είναι σύντομα διαθέσιμα."</string>
<string name="screen_space_announcement_subtitle">"Καλώς ορίσατε στην δοκιμαστική έκδοση των Χώρων! Με αυτήν την πρώτη έκδοση μπορείτε:"</string>
<string name="screen_space_announcement_title">"Παρουσιάζοντας τους Χώρους"</string>
</resources>

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_space_announcement_item1">"Vaadata kogukondi, mille oled loonud või millega oled liitunud"</string>
<string name="screen_space_announcement_item2">"Nõustuda kutsetega liitumiseks kogukonnaga või sellest keelduda"</string>
<string name="screen_space_announcement_item3">"Uurida neis kogukondades leiduvaid jututube ning nendega liituda"</string>
<string name="screen_space_announcement_item4">"Liituda avalike kogukondadega"</string>
<string name="screen_space_announcement_item5">"Lahkuda kogukonnast, millega oled liitunud"</string>
<string name="screen_space_announcement_notice">"Kogukondade filtreerimine, loomine ja haldamine lisandub peagi"</string>
<string name="screen_space_announcement_subtitle">"Tere tulemast kasutama kogukondade beetaversiooni! Selles esimeses versioonis saad sa:"</string>
<string name="screen_space_announcement_title">"Võtame kasutusele kogukonnad"</string>
</resources>

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_space_announcement_item1">"دیدن فضاهایی که ساخته یا پیوسته‌اید"</string>
<string name="screen_space_announcement_item2">"پذیرش یا رد دعوت‌ها به فضاها"</string>
<string name="screen_space_announcement_item3">"کشف تمامی اتاق‌هایی که می‌توانید در فضاهایتان بپیوندید"</string>
<string name="screen_space_announcement_item4">"پیوستن به فضاهای عمومی"</string>
<string name="screen_space_announcement_item5">"ترک هر فضایی که پیوسته‌اید"</string>
<string name="screen_space_announcement_notice">"پالایش، ایجاد و مدیریت کردن فضاها به زودی."</string>
<string name="screen_space_announcement_subtitle">"به نگارش آزمایشی فضاها خوش آمدید! در این نگارش می‌توانید:"</string>
<string name="screen_space_announcement_title">"معرّفی فضاها"</string>
</resources>

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_space_announcement_item1">"Nähdä luomasi tai liittymäsi tilat"</string>
<string name="screen_space_announcement_item2">"Hyväksyä tai hylätä kutsuja tiloihin"</string>
<string name="screen_space_announcement_item3">"Löytää kaikki huoneet, joihin voit liittyä tiloissasi"</string>
<string name="screen_space_announcement_item4">"Liittyä julkisiin tiloihin"</string>
<string name="screen_space_announcement_item5">"Poistua mistä tahansa tilasta, johon olet liittynyt"</string>
<string name="screen_space_announcement_notice">"Tilojen suodatus, luominen ja hallinta on tulossa pian."</string>
<string name="screen_space_announcement_subtitle">"Tervetuloa tilojen beetaversioon! Tämän ensimmäisen version avulla voit:"</string>
<string name="screen_space_announcement_title">"Esittelyssä tilat"</string>
</resources>

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_space_announcement_item1">"Voir les espaces que vous avez créés ou rejoints"</string>
<string name="screen_space_announcement_item2">"Accepter ou refuser les invitations aux espaces"</string>
<string name="screen_space_announcement_item3">"Découvrir les salons que vous pouvez joindre depuis vos espaces"</string>
<string name="screen_space_announcement_item4">"Rejoindre les espaces publics"</string>
<string name="screen_space_announcement_item5">"Quitter les espaces dont vous êtes membre."</string>
<string name="screen_space_announcement_notice">"Le filtrage, la création et la gestion des espaces seront bientôt disponibles."</string>
<string name="screen_space_announcement_subtitle">"Bienvenue dans la version bêta des espaces! Avec cette première version, vous pourrez :"</string>
<string name="screen_space_announcement_title">"Ajout des espaces"</string>
</resources>

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_space_announcement_item1">"Pregledajte prostore koje ste stvorili ili kojima ste se pridružili"</string>
<string name="screen_space_announcement_item2">"Prihvatite ili odbijte pozivnice za prostore"</string>
<string name="screen_space_announcement_item3">"Otkrijte sve sobe kojima se možete pridružiti u svojim prostorima"</string>
<string name="screen_space_announcement_item4">"Pridružite se javnim prostorima"</string>
<string name="screen_space_announcement_item5">"Napustite sve prostore kojima ste se pridružili"</string>
<string name="screen_space_announcement_notice">"Uskoro stiže filtriranje i stvaranje prostora te upravljanje njima."</string>
<string name="screen_space_announcement_subtitle">"Dobrodošli u beta inačicu prostora! S ovom prvom inačicom možete:"</string>
<string name="screen_space_announcement_title">"Predstavljamo prostore"</string>
</resources>

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_space_announcement_item1">"Az Ön által létrehozott vagy csatlakozott térek megtekintése"</string>
<string name="screen_space_announcement_item2">"A meghívások elfogadására vagy elutasítására a terekhez"</string>
<string name="screen_space_announcement_item3">"Szobák felfedezése a terekben, amelyekhez csatlakozhat"</string>
<string name="screen_space_announcement_item4">"Csatlakozás nyilvános terekhez"</string>
<string name="screen_space_announcement_item5">"Terek elhagyása"</string>
<string name="screen_space_announcement_notice">"Terek szűrése, készítése és kezelése hamarosan érkezik."</string>
<string name="screen_space_announcement_subtitle">"Üdvözöljük a tér béta verziójában! Ezzel az első verzióval a következőket teheti:"</string>
<string name="screen_space_announcement_title">"Bemutatkoznak a terek"</string>
</resources>

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_space_announcement_item1">"Visualizza gli spazi che hai creato o a cui partecipi"</string>
<string name="screen_space_announcement_item2">"Accetta o rifiuta gli inviti agli spazi"</string>
<string name="screen_space_announcement_item3">"Scopri tutte le stanze a cui puoi partecipare nei tuoi spazi"</string>
<string name="screen_space_announcement_item4">"Unisciti agli spazi pubblici"</string>
<string name="screen_space_announcement_item5">"Lascia tutti gli spazi a cui ti sei unito"</string>
<string name="screen_space_announcement_notice">"A breve saranno disponibili le funzionalità di filtraggio, creazione e gestione degli spazi."</string>
<string name="screen_space_announcement_subtitle">"Benvenuti alla versione beta degli Spazi! Con questa prima versione potrete:"</string>
<string name="screen_space_announcement_title">"Ti presentiamo gli Spazi"</string>
</resources>

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_space_announcement_item1">"作成または参加したスペースを表示できます"</string>
<string name="screen_space_announcement_item2">"スペースへの招待を受諾または拒否できます"</string>
<string name="screen_space_announcement_item3">"スペース内の参加可能なルームを検索できます"</string>
<string name="screen_space_announcement_item4">"公開スペースに参加できます"</string>
<string name="screen_space_announcement_item5">"参加したスペースを退出できます"</string>
<string name="screen_space_announcement_notice">"スペースの作成や管理, フィルター検索は近日実装予定です。"</string>
<string name="screen_space_announcement_subtitle">"ベータ版のスペースにようこそ。この最新のバージョンでは:"</string>
<string name="screen_space_announcement_title">"スペースの紹介"</string>
</resources>

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_space_announcement_item1">"직접 만들거나 참여 중인 스페이스 보기"</string>
<string name="screen_space_announcement_item2">"스페이스 초대 수락 또는 거절"</string>
<string name="screen_space_announcement_item3">"참여 가능한 스페이스 내 모든 방 탐색"</string>
<string name="screen_space_announcement_item4">"공개 스페이스 참여"</string>
<string name="screen_space_announcement_item5">"참여 중인 스페이스 나가기"</string>
<string name="screen_space_announcement_notice">"스페이스 필터링, 생성 및 관리 기능이 곧 추가될 예정입니다."</string>
<string name="screen_space_announcement_subtitle">"스페이스 베타 버전에 오신 것을 환영합니다! 이번 첫 번째 버전에서는 다음과 같은 기능을 이용하실 수 있습니다.:"</string>
<string name="screen_space_announcement_title">"스페이스 소개"</string>
</resources>

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_space_announcement_item1">"Se områder du har opprettet eller blitt med i"</string>
<string name="screen_space_announcement_item2">"Godta eller avslå invitasjoner til områder"</string>
<string name="screen_space_announcement_item3">"Oppdag alle rom du kan bli med i i dine områder"</string>
<string name="screen_space_announcement_item4">"Bli med i offentlige områder"</string>
<string name="screen_space_announcement_item5">"Forlat områder du har blitt med i"</string>
<string name="screen_space_announcement_notice">"Oppretting, filtrering og administrasjon av områder kommer snart."</string>
<string name="screen_space_announcement_subtitle">"Velkommen til betaversjonen av Områder! Med denne første versjonen kan du:"</string>
<string name="screen_space_announcement_title">"Vi introduserer Områder"</string>
</resources>

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_space_announcement_item1">"Wyświetlić przestrzenie, które stworzyłeś lub do których dołączyłeś"</string>
<string name="screen_space_announcement_item2">"Akceptować lub odrzucać zaproszenia"</string>
<string name="screen_space_announcement_item3">"Odkrywać wszystkie pokoje, do których możesz dołączyć w swoich przestrzeniach"</string>
<string name="screen_space_announcement_item4">"Dołączać do przestrzeni publicznych"</string>
<string name="screen_space_announcement_item5">"Opuszczać jakąkolwiek przestrzeń, do której dołączyłeś"</string>
<string name="screen_space_announcement_notice">"Filtrowanie, tworzenie i zarządzanie przestrzeniami pojawi się wkrótce."</string>
<string name="screen_space_announcement_subtitle">"Witamy w wersji beta przestrzeni! W tej wersji możesz:"</string>
<string name="screen_space_announcement_title">"Przedstawiamy przestrzenie"</string>
</resources>

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_space_announcement_item1">"Visualizar espaços que criou ou entrou"</string>
<string name="screen_space_announcement_item2">"Aceitar ou recusar convites aos espaços"</string>
<string name="screen_space_announcement_item3">"Descobrir quaisquer salas que você pode entrar nos espaços"</string>
<string name="screen_space_announcement_item4">"Entrar espaços públicos"</string>
<string name="screen_space_announcement_item5">"Sair de quaisquer espaços que entrou"</string>
<string name="screen_space_announcement_notice">"Filtrar, criar, e gerenciar espaços virão em breve."</string>
<string name="screen_space_announcement_subtitle">"Boas-vindas à versão beta dos Espaços! Com essa primeira versão, você pode:"</string>
<string name="screen_space_announcement_title">"Apresentando Espaços"</string>
</resources>

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_space_announcement_item1">"Ver espaços que criaste ou nos quais entraste"</string>
<string name="screen_space_announcement_item2">"Aceitar ou recusar convites para espaços"</string>
<string name="screen_space_announcement_item3">"Descobrir todas as salas dos seus espaços nas quais podes entrar"</string>
<string name="screen_space_announcement_item4">"Entrar em espaços públicos"</string>
<string name="screen_space_announcement_item5">"Deixar todos os espaços em que entraste"</string>
<string name="screen_space_announcement_notice">"Em breve, será possível filtrar, criar e gerir espaços."</string>
<string name="screen_space_announcement_subtitle">"Eis a versão beta dos Espaços! Nesta primeira versão, podes:"</string>
<string name="screen_space_announcement_title">"Apresentamos os Espaços"</string>
</resources>

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_space_announcement_item1">"Vizualizați spațiile pe care le-ați creat sau la care v-ați alăturat"</string>
<string name="screen_space_announcement_item2">"Acceptați sau refuzați invitațiile la spații"</string>
<string name="screen_space_announcement_item3">"Descoperiți toate camerele la care vă puteți alătura în spațiile dumneavoastră."</string>
<string name="screen_space_announcement_item4">"Alăturați-vă spațiilor publice"</string>
<string name="screen_space_announcement_item5">"Părăsiți spațiile la care v-ați alăturat."</string>
<string name="screen_space_announcement_notice">"Filtrarea, crearea și gestionarea spațiilor vor fi disponibile în curând."</string>
<string name="screen_space_announcement_subtitle">"Bun venit la versiunea beta a Spațiilor! Cu această primă versiune puteți:"</string>
<string name="screen_space_announcement_title">"Vă prezentăm Spații"</string>
</resources>

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_space_announcement_item1">"Просматривать пространства, которые вы создали или к которым присоединились"</string>
<string name="screen_space_announcement_item2">"Принимать или отклонять приглашения в пространства"</string>
<string name="screen_space_announcement_item3">"Находить все комнаты, к которым можно присоединиться в ваших пространствах"</string>
<string name="screen_space_announcement_item4">"Присоединяться к публичным пространствам"</string>
<string name="screen_space_announcement_item5">"Покидать все пространства, к которым вы присоединились"</string>
<string name="screen_space_announcement_notice">"Фильтровать, создавать пространства и управлять ими можно будет позже."</string>
<string name="screen_space_announcement_subtitle">"Добро пожаловать в бета-версию пространств! Сейчас вы сможете:"</string>
<string name="screen_space_announcement_title">"Представляем пространства"</string>
</resources>

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_space_announcement_item1">"Zobraziť priestory, ktoré ste vytvorili alebo ku ktorým ste sa pripojili"</string>
<string name="screen_space_announcement_item2">"Prijímať alebo odmietať pozvánky do priestorov"</string>
<string name="screen_space_announcement_item3">"Objaviť všetky miestnosti, do ktorých sa môžete pripojiť vo svojich priestoroch"</string>
<string name="screen_space_announcement_item4">"Pripojiť sa k verejnému priestoru"</string>
<string name="screen_space_announcement_item5">"Opustiť akékoľvek priestory, ku ktorým ste sa pridali"</string>
<string name="screen_space_announcement_notice">"Filtrovanie, vytváranie a správa priestorov bude čoskoro k dispozícii."</string>
<string name="screen_space_announcement_subtitle">"Vitajte v beta verzii priestorov! S touto prvou verziou môžete:"</string>
<string name="screen_space_announcement_title">"Predstavujeme priestory"</string>
</resources>

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_space_announcement_item1">"Oluşturduğunuz veya katıldığınız alanları görüntüleyin"</string>
<string name="screen_space_announcement_item2">"Alan davetlerini kabul edin veya reddedin"</string>
<string name="screen_space_announcement_item3">"Alanlarınızdaki katılabileceğiniz odaları keşfedin"</string>
<string name="screen_space_announcement_item4">"Herkese açık alanlara katılın"</string>
<string name="screen_space_announcement_item5">"Katıldığınız alanlardan ayrılın"</string>
<string name="screen_space_announcement_notice">"Alanları filtreleme, oluşturma ve yönetme yakında geliyor."</string>
<string name="screen_space_announcement_subtitle">"Alanların beta sürümüne hoş geldiniz! Bu ilk sürümle şunları yapabilirsiniz:"</string>
<string name="screen_space_announcement_title">"Alanlar ile tanışın"</string>
</resources>

View file

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_space_announcement_item3">"Знаходьте у своїх просторах кімнати, до яких можна приєднатися"</string>
<string name="screen_space_announcement_notice">"Фільтрування, створення та керування просторами стане доступним найближчим часом."</string>
<string name="screen_space_announcement_subtitle">"Ласкаво просимо до бета-версії Просторів! У цій першій версії ви можете:"</string>
<string name="screen_space_announcement_title">"Представляємо Простори"</string>
</resources>

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_space_announcement_item1">"Siz yaratgan yoki qoshilgan maydonlarni korish"</string>
<string name="screen_space_announcement_item2">"Maydonlarga takliflarni qabul qilish yoki rad etish"</string>
<string name="screen_space_announcement_item3">"Maydonlaringizga qoshilishingiz mumkin bolgan xonalarni kashf eting"</string>
<string name="screen_space_announcement_item4">"Jamoat maydonlariga qoshilish"</string>
<string name="screen_space_announcement_item5">"Kirgan maydonlaringizni tark eting"</string>
<string name="screen_space_announcement_notice">"Maydonlarni filtrlash, yaratish va boshqarish tez orada amalga oshiriladi."</string>
<string name="screen_space_announcement_subtitle">"Maydonlar beta versiyasiga xush kelibsiz! Bu birinchi versiya bilan siz:"</string>
<string name="screen_space_announcement_title">"Maydonlar bilan tanishish"</string>
</resources>

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_space_announcement_item1">"檢視您建立或加入的空間"</string>
<string name="screen_space_announcement_item2">"接受或拒絕空間邀請"</string>
<string name="screen_space_announcement_item3">"探索空間內您可以加入的任何聊天室"</string>
<string name="screen_space_announcement_item4">"加入公開空間"</string>
<string name="screen_space_announcement_item5">"離開任何您已加入的空間"</string>
<string name="screen_space_announcement_notice">"篩選、建立與管理空間功能即將推出。"</string>
<string name="screen_space_announcement_subtitle">"歡迎使用空間的測試版!此初始版本可讓您:"</string>
<string name="screen_space_announcement_title">"介紹空間"</string>
</resources>

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_space_announcement_item1">"查看您创建或加入的空间"</string>
<string name="screen_space_announcement_item2">"接受或拒绝空间邀请"</string>
<string name="screen_space_announcement_item3">"发现您可以加入空间的所有房间"</string>
<string name="screen_space_announcement_item4">"加入公共空间"</string>
<string name="screen_space_announcement_item5">"离开你加入的所有空间"</string>
<string name="screen_space_announcement_notice">"筛选、创建及管理空间功能即将上线。"</string>
<string name="screen_space_announcement_subtitle">"欢迎使用空间测试版!使用首个版本,您可以:"</string>
<string name="screen_space_announcement_title">"空间简介"</string>
</resources>

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_space_announcement_item1">"View spaces you\'ve created or joined"</string>
<string name="screen_space_announcement_item2">"Accept or decline invites to spaces"</string>
<string name="screen_space_announcement_item3">"Discover any rooms you can join in your spaces"</string>
<string name="screen_space_announcement_item4">"Join public spaces"</string>
<string name="screen_space_announcement_item5">"Leave any spaces youve joined"</string>
<string name="screen_space_announcement_notice">"Filtering, creating and managing spaces is coming soon."</string>
<string name="screen_space_announcement_subtitle">"Welcome to the beta version of Spaces! With this first version you can:"</string>
<string name="screen_space_announcement_title">"Introducing Spaces"</string>
</resources>

View file

@ -6,11 +6,14 @@
* Please see LICENSE files in the repository root for full details. * Please see LICENSE files in the repository root for full details.
*/ */
@file:OptIn(ExperimentalTestApi::class)
package io.element.android.features.announcement.impl.fullscreen package io.element.android.features.announcement.impl.fullscreen
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.compose.ui.test.junit4.AndroidComposeTestRule import androidx.compose.ui.test.AndroidComposeUiTest
import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.v2.runAndroidComposeUiTest
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import io.element.android.features.announcement.api.Announcement import io.element.android.features.announcement.api.Announcement
import io.element.android.features.announcement.impl.AnnouncementEvent import io.element.android.features.announcement.impl.AnnouncementEvent
@ -20,43 +23,39 @@ import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.EventsRecorder
import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.clickOn
import io.element.android.tests.testutils.pressBackKey import io.element.android.tests.testutils.pressBackKey
import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.TestRule
import org.junit.runner.RunWith import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class FullscreenAnnouncementViewTest { class FullscreenAnnouncementViewTest {
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
@Test @Test
fun `clicking on back sends a AnnouncementEvent`() { fun `clicking on back sends a AnnouncementEvent`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<AnnouncementEvent>() val eventsRecorder = EventsRecorder<AnnouncementEvent>()
rule.setFullscreenAnnouncementView( setFullscreenAnnouncementView(
anAnnouncementState( anAnnouncementState(
announcement = Announcement.Fullscreen.Space, announcement = Announcement.Fullscreen.Space,
eventSink = eventsRecorder, eventSink = eventsRecorder,
), ),
) )
rule.pressBackKey() pressBackKey()
eventsRecorder.assertSingle(AnnouncementEvent.Continue(Announcement.Fullscreen.Space)) eventsRecorder.assertSingle(AnnouncementEvent.Continue(Announcement.Fullscreen.Space))
} }
@Test @Test
fun `clicking on Continue sends a AnnouncementEvent`() { fun `clicking on Continue sends a AnnouncementEvent`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<AnnouncementEvent>() val eventsRecorder = EventsRecorder<AnnouncementEvent>()
rule.setFullscreenAnnouncementView( setFullscreenAnnouncementView(
anAnnouncementState( anAnnouncementState(
announcement = Announcement.Fullscreen.Space, announcement = Announcement.Fullscreen.Space,
eventSink = eventsRecorder, eventSink = eventsRecorder,
), ),
) )
rule.clickOn(CommonStrings.action_continue) clickOn(CommonStrings.action_continue)
eventsRecorder.assertSingle(AnnouncementEvent.Continue(Announcement.Fullscreen.Space)) eventsRecorder.assertSingle(AnnouncementEvent.Continue(Announcement.Fullscreen.Space))
} }
} }
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setFullscreenAnnouncementView( private fun AndroidComposeUiTest<ComponentActivity>.setFullscreenAnnouncementView(
state: AnnouncementState, state: AnnouncementState,
) { ) {
setContent { setContent {

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