Merge branch 'develop' into julioromano/poll_history_entry_point

This commit is contained in:
ganfra 2023-12-13 17:22:55 +01:00
commit 863d156e4d
738 changed files with 9387 additions and 1581 deletions

1
.gitattributes vendored
View file

@ -1,2 +1,3 @@
**/snapshots/**/*.png filter=lfs diff=lfs merge=lfs -text
**/docs/images-lfs/*.png filter=lfs diff=lfs merge=lfs -text
libraries/mediaupload/impl/src/test/assets/* filter=lfs diff=lfs merge=lfs -text

View file

@ -1,5 +1,5 @@
name: Bug report for the Element X Android app
description: Report any issues that you have found with the Element X app. Please [check open issues](https://github.com/vector-im/element-x-android/issues) first, in case it has already been reported.
description: Report any issues that you have found with the Element X app. Please [check open issues](https://github.com/element-hq/element-x-android/issues) first, in case it has already been reported.
labels: [T-Defect]
body:
- type: markdown

View file

@ -5,7 +5,7 @@ body:
- type: markdown
attributes:
value: |
Thank you for taking the time to propose an enhancement to an existing feature. If you would like to propose a new feature or a major cross-platform change, please [start a discussion here](https://github.com/vector-im/element-meta/discussions/new?category=ideas).
Thank you for taking the time to propose an enhancement to an existing feature. If you would like to propose a new feature or a major cross-platform change, please [start a discussion here](https://github.com/element-hq/element-meta/discussions/new?category=ideas).
- type: textarea
id: usecase
attributes:

View file

@ -13,7 +13,7 @@ updates:
ignore:
- dependency-name: "*"
reviewers:
- "vector-im/element-x-android-reviewers"
- "element-hq/element-x-android-reviewers"
# Updates for Gradle dependencies used in the app
- package-ecosystem: "gradle"
directory: "/"
@ -23,4 +23,4 @@ updates:
ignore:
- dependency-name: "*"
reviewers:
- "vector-im/element-x-android-reviewers"
- "element-hq/element-x-android-reviewers"

View file

@ -1,4 +1,4 @@
<!-- Please read [CONTRIBUTING.md](https://github.com/vector-im/element-x-android/blob/develop/CONTRIBUTING.md) before submitting your pull request -->
<!-- Please read [CONTRIBUTING.md](https://github.com/element-hq/element-x-android/blob/develop/CONTRIBUTING.md) before submitting your pull request -->
## Type of change
@ -53,9 +53,9 @@ Uncomment this markdown table below and edit the last line `|||`:
- [ ] Changes have been tested on an Android device or Android emulator with API 23
- [ ] UI change has been tested on both light and dark themes
- [ ] Accessibility has been taken into account. See https://github.com/vector-im/element-x-android/blob/develop/CONTRIBUTING.md#accessibility
- [ ] Accessibility has been taken into account. See https://github.com/element-hq/element-x-android/blob/develop/CONTRIBUTING.md#accessibility
- [ ] Pull request is based on the develop branch
- [ ] Pull request includes a new file under ./changelog.d. See https://github.com/vector-im/element-x-android/blob/develop/CONTRIBUTING.md#changelog
- [ ] Pull request includes a new file under ./changelog.d. See https://github.com/element-hq/element-x-android/blob/develop/CONTRIBUTING.md#changelog
- [ ] Pull request includes screenshots or videos if containing UI changes
- [ ] Pull request includes a [sign off](https://matrix-org.github.io/synapse/latest/development/contributing_guide.html#sign-off)
- [ ] You've made a self review of your PR

View file

@ -38,7 +38,7 @@ jobs:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17'
- name: Configure gradle
uses: gradle/gradle-build-action@v2.10.0
uses: gradle/gradle-build-action@v2.11.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Assemble debug APK

View file

@ -11,7 +11,7 @@ jobs:
- run: |
npm install --save-dev @babel/plugin-transform-flow-strip-types
- name: Danger
uses: danger/danger-js@11.3.0
uses: danger/danger-js@11.3.1
with:
args: "--dangerfile ./tools/danger/dangerfile.js"
env:

View file

@ -12,7 +12,7 @@ jobs:
- name: Update Gradle Wrapper
uses: gradle-update/update-gradle-wrapper-action@v1
# Skip in forks
if: github.repository == 'vector-im/element-x-android'
if: github.repository == 'element-hq/element-x-android'
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
target-branch: develop

View file

@ -14,7 +14,7 @@ jobs:
nightly:
name: Build and publish nightly bundle to Firebase
runs-on: ubuntu-latest
if: ${{ github.repository == 'vector-im/element-x-android' }}
if: ${{ github.repository == 'element-hq/element-x-android' }}
steps:
- uses: actions/checkout@v4
- name: Use JDK 17

View file

@ -15,7 +15,7 @@ jobs:
nightlyReports:
name: Create kover report artifact and upload sonar result.
runs-on: ubuntu-latest
if: ${{ github.repository == 'vector-im/element-x-android' }}
if: ${{ github.repository == 'element-hq/element-x-android' }}
steps:
- name: ⏬ Checkout with LFS
uses: nschloe/action-cached-lfs-checkout@v1.2.2
@ -62,7 +62,7 @@ jobs:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17'
- name: Configure gradle
uses: gradle/gradle-build-action@v2.10.0
uses: gradle/gradle-build-action@v2.11.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Dependency analysis

View file

@ -40,7 +40,7 @@ jobs:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17'
- name: Configure gradle
uses: gradle/gradle-build-action@v2.10.0
uses: gradle/gradle-build-action@v2.11.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Run code quality check suite
@ -60,7 +60,7 @@ jobs:
yarn add danger-plugin-lint-report --dev
- name: Danger lint
if: always()
uses: danger/danger-js@11.3.0
uses: danger/danger-js@11.3.1
with:
args: "--dangerfile ./tools/danger/dangerfile-lint.js"
env:

View file

@ -39,7 +39,7 @@ jobs:
java-version: '17'
# Add gradle cache, this should speed up the process
- name: Configure gradle
uses: gradle/gradle-build-action@v2.10.0
uses: gradle/gradle-build-action@v2.11.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Record screenshots

View file

@ -25,7 +25,7 @@ jobs:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17'
- name: Configure gradle
uses: gradle/gradle-build-action@v2.10.0
uses: gradle/gradle-build-action@v2.11.0
- name: Create app bundle
env:
ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }}

View file

@ -32,7 +32,7 @@ jobs:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17'
- name: Configure gradle
uses: gradle/gradle-build-action@v2.10.0
uses: gradle/gradle-build-action@v2.11.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: 🔊 Publish results to Sonar

View file

@ -9,11 +9,11 @@ jobs:
sync-localazy:
runs-on: ubuntu-latest
# Skip in forks
if: github.repository == 'vector-im/element-x-android'
if: github.repository == 'element-hq/element-x-android'
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.9
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: 3.9
- name: Setup Localazy

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

@ -0,0 +1,35 @@
name: Sync SAS strings
on:
workflow_dispatch:
schedule:
# At 00:00 on every Monday UTC
- cron: '0 0 * * 1'
jobs:
sync-sas-strings:
runs-on: ubuntu-latest
# Skip in forks
if: github.repository == 'element-hq/element-x-android'
# No concurrency required, runs every time on a schedule.
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.8
uses: actions/setup-python@v5
with:
python-version: 3.9
- 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@v5
with:
commit-message: Sync SAS Strings
title: Sync SAS Strings
body: |
- Update SAS Strings from matrix-doc.
branch: sync-sas-strings
base: develop

View file

@ -44,7 +44,7 @@ jobs:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17'
- name: Configure gradle
uses: gradle/gradle-build-action@v2.10.0
uses: gradle/gradle-build-action@v2.11.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}

View file

@ -10,5 +10,5 @@ jobs:
steps:
- uses: actions/add-to-project@main
with:
project-url: https://github.com/orgs/vector-im/projects/91
project-url: https://github.com/orgs/element-hq/projects/91
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}

View file

@ -10,11 +10,11 @@ jobs:
runs-on: ubuntu-latest
# Skip in forks
if: >
github.repository == 'vector-im/element-x-android'
github.repository == 'element-hq/element-x-android'
steps:
- uses: actions/add-to-project@main
with:
project-url: https://github.com/orgs/vector-im/projects/43
project-url: https://github.com/orgs/element-hq/projects/43
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
move_needs_info:
@ -24,7 +24,7 @@ jobs:
- uses: actions/add-to-project@main
id: addItem
with:
project-url: https://github.com/orgs/vector-im/projects/91
project-url: https://github.com/orgs/element-hq/projects/91
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
labeled: X-Needs-Info
- name: Print itemId
@ -32,7 +32,7 @@ jobs:
- uses: kalgurn/update-project-item-status@main
if: ${{ steps.addItem.outputs.itemId }}
with:
project-url: https://github.com/orgs/vector-im/projects/91
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"
@ -45,7 +45,7 @@ jobs:
steps:
- uses: actions/add-to-project@main
with:
project-url: https://github.com/orgs/vector-im/projects/73
project-url: https://github.com/orgs/element-hq/projects/73
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
verticals_feature:
@ -56,7 +56,7 @@ jobs:
steps:
- uses: actions/add-to-project@main
with:
project-url: https://github.com/orgs/vector-im/projects/57
project-url: https://github.com/orgs/element-hq/projects/57
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
qa:
@ -68,7 +68,7 @@ jobs:
steps:
- uses: actions/add-to-project@main
with:
project-url: https://github.com/orgs/vector-im/projects/69
project-url: https://github.com/orgs/element-hq/projects/69
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
signoff:
@ -79,5 +79,5 @@ jobs:
steps:
- uses: actions/add-to-project@main
with:
project-url: https://github.com/orgs/vector-im/projects/89
project-url: https://github.com/orgs/element-hq/projects/89
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}

2
.idea/kotlinc.xml generated
View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.9.20" />
<option name="version" value="1.9.21" />
</component>
</project>

10
.idea/migrations.xml generated Normal file
View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectMigrations">
<option name="MigrateToGradleLocalJavaHome">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</component>
</project>

View file

@ -1,4 +1,4 @@
A full developer contributors list can be found [here](https://github.com/vector-im/element-x-android/graphs/contributors).
A full developer contributors list can be found [here](https://github.com/element-hq/element-x-android/graphs/contributors).
# Core team:

View file

@ -3,21 +3,21 @@ Changes in Element X v0.3.2 (2023-11-22)
Features ✨
----------
- Add ongoing call indicator to rooms lists items. ([#1158](https://github.com/vector-im/element-x-android/issues/1158))
- Add support for typing mentions in the message composer. ([#1453](https://github.com/vector-im/element-x-android/issues/1453))
- Add intentional mentions to messages. This needs to be enabled in developer options since it's disabled by default. ([#1591](https://github.com/vector-im/element-x-android/issues/1591))
- Update voice message recording behaviour. Instead of holding the record button, users can now tap the record button to start recording and tap again to stop recording. ([#1784](https://github.com/vector-im/element-x-android/issues/1784))
- Add ongoing call indicator to rooms lists items. ([#1158](https://github.com/element-hq/element-x-android/issues/1158))
- Add support for typing mentions in the message composer. ([#1453](https://github.com/element-hq/element-x-android/issues/1453))
- Add intentional mentions to messages. This needs to be enabled in developer options since it's disabled by default. ([#1591](https://github.com/element-hq/element-x-android/issues/1591))
- Update voice message recording behaviour. Instead of holding the record button, users can now tap the record button to start recording and tap again to stop recording. ([#1784](https://github.com/element-hq/element-x-android/issues/1784))
Bugfixes 🐛
----------
- Always ensure media temp dir exists ([#1790](https://github.com/vector-im/element-x-android/issues/1790))
- Always ensure media temp dir exists ([#1790](https://github.com/element-hq/element-x-android/issues/1790))
Other changes
-------------
- Update icons and move away from `PreferenceText` components. ([#1718](https://github.com/vector-im/element-x-android/issues/1718))
- Add item "This is the beginning of..." at the beginning of the timeline. ([#1801](https://github.com/vector-im/element-x-android/issues/1801))
- LockScreen : rework LoggedInFlowNode and back management when locked. ([#1806](https://github.com/vector-im/element-x-android/issues/1806))
- Suppress usage of removeTimeline method. ([#1824](https://github.com/vector-im/element-x-android/issues/1824))
- Update icons and move away from `PreferenceText` components. ([#1718](https://github.com/element-hq/element-x-android/issues/1718))
- Add item "This is the beginning of..." at the beginning of the timeline. ([#1801](https://github.com/element-hq/element-x-android/issues/1801))
- LockScreen : rework LoggedInFlowNode and back management when locked. ([#1806](https://github.com/element-hq/element-x-android/issues/1806))
- Suppress usage of removeTimeline method. ([#1824](https://github.com/element-hq/element-x-android/issues/1824))
- Remove Element Call feature flag, it's now always enabled.
- Reverted the EC base URL to `https://call.element.io`.
- Moved the option to override this URL to developer settings from advanced settings.
@ -28,16 +28,16 @@ Changes in Element X v0.3.1 (2023-11-09)
Features ✨
----------
- Chat backup is still under a feature flag, but when enabled, user can enter their recovery key (it's also possible to input a passphrase) to unlock the encrypted room history. ([#1770](https://github.com/vector-im/element-x-android/pull/1770))
- Chat backup is still under a feature flag, but when enabled, user can enter their recovery key (it's also possible to input a passphrase) to unlock the encrypted room history. ([#1770](https://github.com/element-hq/element-x-android/pull/1770))
Bugfixes 🐛
----------
- Improve confusing text in the 'ready to start verification' screen. ([#879](https://github.com/vector-im/element-x-android/issues/879))
- Message composer wasn't resized when selecting a several lines message to reply to, then a single line one. ([#1560](https://github.com/vector-im/element-x-android/issues/1560))
- Improve confusing text in the 'ready to start verification' screen. ([#879](https://github.com/element-hq/element-x-android/issues/879))
- Message composer wasn't resized when selecting a several lines message to reply to, then a single line one. ([#1560](https://github.com/element-hq/element-x-android/issues/1560))
Other changes
-------------
- PIN: Set lock grace period to 0. ([#1732](https://github.com/vector-im/element-x-android/issues/1732))
- PIN: Set lock grace period to 0. ([#1732](https://github.com/element-hq/element-x-android/issues/1732))
Changes in Element X v0.3.0 (2023-10-31)
@ -45,24 +45,24 @@ Changes in Element X v0.3.0 (2023-10-31)
Features ✨
----------
- Element Call: change the 'join call' button in a chat room when there's an active call. ([#1158](https://github.com/vector-im/element-x-android/issues/1158))
- Mentions: add mentions suggestion view in RTE ([#1452](https://github.com/vector-im/element-x-android/issues/1452))
- Record and send voice messages ([#1596](https://github.com/vector-im/element-x-android/issues/1596))
- Enable voice messages for all users ([#1669](https://github.com/vector-im/element-x-android/issues/1669))
- Receive and play a voice message ([#2084](https://github.com/vector-im/element-x-android/issues/2084))
- Element Call: change the 'join call' button in a chat room when there's an active call. ([#1158](https://github.com/element-hq/element-x-android/issues/1158))
- Mentions: add mentions suggestion view in RTE ([#1452](https://github.com/element-hq/element-x-android/issues/1452))
- Record and send voice messages ([#1596](https://github.com/element-hq/element-x-android/issues/1596))
- Enable voice messages for all users ([#1669](https://github.com/element-hq/element-x-android/issues/1669))
- Receive and play a voice message ([#2084](https://github.com/element-hq/element-x-android/issues/2084))
- Enable Element Call integration in rooms by default, fix several issues when creating or joining calls.
Bugfixes 🐛
----------
- Group fallback notification to avoid having plenty of them displayed. ([#994](https://github.com/vector-im/element-x-android/issues/994))
- Hide keyboard when exiting the chat room screen. ([#1375](https://github.com/vector-im/element-x-android/issues/1375))
- Always register the pusher when application starts ([#1481](https://github.com/vector-im/element-x-android/issues/1481))
- Ensure screen does not turn off when playing a video ([#1519](https://github.com/vector-im/element-x-android/issues/1519))
- Fix issue where text is cleared when cancelling a reply ([#1617](https://github.com/vector-im/element-x-android/issues/1617))
- Group fallback notification to avoid having plenty of them displayed. ([#994](https://github.com/element-hq/element-x-android/issues/994))
- Hide keyboard when exiting the chat room screen. ([#1375](https://github.com/element-hq/element-x-android/issues/1375))
- Always register the pusher when application starts ([#1481](https://github.com/element-hq/element-x-android/issues/1481))
- Ensure screen does not turn off when playing a video ([#1519](https://github.com/element-hq/element-x-android/issues/1519))
- Fix issue where text is cleared when cancelling a reply ([#1617](https://github.com/element-hq/element-x-android/issues/1617))
Other changes
-------------
- Remove usage of blocking methods. ([#1563](https://github.com/vector-im/element-x-android/issues/1563))
- Remove usage of blocking methods. ([#1563](https://github.com/element-hq/element-x-android/issues/1563))
Changes in Element X v0.2.4 (2023-10-12)
@ -70,20 +70,20 @@ Changes in Element X v0.2.4 (2023-10-12)
Features ✨
----------
- [Rich text editor] Add full screen mode ([#1447](https://github.com/vector-im/element-x-android/issues/1447))
- Improve rendering of m.emote. ([#1497](https://github.com/vector-im/element-x-android/issues/1497))
- Improve deleted session behavior. ([#1520](https://github.com/vector-im/element-x-android/issues/1520))
- [Rich text editor] Add full screen mode ([#1447](https://github.com/element-hq/element-x-android/issues/1447))
- Improve rendering of m.emote. ([#1497](https://github.com/element-hq/element-x-android/issues/1497))
- Improve deleted session behavior. ([#1520](https://github.com/element-hq/element-x-android/issues/1520))
Bugfixes 🐛
----------
- WebP images can't be sent as media. ([#1483](https://github.com/vector-im/element-x-android/issues/1483))
- Fix back button not working in bottom sheets. ([#1517](https://github.com/vector-im/element-x-android/issues/1517))
- Render body of unknown msgtype in the timeline and in the room list ([#1539](https://github.com/vector-im/element-x-android/issues/1539))
- WebP images can't be sent as media. ([#1483](https://github.com/element-hq/element-x-android/issues/1483))
- Fix back button not working in bottom sheets. ([#1517](https://github.com/element-hq/element-x-android/issues/1517))
- Render body of unknown msgtype in the timeline and in the room list ([#1539](https://github.com/element-hq/element-x-android/issues/1539))
Other changes
-------------
- Room : makes subscribeToSync/unsubscribeFromSync suspendable. ([#1457](https://github.com/vector-im/element-x-android/issues/1457))
- Add some Konsist tests. ([#1526](https://github.com/vector-im/element-x-android/issues/1526))
- Room : makes subscribeToSync/unsubscribeFromSync suspendable. ([#1457](https://github.com/element-hq/element-x-android/issues/1457))
- Add some Konsist tests. ([#1526](https://github.com/element-hq/element-x-android/issues/1526))
Changes in Element X v0.2.3 (2023-09-27)
@ -91,12 +91,12 @@ Changes in Element X v0.2.3 (2023-09-27)
Features ✨
----------
- Handle installation of Apks from the media viewer. ([#1432](https://github.com/vector-im/element-x-android/pull/1432))
- Integrate SDK 0.1.58 ([#1437](https://github.com/vector-im/element-x-android/pull/1437))
- Handle installation of Apks from the media viewer. ([#1432](https://github.com/element-hq/element-x-android/pull/1432))
- Integrate SDK 0.1.58 ([#1437](https://github.com/element-hq/element-x-android/pull/1437))
Other changes
-------------
- Element call: add custom parameters to Element Call urls. ([#1434](https://github.com/vector-im/element-x-android/issues/1434))
- Element call: add custom parameters to Element Call urls. ([#1434](https://github.com/element-hq/element-x-android/issues/1434))
Changes in Element X v0.2.2 (2023-09-21)
@ -104,8 +104,8 @@ Changes in Element X v0.2.2 (2023-09-21)
Bugfixes 🐛
----------
- Add animation when rendering the timeline to avoid glitches. ([#1323](https://github.com/vector-im/element-x-android/issues/1323))
- Fix crash when trying to take a photo or record a video. ([#1395](https://github.com/vector-im/element-x-android/issues/1395))
- Add animation when rendering the timeline to avoid glitches. ([#1323](https://github.com/element-hq/element-x-android/issues/1323))
- Fix crash when trying to take a photo or record a video. ([#1395](https://github.com/element-hq/element-x-android/issues/1395))
Changes in Element X v0.2.1 (2023-09-20)
@ -114,18 +114,18 @@ Changes in Element X v0.2.1 (2023-09-20)
Features ✨
----------
- Bump Rust SDK to `v0.1.56`
- [Rich text editor] Add link support to rich text editor ([#1309](https://github.com/vector-im/element-x-android/issues/1309))
- Let the SDK figure the best scheme given an homeserver URL (thus allowing HTTP homeservers) ([#1382](https://github.com/vector-im/element-x-android/issues/1382))
- [Rich text editor] Add link support to rich text editor ([#1309](https://github.com/element-hq/element-x-android/issues/1309))
- Let the SDK figure the best scheme given an homeserver URL (thus allowing HTTP homeservers) ([#1382](https://github.com/element-hq/element-x-android/issues/1382))
Bugfixes 🐛
----------
- Fix ANR on RoomList when notification settings change. ([#1370](https://github.com/vector-im/element-x-android/issues/1370))
- Fix ANR on RoomList when notification settings change. ([#1370](https://github.com/element-hq/element-x-android/issues/1370))
Other changes
-------------
- Element Call: support scheme `io.element.call` ([#1377](https://github.com/vector-im/element-x-android/issues/1377))
- [DI] Rework how dagger components are created and provided. ([#1378](https://github.com/vector-im/element-x-android/issues/1378))
- Remove usage of async-uniffi as it leads to a deadlocks and memory leaks. ([#1381](https://github.com/vector-im/element-x-android/issues/1381))
- Element Call: support scheme `io.element.call` ([#1377](https://github.com/element-hq/element-x-android/issues/1377))
- [DI] Rework how dagger components are created and provided. ([#1378](https://github.com/element-hq/element-x-android/issues/1378))
- Remove usage of async-uniffi as it leads to a deadlocks and memory leaks. ([#1381](https://github.com/element-hq/element-x-android/issues/1381))
Changes in Element X v0.2.0 (2023-09-18)
@ -134,37 +134,37 @@ Changes in Element X v0.2.0 (2023-09-18)
Features ✨
----------
- Bump Rust SDK to `v0.1.54`
- Add a "Mute" shortcut icon and a "Notifications" section in the room details screen ([#506](https://github.com/vector-im/element-x-android/issues/506))
- Add a notification permission screen to the initial flow. ([#897](https://github.com/vector-im/element-x-android/issues/897))
- Integrate Element Call into EX by embedding a call in a WebView. ([#1300](https://github.com/vector-im/element-x-android/issues/1300))
- Implement Bloom effect modifier. ([#1217](https://github.com/vector-im/element-x-android/issues/1217))
- Set color on display name and default avatar in the timeline. ([#1224](https://github.com/vector-im/element-x-android/issues/1224))
- Display a thread decorator in timeline so we know when a message is coming from a thread. ([#1236](https://github.com/vector-im/element-x-android/issues/1236))
- [Rich text editor] Integrate rich text editor library. Note that markdown is now not supported and further formatting support will be introduced through the rich text editor. ([#1172](https://github.com/vector-im/element-x-android/issues/1172))
- [Rich text editor] Add formatting menu (accessible via the '+' button) ([#1261](https://github.com/vector-im/element-x-android/issues/1261))
- [Rich text editor] Add feature flag for rich text editor. Markdown support can now be enabled by disabling the rich text editor. ([#1289](https://github.com/vector-im/element-x-android/issues/1289))
- [Rich text editor] Update design ([#1332](https://github.com/vector-im/element-x-android/issues/1332))
- Add a "Mute" shortcut icon and a "Notifications" section in the room details screen ([#506](https://github.com/element-hq/element-x-android/issues/506))
- Add a notification permission screen to the initial flow. ([#897](https://github.com/element-hq/element-x-android/issues/897))
- Integrate Element Call into EX by embedding a call in a WebView. ([#1300](https://github.com/element-hq/element-x-android/issues/1300))
- Implement Bloom effect modifier. ([#1217](https://github.com/element-hq/element-x-android/issues/1217))
- Set color on display name and default avatar in the timeline. ([#1224](https://github.com/element-hq/element-x-android/issues/1224))
- Display a thread decorator in timeline so we know when a message is coming from a thread. ([#1236](https://github.com/element-hq/element-x-android/issues/1236))
- [Rich text editor] Integrate rich text editor library. Note that markdown is now not supported and further formatting support will be introduced through the rich text editor. ([#1172](https://github.com/element-hq/element-x-android/issues/1172))
- [Rich text editor] Add formatting menu (accessible via the '+' button) ([#1261](https://github.com/element-hq/element-x-android/issues/1261))
- [Rich text editor] Add feature flag for rich text editor. Markdown support can now be enabled by disabling the rich text editor. ([#1289](https://github.com/element-hq/element-x-android/issues/1289))
- [Rich text editor] Update design ([#1332](https://github.com/element-hq/element-x-android/issues/1332))
Bugfixes 🐛
----------
- Make links in room topic clickable ([#612](https://github.com/vector-im/element-x-android/issues/612))
- Reply action: harmonize conditions in bottom sheet and swipe to reply. ([#1173](https://github.com/vector-im/element-x-android/issues/1173))
- Fix system bar color after login on light theme. ([#1222](https://github.com/vector-im/element-x-android/issues/1222))
- Fix long click on simple formatted messages ([#1232](https://github.com/vector-im/element-x-android/issues/1232))
- Enable polls in release build. ([#1241](https://github.com/vector-im/element-x-android/issues/1241))
- Fix top padding in room list when app is opened in offline mode. ([#1297](https://github.com/vector-im/element-x-android/issues/1297))
- [Rich text editor] Fix 'text formatting' option only partially visible ([#1335](https://github.com/vector-im/element-x-android/issues/1335))
- [Rich text editor] Ensure keyboard opens for reply and text formatting modes ([#1337](https://github.com/vector-im/element-x-android/issues/1337))
- [Rich text editor] Fix placeholder spilling onto multiple lines ([#1347](https://github.com/vector-im/element-x-android/issues/1347))
- Make links in room topic clickable ([#612](https://github.com/element-hq/element-x-android/issues/612))
- Reply action: harmonize conditions in bottom sheet and swipe to reply. ([#1173](https://github.com/element-hq/element-x-android/issues/1173))
- Fix system bar color after login on light theme. ([#1222](https://github.com/element-hq/element-x-android/issues/1222))
- Fix long click on simple formatted messages ([#1232](https://github.com/element-hq/element-x-android/issues/1232))
- Enable polls in release build. ([#1241](https://github.com/element-hq/element-x-android/issues/1241))
- Fix top padding in room list when app is opened in offline mode. ([#1297](https://github.com/element-hq/element-x-android/issues/1297))
- [Rich text editor] Fix 'text formatting' option only partially visible ([#1335](https://github.com/element-hq/element-x-android/issues/1335))
- [Rich text editor] Ensure keyboard opens for reply and text formatting modes ([#1337](https://github.com/element-hq/element-x-android/issues/1337))
- [Rich text editor] Fix placeholder spilling onto multiple lines ([#1347](https://github.com/element-hq/element-x-android/issues/1347))
Other changes
-------------
- Add a sub-screen "Notifications" in the existing application Settings ([#510](https://github.com/vector-im/element-x-android/issues/510))
- Exclude some groups related to analytics to be included. ([#1191](https://github.com/vector-im/element-x-android/issues/1191))
- Use the new SyncIndicator API. ([#1244](https://github.com/vector-im/element-x-android/issues/1244))
- Improve RoomSummary mapping by using RoomInfo. ([#1251](https://github.com/vector-im/element-x-android/issues/1251))
- Ensure Posthog data are sent to "https://posthog.element.io" ([#1269](https://github.com/vector-im/element-x-android/issues/1269))
- New app icon, with monochrome support. ([#1363](https://github.com/vector-im/element-x-android/issues/1363))
- Add a sub-screen "Notifications" in the existing application Settings ([#510](https://github.com/element-hq/element-x-android/issues/510))
- Exclude some groups related to analytics to be included. ([#1191](https://github.com/element-hq/element-x-android/issues/1191))
- Use the new SyncIndicator API. ([#1244](https://github.com/element-hq/element-x-android/issues/1244))
- Improve RoomSummary mapping by using RoomInfo. ([#1251](https://github.com/element-hq/element-x-android/issues/1251))
- Ensure Posthog data are sent to "https://posthog.element.io" ([#1269](https://github.com/element-hq/element-x-android/issues/1269))
- New app icon, with monochrome support. ([#1363](https://github.com/element-hq/element-x-android/issues/1363))
Changes in Element X v0.1.6 (2023-09-04)
@ -172,22 +172,22 @@ Changes in Element X v0.1.6 (2023-09-04)
Features ✨
----------
- Enable the Polls feature. Allows to create, view, vote and end polls. ([#1196](https://github.com/vector-im/element-x-android/issues/1196))
- Create poll. ([#1143](https://github.com/vector-im/element-x-android/issues/1143))
- Enable the Polls feature. Allows to create, view, vote and end polls. ([#1196](https://github.com/element-hq/element-x-android/issues/1196))
- Create poll. ([#1143](https://github.com/element-hq/element-x-android/issues/1143))
Bugfixes 🐛
----------
- Ensure notification for Event from encrypted room get decrypted content. ([#1178](https://github.com/vector-im/element-x-android/issues/1178))
- Make sure Snackbars are only displayed once. ([#928](https://github.com/vector-im/element-x-android/issues/928))
- Fix the orientation of sent images. ([#1135](https://github.com/vector-im/element-x-android/issues/1135))
- Bug reporter crashes when 'send logs' is disabled. ([#1168](https://github.com/vector-im/element-x-android/issues/1168))
- Add missing link to the terms on the analytics setting screen. ([#1177](https://github.com/vector-im/element-x-android/issues/1177))
- Re-enable `SyncService.withEncryptionSync` to improve decryption of notifications. ([#1198](https://github.com/vector-im/element-x-android/issues/1198))
- Crash with `aspectRatio` modifier when `Float.NaN` was used as input. ([#1995](https://github.com/vector-im/element-x-android/issues/1995))
- Ensure notification for Event from encrypted room get decrypted content. ([#1178](https://github.com/element-hq/element-x-android/issues/1178))
- Make sure Snackbars are only displayed once. ([#928](https://github.com/element-hq/element-x-android/issues/928))
- Fix the orientation of sent images. ([#1135](https://github.com/element-hq/element-x-android/issues/1135))
- Bug reporter crashes when 'send logs' is disabled. ([#1168](https://github.com/element-hq/element-x-android/issues/1168))
- Add missing link to the terms on the analytics setting screen. ([#1177](https://github.com/element-hq/element-x-android/issues/1177))
- Re-enable `SyncService.withEncryptionSync` to improve decryption of notifications. ([#1198](https://github.com/element-hq/element-x-android/issues/1198))
- Crash with `aspectRatio` modifier when `Float.NaN` was used as input. ([#1995](https://github.com/element-hq/element-x-android/issues/1995))
Other changes
-------------
- Remove unnecessary year in copyright mention. ([#1187](https://github.com/vector-im/element-x-android/issues/1187))
- Remove unnecessary year in copyright mention. ([#1187](https://github.com/element-hq/element-x-android/issues/1187))
Changes in Element X v0.1.5 (2023-08-28)
@ -195,7 +195,7 @@ Changes in Element X v0.1.5 (2023-08-28)
Bugfixes 🐛
----------
- Fix crash when opening any room. ([#1160](https://github.com/vector-im/element-x-android/issues/1160))
- Fix crash when opening any room. ([#1160](https://github.com/element-hq/element-x-android/issues/1160))
Changes in Element X v0.1.4 (2023-08-28)
@ -203,32 +203,32 @@ Changes in Element X v0.1.4 (2023-08-28)
Features ✨
----------
- Allow cancelling media upload ([#769](https://github.com/vector-im/element-x-android/issues/769))
- Enable OIDC support. ([#1127](https://github.com/vector-im/element-x-android/issues/1127))
- Add a "Setting up account" screen, displayed the first time the user logs in to the app (per account). ([#1149](https://github.com/vector-im/element-x-android/issues/1149))
- Allow cancelling media upload ([#769](https://github.com/element-hq/element-x-android/issues/769))
- Enable OIDC support. ([#1127](https://github.com/element-hq/element-x-android/issues/1127))
- Add a "Setting up account" screen, displayed the first time the user logs in to the app (per account). ([#1149](https://github.com/element-hq/element-x-android/issues/1149))
Bugfixes 🐛
----------
- Videos sent from the app were cropped in some cases. ([#862](https://github.com/vector-im/element-x-android/issues/862))
- Timeline: sender names are now displayed in one single line. ([#1033](https://github.com/vector-im/element-x-android/issues/1033))
- Fix `TextButtons` being displayed in black. ([#1077](https://github.com/vector-im/element-x-android/issues/1077))
- Linkify links in HTML contents. ([#1079](https://github.com/vector-im/element-x-android/issues/1079))
- Fix bug reporter failing after not finding some log files. ([#1082](https://github.com/vector-im/element-x-android/issues/1082))
- Fix rendering of inline elements in list items. ([#1090](https://github.com/vector-im/element-x-android/issues/1090))
- Fix crash RuntimeException "No matching key found for the ciphertext in the stream" ([#1101](https://github.com/vector-im/element-x-android/issues/1101))
- Make links in messages clickable again. ([#1111](https://github.com/vector-im/element-x-android/issues/1111))
- When event has no id, just cancel parsing the latest room message for a room. ([#1125](https://github.com/vector-im/element-x-android/issues/1125))
- Only display verification prompt after initial sync is done. ([#1131](https://github.com/vector-im/element-x-android/issues/1131))
- Videos sent from the app were cropped in some cases. ([#862](https://github.com/element-hq/element-x-android/issues/862))
- Timeline: sender names are now displayed in one single line. ([#1033](https://github.com/element-hq/element-x-android/issues/1033))
- Fix `TextButtons` being displayed in black. ([#1077](https://github.com/element-hq/element-x-android/issues/1077))
- Linkify links in HTML contents. ([#1079](https://github.com/element-hq/element-x-android/issues/1079))
- Fix bug reporter failing after not finding some log files. ([#1082](https://github.com/element-hq/element-x-android/issues/1082))
- Fix rendering of inline elements in list items. ([#1090](https://github.com/element-hq/element-x-android/issues/1090))
- Fix crash RuntimeException "No matching key found for the ciphertext in the stream" ([#1101](https://github.com/element-hq/element-x-android/issues/1101))
- Make links in messages clickable again. ([#1111](https://github.com/element-hq/element-x-android/issues/1111))
- When event has no id, just cancel parsing the latest room message for a room. ([#1125](https://github.com/element-hq/element-x-android/issues/1125))
- Only display verification prompt after initial sync is done. ([#1131](https://github.com/element-hq/element-x-android/issues/1131))
In development 🚧
----------------
- [Poll] Add feature flag in developer options ([#1064](https://github.com/vector-im/element-x-android/issues/1064))
- [Polls] Improve UI and render ended state ([#1113](https://github.com/vector-im/element-x-android/issues/1113))
- [Poll] Add feature flag in developer options ([#1064](https://github.com/element-hq/element-x-android/issues/1064))
- [Polls] Improve UI and render ended state ([#1113](https://github.com/element-hq/element-x-android/issues/1113))
Other changes
-------------
- Compound: add `ListItem` and `ListSectionHeader` components. ([#990](https://github.com/vector-im/element-x-android/issues/990))
- Migrate `object` to `data object` in sealed interface / class #1135 ([#1135](https://github.com/vector-im/element-x-android/issues/1135))
- Compound: add `ListItem` and `ListSectionHeader` components. ([#990](https://github.com/element-hq/element-x-android/issues/990))
- Migrate `object` to `data object` in sealed interface / class #1135 ([#1135](https://github.com/element-hq/element-x-android/issues/1135))
Changes in Element X v0.1.2 (2023-08-16)
@ -236,20 +236,20 @@ Changes in Element X v0.1.2 (2023-08-16)
Bugfixes 🐛
----------
- Filter push notifications using push rules. ([#640](https://github.com/vector-im/element-x-android/issues/640))
- Use `for` instead of `forEach` in `DefaultDiffCacheInvalidator` to improve performance. ([#1035](https://github.com/vector-im/element-x-android/issues/1035))
- Filter push notifications using push rules. ([#640](https://github.com/element-hq/element-x-android/issues/640))
- Use `for` instead of `forEach` in `DefaultDiffCacheInvalidator` to improve performance. ([#1035](https://github.com/element-hq/element-x-android/issues/1035))
In development 🚧
----------------
- [Poll] Render start event in the timeline ([#1031](https://github.com/vector-im/element-x-android/issues/1031))
- [Poll] Render start event in the timeline ([#1031](https://github.com/element-hq/element-x-android/issues/1031))
Other changes
-------------
- Add Button component based on Compound designs ([#1021](https://github.com/vector-im/element-x-android/issues/1021))
- Compound: implement dialogs. ([#1043](https://github.com/vector-im/element-x-android/issues/1043))
- Compound: customise `IconButton` component. ([#1049](https://github.com/vector-im/element-x-android/issues/1049))
- Compound: implement `DropdownMenu` customisations. ([#1050](https://github.com/vector-im/element-x-android/issues/1050))
- Compound: implement Snackbar component. ([#1054](https://github.com/vector-im/element-x-android/issues/1054))
- Add Button component based on Compound designs ([#1021](https://github.com/element-hq/element-x-android/issues/1021))
- Compound: implement dialogs. ([#1043](https://github.com/element-hq/element-x-android/issues/1043))
- Compound: customise `IconButton` component. ([#1049](https://github.com/element-hq/element-x-android/issues/1049))
- Compound: implement `DropdownMenu` customisations. ([#1050](https://github.com/element-hq/element-x-android/issues/1050))
- Compound: implement Snackbar component. ([#1054](https://github.com/element-hq/element-x-android/issues/1054))
Changes in Element X v0.1.0 (2023-07-19)

View file

@ -1 +1 @@
* @vector-im/element-x-android-reviewers
* @element-hq/element-x-android-reviewers

View file

@ -187,7 +187,7 @@ internal fun PinIconPreview() = ElementPreview {
}
```
This will allow to preview the composable in both light and dark mode in Android Studio. This will also automatically add UI tests. The GitHub action [Record screenshots](https://github.com/vector-im/element-x-android/actions/workflows/recordScreenshots.yml) has to be run to record the new screenshots. The PR reviewer can trigger this for you if you're not part of the core team.
This will allow to preview the composable in both light and dark mode in Android Studio. This will also automatically add UI tests. The GitHub action [Record screenshots](https://github.com/element-hq/element-x-android/actions/workflows/recordScreenshots.yml) has to be run to record the new screenshots. The PR reviewer can trigger this for you if you're not part of the core team.
### Authors

View file

@ -1,4 +1,4 @@
[![Latest build](https://github.com/vector-im/element-x-android/actions/workflows/build.yml/badge.svg?query=branch%3Adevelop)](https://github.com/vector-im/element-x-android/actions/workflows/build.yml?query=branch%3Adevelop)
[![Latest build](https://github.com/element-hq/element-x-android/actions/workflows/build.yml/badge.svg?query=branch%3Adevelop)](https://github.com/element-hq/element-x-android/actions/workflows/build.yml?query=branch%3Adevelop)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=vector-im_element-x-android&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=vector-im_element-x-android)
[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=vector-im_element-x-android&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=vector-im_element-x-android)
[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=vector-im_element-x-android&metric=bugs)](https://sonarcloud.io/summary/new_code?id=vector-im_element-x-android)
@ -10,7 +10,7 @@
Element X Android is a [Matrix](https://matrix.org/) Android Client provided by [element.io](https://element.io/). This app is currently in a pre-alpha release stage with only basic functionalities.
The application is a total rewrite of [Element-Android](https://github.com/vector-im/element-android) using the [Matrix Rust SDK](https://github.com/matrix-org/matrix-rust-sdk) underneath and targeting devices running Android 6+. The UI layer is written using [Jetpack Compose](https://developer.android.com/jetpack/compose), and the navigation is managed using [Appyx](https://github.com/bumble-tech/appyx).
The application is a total rewrite of [Element-Android](https://github.com/element-hq/element-android) using the [Matrix Rust SDK](https://github.com/matrix-org/matrix-rust-sdk) underneath and targeting devices running Android 6+. The UI layer is written using [Jetpack Compose](https://developer.android.com/jetpack/compose), and the navigation is managed using [Appyx](https://github.com/bumble-tech/appyx).
Learn more about why we are building Element X in our blog post: [https://element.io/blog/element-x-experience-the-future-of-element/](https://element.io/blog/element-x-experience-the-future-of-element/).
@ -58,11 +58,11 @@ We're doing this as a way to share code between platforms and while we've seen p
## Status
This project is in work in progress. The app does not cover yet all functionalities we expect. The list of supported features can be found in [this issue](https://github.com/vector-im/element-x-android/issues/911).
This project is in work in progress. The app does not cover yet all functionalities we expect. The list of supported features can be found in [this issue](https://github.com/element-hq/element-x-android/issues/911).
## Contributing
Want to get actively involved in the project? You're more than welcome! A good way to start is to check the issues that are labelled with the [good first issue](https://github.com/vector-im/element-x-android/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) label. Let us know by commenting the issue that you're starting working on it.
Want to get actively involved in the project? You're more than welcome! A good way to start is to check the issues that are labelled with the [good first issue](https://github.com/element-hq/element-x-android/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) label. Let us know by commenting the issue that you're starting working on it.
But first make sure to read our [contribution guide](CONTRIBUTING.md) first.
@ -75,7 +75,7 @@ Makes sure to select the `app` configuration when building (as we also have samp
## Support
When you are experiencing an issue on Element X Android, please first search in [GitHub issues](https://github.com/vector-im/element-x-android/issues)
When you are experiencing an issue on Element X Android, please first search in [GitHub issues](https://github.com/element-hq/element-x-android/issues)
and then in [#element-x-android:matrix.org](https://matrix.to/#/#element-x-android:matrix.org).
If after your research you still have a question, ask at [#element-x-android:matrix.org](https://matrix.to/#/#element-x-android:matrix.org). Otherwise feel free to create a GitHub issue if you encounter a bug or a crash, by explaining clearly in detail what happened. You can also perform bug reporting from the application settings. This is especially recommended when you encounter a crash.

View file

@ -57,6 +57,7 @@ class TracingInitializer : Initializer<Unit> {
)
}
bugReporter.cleanLogDirectoryIfNeeded()
bugReporter.setCurrentTracingFilter(tracingConfiguration.filterConfiguration.filter)
tracingService.setupTracing(tracingConfiguration)
// Also set env variable for rust back trace
Os.setenv("RUST_BACKTRACE", "1", true)

View file

@ -33,13 +33,12 @@ import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode
import io.element.android.appnav.di.SessionComponentFactory
import io.element.android.libraries.architecture.NodeInputs
import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.DaggerComponentOwner
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.ui.di.MatrixUIBindings
import io.element.android.libraries.matrix.ui.media.ImageLoaderHolder
import kotlinx.parcelize.Parcelize
/**
@ -52,6 +51,7 @@ class LoggedInAppScopeFlowNode @AssistedInject constructor(
@Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>,
sessionComponentFactory: SessionComponentFactory,
private val imageLoaderHolder: ImageLoaderHolder,
) : ParentNode<LoggedInAppScopeFlowNode.NavTarget>(
navModel = PermanentNavModel(
navTargets = setOf(NavTarget),
@ -78,8 +78,7 @@ class LoggedInAppScopeFlowNode @AssistedInject constructor(
super.onBuilt()
lifecycle.subscribe(
onCreate = {
val imageLoaderFactory = bindings<MatrixUIBindings>().loggedInImageLoaderFactory()
Coil.setImageLoader(imageLoaderFactory)
Coil.setImageLoader(imageLoaderHolder.get(inputs.matrixClient))
},
)
}

View file

@ -256,6 +256,10 @@ class LoggedInFlowNode @AssistedInject constructor(
}
is NavTarget.Room -> {
val callback = object : RoomLoadedFlowNode.Callback {
override fun onOpenRoom(roomId: RoomId) {
backstack.push(NavTarget.Room(roomId))
}
override fun onForwardedToSingleRoom(roomId: RoomId) {
coroutineScope.launch { attachRoom(roomId) }
}

View file

@ -34,6 +34,8 @@ class IntentResolver @Inject constructor(
private val oidcIntentResolver: OidcIntentResolver
) {
fun resolve(intent: Intent): ResolvedIntent? {
if (intent.canBeIgnored()) return null
val deepLinkData = deeplinkParser.getFromIntent(intent)
if (deepLinkData != null) return ResolvedIntent.Navigation(deepLinkData)
@ -45,3 +47,8 @@ class IntentResolver @Inject constructor(
return null
}
}
private fun Intent.canBeIgnored(): Boolean {
return action == Intent.ACTION_MAIN &&
categories?.contains(Intent.CATEGORY_LAUNCHER) == true
}

View file

@ -74,6 +74,7 @@ class RoomLoadedFlowNode @AssistedInject constructor(
), DaggerComponentOwner {
interface Callback : Plugin {
fun onOpenRoom(roomId: RoomId)
fun onForwardedToSingleRoom(roomId: RoomId)
fun onOpenGlobalNotificationSettings()
}
@ -134,6 +135,10 @@ class RoomLoadedFlowNode @AssistedInject constructor(
override fun onOpenGlobalNotificationSettings() {
callbacks.forEach { it.onOpenGlobalNotificationSettings() }
}
override fun onOpenRoom(roomId: RoomId) {
callbacks.forEach { it.onOpenRoom(roomId) }
}
}
return roomDetailsEntryPoint.nodeBuilder(this, buildContext)
.params(RoomDetailsEntryPoint.Params(initialTarget))

View file

@ -25,7 +25,7 @@ import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.navmodel.backstack.activeElement
import com.bumble.appyx.testing.junit4.util.MainDispatcherRule
import com.bumble.appyx.testing.unit.common.helper.parentNodeTestHelper
import com.google.common.truth.Truth
import com.google.common.truth.Truth.assertThat
import io.element.android.appnav.di.RoomComponentFactory
import io.element.android.appnav.room.RoomLoadedFlowNode
import io.element.android.features.messages.api.MessagesEntryPoint
@ -83,7 +83,7 @@ class RoomFlowNodeTest {
}
override fun build(): Node {
return node(buildContext) {}.also {
return node(buildContext) {}.also {
nodeId = it.id
}
}
@ -122,10 +122,10 @@ class RoomFlowNodeTest {
val roomFlowNodeTestHelper = roomFlowNode.parentNodeTestHelper()
// THEN
Truth.assertThat(roomFlowNode.backstack.activeElement).isEqualTo(RoomLoadedFlowNode.NavTarget.Messages)
assertThat(roomFlowNode.backstack.activeElement).isEqualTo(RoomLoadedFlowNode.NavTarget.Messages)
roomFlowNodeTestHelper.assertChildHasLifecycle(RoomLoadedFlowNode.NavTarget.Messages, Lifecycle.State.CREATED)
val messagesNode = roomFlowNode.childNode(RoomLoadedFlowNode.NavTarget.Messages)!!
Truth.assertThat(messagesNode.id).isEqualTo(fakeMessagesEntryPoint.nodeId)
assertThat(messagesNode.id).isEqualTo(fakeMessagesEntryPoint.nodeId)
}
@Test
@ -147,6 +147,6 @@ class RoomFlowNodeTest {
// THEN
roomFlowNodeTestHelper.assertChildHasLifecycle(RoomLoadedFlowNode.NavTarget.RoomDetails, Lifecycle.State.CREATED)
val roomDetailsNode = roomFlowNode.childNode(RoomLoadedFlowNode.NavTarget.RoomDetails)!!
Truth.assertThat(roomDetailsNode.id).isEqualTo(fakeRoomDetailsEntryPoint.nodeId)
assertThat(roomDetailsNode.id).isEqualTo(fakeRoomDetailsEntryPoint.nodeId)
}
}

View file

@ -37,6 +37,17 @@ import org.robolectric.RuntimeEnvironment
@RunWith(RobolectricTestRunner::class)
class IntentResolverTest {
@Test
fun `resolve launcher intent should return null`() {
val sut = createIntentResolver()
val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply {
action = Intent.ACTION_MAIN
addCategory(Intent.CATEGORY_LAUNCHER)
}
val result = sut.resolve(intent)
assertThat(result).isNull()
}
@Test
fun `test resolve navigation intent root`() {
val sut = createIntentResolver()

View file

@ -17,7 +17,7 @@
package io.element.android.appnav.room
import app.cash.turbine.test
import com.google.common.truth.Truth
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.api.roomlist.RoomList
import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_SESSION_ID
@ -39,8 +39,8 @@ class LoadingRoomStateFlowFactoryTest {
flowFactory
.create(this, A_ROOM_ID)
.test {
Truth.assertThat(awaitItem()).isEqualTo(LoadingRoomState.Loading)
Truth.assertThat(awaitItem()).isEqualTo(LoadingRoomState.Loaded(room))
assertThat(awaitItem()).isEqualTo(LoadingRoomState.Loading)
assertThat(awaitItem()).isEqualTo(LoadingRoomState.Loaded(room))
}
}
@ -53,10 +53,10 @@ class LoadingRoomStateFlowFactoryTest {
flowFactory
.create(this, A_ROOM_ID)
.test {
Truth.assertThat(awaitItem()).isEqualTo(LoadingRoomState.Loading)
assertThat(awaitItem()).isEqualTo(LoadingRoomState.Loading)
matrixClient.givenGetRoomResult(A_ROOM_ID, room)
roomListService.postAllRoomsLoadingState(RoomList.LoadingState.Loaded(1))
Truth.assertThat(awaitItem()).isEqualTo(LoadingRoomState.Loaded(room))
assertThat(awaitItem()).isEqualTo(LoadingRoomState.Loaded(room))
}
}
@ -68,12 +68,9 @@ class LoadingRoomStateFlowFactoryTest {
flowFactory
.create(this, A_ROOM_ID)
.test {
Truth.assertThat(awaitItem()).isEqualTo(LoadingRoomState.Loading)
assertThat(awaitItem()).isEqualTo(LoadingRoomState.Loading)
roomListService.postAllRoomsLoadingState(RoomList.LoadingState.Loaded(1))
Truth.assertThat(awaitItem()).isEqualTo(LoadingRoomState.Error)
assertThat(awaitItem()).isEqualTo(LoadingRoomState.Error)
}
}
}

View file

@ -62,7 +62,7 @@ allprojects {
config.from(files("$rootDir/tools/detekt/detekt.yml"))
}
dependencies {
detektPlugins("io.nlopez.compose.rules:detekt:0.3.3")
detektPlugins("io.nlopez.compose.rules:detekt:0.3.5")
}
// KtLint
@ -130,10 +130,10 @@ sonar {
property("sonar.host.url", "https://sonarcloud.io")
property("sonar.projectVersion", "1.0") // TODO project(":app").android.defaultConfig.versionName)
property("sonar.sourceEncoding", "UTF-8")
property("sonar.links.homepage", "https://github.com/vector-im/element-x-android/")
property("sonar.links.ci", "https://github.com/vector-im/element-x-android/actions")
property("sonar.links.scm", "https://github.com/vector-im/element-x-android/")
property("sonar.links.issue", "https://github.com/vector-im/element-x-android/issues")
property("sonar.links.homepage", "https://github.com/element-hq/element-x-android/")
property("sonar.links.ci", "https://github.com/element-hq/element-x-android/actions")
property("sonar.links.scm", "https://github.com/element-hq/element-x-android/")
property("sonar.links.issue", "https://github.com/element-hq/element-x-android/issues")
property("sonar.organization", "new_vector_ltd_organization")
property("sonar.login", if (project.hasProperty("SONAR_LOGIN")) project.property("SONAR_LOGIN")!! else "invalid")

View file

@ -1 +1 @@
Remove `:libraries:theme` module, extract theme and tokens to [Compound Android](https://github.com/vector-im/compound-android).
Remove `:libraries:theme` module, extract theme and tokens to [Compound Android](https://github.com/element-hq/compound-android).

1
changelog.d/1944.misc Normal file
View file

@ -0,0 +1 @@
Add toggle in the notification settings to disable notifications for room invites.

1
changelog.d/1950.bugfix Normal file
View file

@ -0,0 +1 @@
Fix rendering of user name with vertical text by clipping the text.

1
changelog.d/1965.misc Normal file
View file

@ -0,0 +1 @@
Update rendering of Emojis displayed during verification.

1
changelog.d/1979.misc Normal file
View file

@ -0,0 +1 @@
Hide sender info in direct rooms

1
changelog.d/1991.bugfix Normal file
View file

@ -0,0 +1 @@
Fix avatar not displayed in notification when the app is not in background

1
changelog.d/1991.misc Normal file
View file

@ -0,0 +1 @@
Render images in Notification

View file

@ -117,7 +117,7 @@ You can also have access to the aars through the [release](https://github.com/ma
#### Build the SDK locally
Easiest way: run the script [./tools/sdk/build_rust_sdk.sh](./tools/sdk/build_rust_sdk.sh) and just answer the questions.
Easiest way: run the script [../tools/sdk/build_rust_sdk.sh](../tools/sdk/build_rust_sdk.sh) and just answer the questions.
Legacy way:
@ -322,7 +322,7 @@ We are using [Gradle version catalog](https://docs.gradle.org/current/userguide/
All the dependencies (including android artifact, gradle plugin, etc.) should be declared in [../gradle/libs.versions.toml](libs.versions.toml) file.
Some dependency, mainly because they are not shared can be declared in `build.gradle.kts` files.
[Renovate](https://github.com/apps/renovate) is set up on the project. This tool will automatically create Pull Request to upgrade our dependencies one by one. A [dependency dashboard issue](https://github.com/vector-im/element-x-android/issues/150) is maintained by the tool and allow to perform some actions.
[Renovate](https://github.com/apps/renovate) is set up on the project. This tool will automatically create Pull Request to upgrade our dependencies one by one. A [dependency dashboard issue](https://github.com/element-hq/element-x-android/issues/150) is maintained by the tool and allow to perform some actions.
### Test

View file

@ -63,7 +63,7 @@ bundle exec danger pr <PR_URL> --dangerfile=./tools/danger/dangerfile.js
For instance:
```shell
bundle exec danger pr https://github.com/vector-im/element-android/pull/6637 --dangerfile=./tools/danger/dangerfile.js
bundle exec danger pr https://github.com/element-hq/element-android/pull/6637 --dangerfile=./tools/danger/dangerfile.js
```
We may need to create a GitHub token to have less API rate limiting, and then set the env var:
@ -84,7 +84,7 @@ bundle exec danger-kotlin pr <PR_URL> --dangerfile=./tools/danger/dangerfile.js
To let Danger check all the PRs, including PRs form forks, a GitHub account have been created:
- login: ElementBot
- password: Stored on Passbolt
- GitHub token: A token with limited access has been created and added to the repository https://github.com/vector-im/element-android as secret DANGER_GITHUB_API_TOKEN. This token is not saved anywhere else. In case of problem, just delete it and create a new one, then update the secret.
- GitHub token: A token with limited access has been created and added to the repository https://github.com/element-hq/element-x-android as secret DANGER_GITHUB_API_TOKEN. This token is not saved anywhere else. In case of problem, just delete it and create a new one, then update the secret.
PRs from forks do not always have access to the secret `secrets.DANGER_GITHUB_API_TOKEN`, so `secrets.GITHUB_TOKEN` is also provided to the job environment. If `secrets.DANGER_GITHUB_API_TOKEN` is available, it will be used, so user `ElementBot` will comment the PR. Else `secrets.GITHUB_TOKEN` will be used, and bot `github-actions` will comment the PR.

View file

@ -8,15 +8,15 @@
1) Disable "Use Android Studio HTTP proxy settings" and pick "Manual proxy configuration".
1) Set `127.0.0.1` as "Host name" and `8080` as "Port number".
1) Click "Apply" and verify that "Proxy status" is "Success" and close the settings window.
<img width="932" alt="Screenshot 2023-10-04 at 14 48 47" src="https://github.com/vector-im/element-x-android/assets/1273124/bf99a053-20b0-42a4-91d3-9602f709f684">
<img width="932" alt="Screenshot 2023-10-04 at 14 48 47" src="https://github.com/element-hq/element-x-android/assets/1273124/bf99a053-20b0-42a4-91d3-9602f709f684">
1) Install the mitmproxy CA cert (this is needed to see traffic from java/kotlin code, it's not needed for traffic coming from native code e.g. the matrix-rust-sdk).
1) Open the emulator Chrome browser app
1) Go to the url `mitm.it`
1) Follow the instructions to install the CA cert on Android devices.
<img width="606" alt="Screenshot 2023-10-04 at 14 51 27" src="https://github.com/vector-im/element-x-android/assets/1273124/5f2b6f27-6958-4ea7-97fe-c7f06d105da5">
<img width="606" alt="Screenshot 2023-10-04 at 14 51 27" src="https://github.com/element-hq/element-x-android/assets/1273124/5f2b6f27-6958-4ea7-97fe-c7f06d105da5">
1) Slightly modify the Element X app source code.
1) Go to the `RustMatrixClientFactory.create()` method.
1) Add `.disableSslVerification()` in the `ClientBuilder` method chain.
1) Build and run the Element X app.
1) Enjoy, you will see all the traffic in mitmproxy's web interface.
<img width="1110" alt="Screenshot 2023-10-04 at 14 50 03" src="https://github.com/vector-im/element-x-android/assets/1273124/5d039efd-448d-426c-a384-dbbceb9f33ac">
<img width="1110" alt="Screenshot 2023-10-04 at 14 50 03" src="https://github.com/element-hq/element-x-android/assets/1273124/5d039efd-448d-426c-a384-dbbceb9f33ac">

View file

@ -64,7 +64,7 @@ A comprehensive [color definition documentation](https://compound.element.io/?pa
Most icons should be available as part of the [Compound icon library](https://compound.element.io/?path=/docs/tokens-icons--docs)
All drawable are auto-generated as part of the design tokens library. You can find
all assets in [`vector-im/compound-design-tokens#assets/android`](https://github.com/vector-im/compound-design-tokens/tree/develop/assets/android)
all assets in [`element-hq/compound-design-tokens#assets/android`](https://github.com/element-hq/compound-design-tokens/tree/main/assets/android)
If you are missing an icon, follow to [contribution guidelines for icons](https://www.figma.com/file/gkNXqPoiJhEv2wt0EJpew4/Compound-Icons?type=design&node-id=178-3119&t=j2uSJD9xPXJn5aRM-0)

View file

@ -18,11 +18,11 @@ The easiest way to do that is to use the debug signature that is shared between
You can clone the project by running:
```bash
git clone git@github.com:vector-im/element-x-android.git
git clone git@github.com:element-hq/element-x-android.git
```
or
```bash
git clone https://github.com/vector-im/element-x-android.git
git clone https://github.com/element-hq/element-x-android.git
```
You will also need to install [bundletool](https://developer.android.com/studio/command-line/bundletool). On MacOS, you can run the following command:
@ -33,7 +33,7 @@ brew install bundletool
## Steps
1. Open the GitHub release that you want to install from https://github.com/vector-im/element-x-android/releases
1. Open the GitHub release that you want to install from https://github.com/element-hq/element-x-android/releases
2. Download the asset `app-release-signed.aab`
3. Navigate to the folder where you cloned the project and run the following command:
```bash

View file

@ -1,14 +1,14 @@
This file contains some rough notes about Oidc implementation, with some examples of actual data.
[ios implementation](https://github.com/vector-im/element-x-ios/compare/develop...doug/oidc-temp)
[ios implementation](https://github.com/element-hq/element-x-ios/compare/develop...doug/oidc-temp)
Rust sdk branch: https://github.com/matrix-org/matrix-rust-sdk/tree/oidc-ffi
Figma https://www.figma.com/file/o9p34zmiuEpZRyvZXJZAYL/FTUE?node-id=133-5426&t=yQXKeANatk6keoZF-0
Server list: https://github.com/vector-im/oidc-playground
Server list: https://github.com/element-hq/oidc-playground
Metadata iOS: (from https://github.com/vector-im/element-x-ios/blob/5f9d07377cebc4f21d9668b1a25f6e3bb22f64a1/ElementX/Sources/Services/Authentication/AuthenticationServiceProxy.swift#L28)
Metadata iOS: (from https://github.com/element-hq/element-x-ios/blob/5f9d07377cebc4f21d9668b1a25f6e3bb22f64a1/ElementX/Sources/Services/Authentication/AuthenticationServiceProxy.swift#L28)
clientName: InfoPlistReader.main.bundleDisplayName,
redirectUri: "io.element:/callback",

View file

@ -86,13 +86,13 @@ Exceptions can occur:
We use automatic assignment for PR reviews. **A PR is automatically routed by GitHub to one team member** using the round robin algorithm. Additional reviewers can be used for complex changes or when the first reviewer is not confident enough on the changes.
The process is the following:
- The PR creator selects the [element-x-android-reviewers](https://github.com/orgs/vector-im/teams/element-x-android-reviewers) team as a reviewer.
- The PR creator selects the [element-x-android-reviewers](https://github.com/orgs/element-hq/teams/element-x-android-reviewers) team as a reviewer.
- GitHub automatically assign the reviewer. If the reviewer is not available (holiday, etc.), remove them and set again the team, GitHub will select another reviewer.
- Alternatively, the PR creator can directly assign specific people if they have another Android developer in their team or they think a specific reviewer should take a look at their PR.
- Reviewers get a notification to make the review: they review the code following the good practice (see the rest of this document).
- After making their own review, if they feel not confident enough, they can ask another person for a full review, or they can tag someone within a PR comment to check specific lines.
For PRs coming from the community, the issue wrangler can assign either the team [element-x-android-reviewers](https://github.com/orgs/vector-im/teams/element-x-android-reviewers) or any member directly.
For PRs coming from the community, the issue wrangler can assign either the team [element-x-android-reviewers](https://github.com/orgs/element-hq/teams/element-x-android-reviewers) or any member directly.
##### PR review time

View file

@ -30,7 +30,7 @@ If installed correctly, `git push` and `git pull` will now include LFS content.
## Recording
Recording of screenshots is done by triggering the GitHub action [Record screenshots](https://github.com/vector-im/element-x-android/actions/workflows/recordScreenshots.yml), to avoid differences of generated binary files (png images) depending on developers' environment.
Recording of screenshots is done by triggering the GitHub action [Record screenshots](https://github.com/element-hq/element-x-android/actions/workflows/recordScreenshots.yml), to avoid differences of generated binary files (png images) depending on developers' environment.
So basically, you will create a branch, do some commits with your work on it, then push your branch, trigger the GitHub action to record the screenshots (only if you think preview may have changed), and finally create a pull request. The GitHub action will record the screenshots and commit the changes to the branch.

View file

@ -1,2 +1,2 @@
First release of Element X 🚀!
Full changelog: https://github.com/vector-im/element-x-android/releases
Full changelog: https://github.com/element-hq/element-x-android/releases

View file

@ -1,2 +1,2 @@
Main changes in this version: bug fixes and add OIDC support.
Full changelog: https://github.com/vector-im/element-x-android/releases
Full changelog: https://github.com/element-hq/element-x-android/releases

View file

@ -1,2 +1,2 @@
Main changes in this version: bug fixes and add OIDC support.
Full changelog: https://github.com/vector-im/element-x-android/releases
Full changelog: https://github.com/element-hq/element-x-android/releases

View file

@ -1,2 +1,2 @@
Main changes in this version: bugfixes.
Full changelog: https://github.com/vector-im/element-x-android/releases
Full changelog: https://github.com/element-hq/element-x-android/releases

View file

@ -1,2 +1,2 @@
Main changes in this version: Element Call, design update, bugfixes
Full changelog: https://github.com/vector-im/element-x-android/releases
Full changelog: https://github.com/element-hq/element-x-android/releases

View file

@ -1,2 +1,2 @@
Main changes in this version: Element Call, design update, bugfixes
Full changelog: https://github.com/vector-im/element-x-android/releases
Full changelog: https://github.com/element-hq/element-x-android/releases

View file

@ -1,2 +1,2 @@
Main changes in this version: bugfixes
Full changelog: https://github.com/vector-im/element-x-android/releases
Full changelog: https://github.com/element-hq/element-x-android/releases

View file

@ -1,2 +1,2 @@
Main changes in this version: bugfixes.
Full changelog: https://github.com/vector-im/element-x-android/releases
Full changelog: https://github.com/element-hq/element-x-android/releases

View file

@ -1,2 +1,2 @@
Main changes in this version: bugfixes.
Full changelog: https://github.com/vector-im/element-x-android/releases
Full changelog: https://github.com/element-hq/element-x-android/releases

View file

@ -1,2 +1,2 @@
Main changes in this version: Element Call, voice message.
Full changelog: https://github.com/vector-im/element-x-android/releases
Full changelog: https://github.com/element-hq/element-x-android/releases

View file

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

View file

@ -4,4 +4,4 @@ Main changes in this version:
- Adding mentions to a message is now possible, but it's disabled by default as it's a work in progress. They can be enabled in the developer options.
- Voice messages behavior changed: there is no need to keep pressing to record, to start recording a message just tap on the mic button, then tap again to stop recording.
- Added a marker in the timeline to indicate the starting point of the room messages.
Full changelog: https://github.com/vector-im/element-x-android/releases
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_share_data">"Elemzési adatok megosztása"</string>
<string name="screen_analytics_settings_help_us_improve">"Anonim használati adatok megosztása a problémák azonosítása érdekében."</string>
<string name="screen_analytics_settings_read_terms">"%1$s olvashatja el a feltételeinket."</string>
<string name="screen_analytics_settings_read_terms_content_link">"Itt"</string>
</resources>

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_share_data">"Bagikan data analitik"</string>
<string name="screen_analytics_settings_help_us_improve">"Bagikan data penggunaan anonim untuk membantu kami mengidentifikasi masalah."</string>
<string name="screen_analytics_settings_read_terms">"Anda dapat membaca semua persyaratan kami %1$s."</string>
<string name="screen_analytics_settings_read_terms_content_link">"di sini"</string>
</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">"Nem rögzítünk vagy profilozunk személyes adatokat"</string>
<string name="screen_analytics_prompt_help_us_improve">"Anonim használati adatok megosztása a problémák azonosítása érdekében."</string>
<string name="screen_analytics_prompt_read_terms">"%1$s olvashatja el a feltételeinket."</string>
<string name="screen_analytics_prompt_read_terms_content_link">"Itt"</string>
<string name="screen_analytics_prompt_settings">"Ezt bármikor kikapcsolhatja"</string>
<string name="screen_analytics_prompt_third_party_sharing">"Adatait nem osztjuk meg harmadik felekkel"</string>
<string name="screen_analytics_prompt_title">"Segítsen az %1$s fejlesztésében"</string>
</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">"Kami tidak akan merekam atau memprofil data pribadi apa pun"</string>
<string name="screen_analytics_prompt_help_us_improve">"Bagikan data penggunaan anonim untuk membantu kami mengidentifikasi masalah."</string>
<string name="screen_analytics_prompt_read_terms">"Anda dapat membaca semua persyaratan kami %1$s."</string>
<string name="screen_analytics_prompt_read_terms_content_link">"di sini"</string>
<string name="screen_analytics_prompt_settings">"Anda dapat mematikan ini kapan saja"</string>
<string name="screen_analytics_prompt_third_party_sharing">"Kami tidak akan membagikan data Anda dengan pihak ketiga"</string>
<string name="screen_analytics_prompt_title">"Bantu sempurnakan %1$s"</string>
</resources>

View file

@ -16,7 +16,7 @@
package io.element.android.features.cachecleaner.impl
import com.google.common.truth.Truth
import com.google.common.truth.Truth.assertThat
import io.element.android.tests.testutils.testCoroutineDispatchers
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
@ -46,9 +46,9 @@ class DefaultCacheCleanerTest {
// Check the files are gone but the sub dirs are not.
DefaultCacheCleaner.SUBDIRS_TO_CLEANUP.forEach {
File(temporaryFolder.root, it).apply {
Truth.assertThat(exists()).isTrue()
Truth.assertThat(isDirectory).isTrue()
Truth.assertThat(listFiles()).isEmpty()
assertThat(exists()).isTrue()
assertThat(isDirectory).isTrue()
assertThat(listFiles()).isEmpty()
}
}
}

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="call_foreground_service_channel_title_android">"Folyamatban lévő hívás"</string>
<string name="call_foreground_service_message_android">"Koppintson a híváshoz való visszatéréshez"</string>
<string name="call_foreground_service_title_android">"☎️ Hívás folyamatban"</string>
</resources>

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="call_foreground_service_channel_title_android">"Panggilan berlangsung"</string>
<string name="call_foreground_service_message_android">"Ketuk untuk kembali ke panggilan"</string>
<string name="call_foreground_service_title_android">"☎️ Panggilan sedang berlangsung"</string>
</resources>

View file

@ -39,7 +39,6 @@ class MapWebkitPermissionsTest {
@Test
fun `given any other permission, it returns nothing`() {
val permission = mapWebkitPermissions(arrayOf(PermissionRequest.RESOURCE_PROTECTED_MEDIA_ID))
assertThat(permission).isEqualTo(emptyList<String>())
assertThat(permission).isEmpty()
}
}

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.createroom.api
import androidx.compose.runtime.MutableState
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.UserId
interface StartDMAction {
/**
* Try to find an existing DM with the given user, or create one if none exists.
* @param userId The user to start a DM with.
* @param actionState The state to update with the result of the action.
*/
suspend fun execute(userId: UserId, actionState: MutableState<Async<RoomId>>)
}

View file

@ -67,6 +67,7 @@ dependencies {
testImplementation(projects.libraries.mediaupload.test)
testImplementation(projects.libraries.permissions.test)
testImplementation(projects.libraries.usersearch.test)
testImplementation(projects.features.createroom.test)
testImplementation(projects.tests.testutils)
ksp(libs.showkase.processor)

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.createroom.impl
import androidx.compose.runtime.MutableState
import com.squareup.anvil.annotations.ContributesBinding
import im.vector.app.features.analytics.plan.CreatedRoom
import io.element.android.features.createroom.api.StartDMAction
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.room.StartDMResult
import io.element.android.libraries.matrix.api.room.startDM
import io.element.android.services.analytics.api.AnalyticsService
import javax.inject.Inject
@ContributesBinding(SessionScope::class)
class DefaultStartDMAction @Inject constructor(
private val matrixClient: MatrixClient,
private val analyticsService: AnalyticsService,
) : StartDMAction {
override suspend fun execute(userId: UserId, actionState: MutableState<Async<RoomId>>) {
actionState.value = Async.Loading()
when (val result = matrixClient.startDM(userId)) {
is StartDMResult.Success -> {
if (result.isNew) {
analyticsService.capture(CreatedRoom(isDM = true))
}
actionState.value = Async.Success(result.roomId)
}
is StartDMResult.Failure -> {
actionState.value = Async.Failure(result.throwable)
}
}
}
}

View file

@ -21,21 +21,16 @@ import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import im.vector.app.features.analytics.plan.CreatedRoom
import io.element.android.features.createroom.api.StartDMAction
import io.element.android.features.createroom.impl.userlist.SelectionMode
import io.element.android.features.createroom.impl.userlist.UserListDataStore
import io.element.android.features.createroom.impl.userlist.UserListPresenter
import io.element.android.features.createroom.impl.userlist.UserListPresenterArgs
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.runCatchingUpdatingState
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.usersearch.api.UserRepository
import io.element.android.services.analytics.api.AnalyticsService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import javax.inject.Inject
@ -43,8 +38,7 @@ class CreateRoomRootPresenter @Inject constructor(
presenterFactory: UserListPresenter.Factory,
userRepository: UserRepository,
userListDataStore: UserListDataStore,
private val matrixClient: MatrixClient,
private val analyticsService: AnalyticsService,
private val startDMAction: StartDMAction,
private val buildMeta: BuildMeta,
) : Presenter<CreateRoomRootState> {
@ -61,37 +55,22 @@ class CreateRoomRootPresenter @Inject constructor(
val userListState = presenter.present()
val localCoroutineScope = rememberCoroutineScope()
val startDmAction: MutableState<Async<RoomId>> = remember { mutableStateOf(Async.Uninitialized) }
val startDmActionState: MutableState<Async<RoomId>> = remember { mutableStateOf(Async.Uninitialized) }
fun handleEvents(event: CreateRoomRootEvents) {
when (event) {
is CreateRoomRootEvents.StartDM -> localCoroutineScope.startDm(event.matrixUser, startDmAction)
CreateRoomRootEvents.CancelStartDM -> startDmAction.value = Async.Uninitialized
is CreateRoomRootEvents.StartDM -> localCoroutineScope.launch {
startDMAction.execute(event.matrixUser.userId, startDmActionState)
}
CreateRoomRootEvents.CancelStartDM -> startDmActionState.value = Async.Uninitialized
}
}
return CreateRoomRootState(
applicationName = buildMeta.applicationName,
userListState = userListState,
startDmAction = startDmAction.value,
startDmAction = startDmActionState.value,
eventSink = ::handleEvents,
)
}
private fun CoroutineScope.startDm(matrixUser: MatrixUser, startDmAction: MutableState<Async<RoomId>>) = launch {
suspend {
matrixClient.findDM(matrixUser.userId).use { existingDM ->
existingDM?.roomId ?: createDM(matrixUser)
}
}.runCatchingUpdatingState(startDmAction)
}
private suspend fun createDM(user: MatrixUser): RoomId {
return matrixClient
.createDM(user.userId)
.onSuccess {
analyticsService.capture(CreatedRoom(isDM = true))
}
.getOrThrow()
}
}

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_create_room_action_create_room">"Új szoba"</string>
<string name="screen_create_room_action_invite_people">"Hívja meg ismerőseit az Elementbe"</string>
<string name="screen_create_room_add_people_title">"Emberek meghívása"</string>
<string name="screen_create_room_error_creating_room">"Hiba történt a szoba létrehozásakor"</string>
<string name="screen_create_room_private_option_description">"A szobában lévő üzenetek titkosítottak. A titkosítást utólag nem lehet kikapcsolni."</string>
<string name="screen_create_room_private_option_title">"Privát szoba (csak meghívással)"</string>
<string name="screen_create_room_public_option_description">"Az üzenetek nincsenek titkosítva, és bárki elolvashatja őket. A titkosítást később is engedélyezheti."</string>
<string name="screen_create_room_public_option_title">"Nyilvános szoba (bárki)"</string>
<string name="screen_create_room_room_name_label">"Szoba neve"</string>
<string name="screen_create_room_topic_label">"Téma (nem kötelező)"</string>
<string name="screen_start_chat_error_starting_chat">"Hiba történt a csevegés indításakor"</string>
<string name="screen_create_room_title">"Szoba létrehozása"</string>
</resources>

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_create_room_action_create_room">"Ruangan baru"</string>
<string name="screen_create_room_action_invite_people">"Undang teman ke Element"</string>
<string name="screen_create_room_add_people_title">"Undang seseorang"</string>
<string name="screen_create_room_error_creating_room">"Terjadi kesalahan saat membuat ruangan"</string>
<string name="screen_create_room_private_option_description">"Pesan di ruangan ini dienkripsi. Enkripsi tidak dapat dinonaktifkan setelahnya."</string>
<string name="screen_create_room_private_option_title">"Ruangan pribadi (hanya undangan)"</string>
<string name="screen_create_room_public_option_description">"Pesan tidak dienkripsi dan siapa pun dapat membacanya. Anda dapat mengaktifkan enkripsi di kemudian hari."</string>
<string name="screen_create_room_public_option_title">"Ruang publik (siapa saja)"</string>
<string name="screen_create_room_room_name_label">"Nama ruangan"</string>
<string name="screen_create_room_topic_label">"Topik (opsional)"</string>
<string name="screen_start_chat_error_starting_chat">"Terjadi kesalahan saat mencoba memulai obrolan"</string>
<string name="screen_create_room_title">"Buat ruangan"</string>
</resources>

View file

@ -0,0 +1,82 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.createroom.impl
import androidx.compose.runtime.mutableStateOf
import com.google.common.truth.Truth.assertThat
import im.vector.app.features.analytics.plan.CreatedRoom
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_THROWABLE
import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.analytics.test.FakeAnalyticsService
import kotlinx.coroutines.test.runTest
import org.junit.Test
class DefaultStartDMActionTests {
@Test
fun `when dm is found, assert state is updated with given room id`() = runTest {
val matrixClient = FakeMatrixClient().apply {
givenFindDmResult(A_ROOM_ID)
}
val action = createStartDMAction(matrixClient)
val state = mutableStateOf<Async<RoomId>>(Async.Uninitialized)
action.execute(A_USER_ID, state)
assertThat(state.value).isEqualTo(Async.Success(A_ROOM_ID))
}
@Test
fun `when dm is not found, assert dm is created, state is updated with given room id and analytics get called`() = runTest {
val matrixClient = FakeMatrixClient().apply {
givenFindDmResult(null)
givenCreateDmResult(Result.success(A_ROOM_ID))
}
val analyticsService = FakeAnalyticsService()
val action = createStartDMAction(matrixClient, analyticsService)
val state = mutableStateOf<Async<RoomId>>(Async.Uninitialized)
action.execute(A_USER_ID, state)
assertThat(state.value).isEqualTo(Async.Success(A_ROOM_ID))
assertThat(analyticsService.capturedEvents).containsExactly(CreatedRoom(isDM = true))
}
@Test
fun `when dm creation fails, assert state is updated with given error`() = runTest {
val matrixClient = FakeMatrixClient().apply {
givenFindDmResult(null)
givenCreateDmResult(Result.failure(A_THROWABLE))
}
val action = createStartDMAction(matrixClient)
val state = mutableStateOf<Async<RoomId>>(Async.Uninitialized)
action.execute(A_USER_ID, state)
assertThat(state.value).isEqualTo(Async.Failure<RoomId>(A_THROWABLE))
}
private fun createStartDMAction(
matrixClient: MatrixClient = FakeMatrixClient(),
analyticsService: AnalyticsService = FakeAnalyticsService(),
): DefaultStartDMAction {
return DefaultStartDMAction(
matrixClient = matrixClient,
analyticsService = analyticsService,
)
}
}

View file

@ -20,25 +20,21 @@ import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import im.vector.app.features.analytics.plan.CreatedRoom
import io.element.android.features.createroom.api.StartDMAction
import io.element.android.features.createroom.impl.userlist.FakeUserListPresenter
import io.element.android.features.createroom.impl.userlist.FakeUserListPresenterFactory
import io.element.android.features.createroom.impl.userlist.UserListDataStore
import io.element.android.features.createroom.impl.userlist.aUserListState
import io.element.android.features.createroom.test.FakeStartDMAction
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_THROWABLE
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.core.aBuildMeta
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
import io.element.android.libraries.usersearch.test.FakeUserRepository
import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.tests.testutils.WarmUpRule
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@ -47,142 +43,57 @@ class CreateRoomRootPresenterTests {
@get:Rule
val warmUpRule = WarmUpRule()
private lateinit var userRepository: FakeUserRepository
private lateinit var presenter: CreateRoomRootPresenter
private lateinit var fakeUserListPresenter: FakeUserListPresenter
private lateinit var fakeMatrixClient: FakeMatrixClient
private lateinit var fakeAnalyticsService: FakeAnalyticsService
@Before
fun setup() {
fakeUserListPresenter = FakeUserListPresenter()
fakeMatrixClient = FakeMatrixClient()
fakeAnalyticsService = FakeAnalyticsService()
userRepository = FakeUserRepository()
presenter = CreateRoomRootPresenter(
presenterFactory = FakeUserListPresenterFactory(fakeUserListPresenter),
userRepository = userRepository,
userListDataStore = UserListDataStore(),
matrixClient = fakeMatrixClient,
analyticsService = fakeAnalyticsService,
buildMeta = aBuildMeta(),
)
}
@Test
fun `present - initial state`() = runTest {
fun `present - start DM action complete scenario`() = runTest {
val startDMAction = FakeStartDMAction()
val presenter = createCreateRoomRootPresenter(startDMAction)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
assertThat(initialState.startDmAction).isInstanceOf(Async.Uninitialized::class.java)
assertThat(initialState.applicationName).isEqualTo(aBuildMeta().applicationName)
assertThat(initialState.userListState.selectedUsers).isEmpty()
assertThat(initialState.userListState.isSearchActive).isFalse()
assertThat(initialState.userListState.isMultiSelectionEnabled).isFalse()
}
}
@Test
fun `present - trigger create DM action`() = runTest {
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
val matrixUser = MatrixUser(UserId("@name:domain"))
val createDmResult = Result.success(RoomId("!createDmResult:domain"))
fakeMatrixClient.givenFindDmResult(null)
fakeMatrixClient.givenCreateDmResult(createDmResult)
initialState.eventSink(CreateRoomRootEvents.StartDM(matrixUser))
assertThat(awaitItem().startDmAction).isInstanceOf(Async.Loading::class.java)
val stateAfterStartDM = awaitItem()
assertThat(stateAfterStartDM.startDmAction).isInstanceOf(Async.Success::class.java)
assertThat(stateAfterStartDM.startDmAction.dataOrNull()).isEqualTo(createDmResult.getOrNull())
}
}
@Test
fun `present - creating a DM records analytics event`() = runTest {
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
val matrixUser = MatrixUser(UserId("@name:domain"))
val createDmResult = Result.success(RoomId("!createDmResult:domain"))
fakeMatrixClient.givenFindDmResult(null)
fakeMatrixClient.givenCreateDmResult(createDmResult)
initialState.eventSink(CreateRoomRootEvents.StartDM(matrixUser))
skipItems(2)
val analyticsEvent = fakeAnalyticsService.capturedEvents.filterIsInstance<CreatedRoom>().firstOrNull()
assertThat(analyticsEvent).isNotNull()
assertThat(analyticsEvent?.isDM).isTrue()
}
}
@Test
fun `present - trigger retrieve DM action`() = runTest {
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
val matrixUser = MatrixUser(UserId("@name:domain"))
val fakeDmResult = FakeMatrixRoom(roomId = RoomId("!fakeDmResult:domain"))
fakeMatrixClient.givenFindDmResult(fakeDmResult)
initialState.eventSink(CreateRoomRootEvents.StartDM(matrixUser))
val stateAfterStartDM = awaitItem()
assertThat(stateAfterStartDM.startDmAction).isInstanceOf(Async.Success::class.java)
assertThat(stateAfterStartDM.startDmAction.dataOrNull()).isEqualTo(fakeDmResult.roomId)
assertThat(fakeAnalyticsService.capturedEvents.filterIsInstance<CreatedRoom>()).isEmpty()
}
}
@Test
fun `present - trigger retry create DM action`() = runTest {
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
val matrixUser = MatrixUser(UserId("@name:domain"))
val createDmResult = Result.success(RoomId("!createDmResult:domain"))
fakeUserListPresenter.givenState(aUserListState().copy(selectedUsers = persistentListOf(matrixUser)))
fakeMatrixClient.givenFindDmResult(null)
fakeMatrixClient.givenCreateDmError(A_THROWABLE)
fakeMatrixClient.givenCreateDmResult(createDmResult)
val startDMSuccessResult = Async.Success(A_ROOM_ID)
val startDMFailureResult = Async.Failure<RoomId>(A_THROWABLE)
// Failure
startDMAction.givenExecuteResult(startDMFailureResult)
initialState.eventSink(CreateRoomRootEvents.StartDM(matrixUser))
assertThat(awaitItem().startDmAction).isInstanceOf(Async.Loading::class.java)
val stateAfterStartDM = awaitItem()
assertThat(stateAfterStartDM.startDmAction).isInstanceOf(Async.Failure::class.java)
assertThat(fakeAnalyticsService.capturedEvents.filterIsInstance<CreatedRoom>()).isEmpty()
awaitItem().also { state ->
assertThat(state.startDmAction).isEqualTo(startDMFailureResult)
state.eventSink(CreateRoomRootEvents.CancelStartDM)
}
// Cancel
stateAfterStartDM.eventSink(CreateRoomRootEvents.CancelStartDM)
val stateAfterCancel = awaitItem()
assertThat(stateAfterCancel.startDmAction).isInstanceOf(Async.Uninitialized::class.java)
// Failure
stateAfterCancel.eventSink(CreateRoomRootEvents.StartDM(matrixUser))
// Success
startDMAction.givenExecuteResult(startDMSuccessResult)
awaitItem().also { state ->
assertThat(state.startDmAction).isEqualTo(Async.Uninitialized)
state.eventSink(CreateRoomRootEvents.StartDM(matrixUser))
}
assertThat(awaitItem().startDmAction).isInstanceOf(Async.Loading::class.java)
val stateAfterSecondAttempt = awaitItem()
assertThat(stateAfterSecondAttempt.startDmAction).isInstanceOf(Async.Failure::class.java)
assertThat(fakeAnalyticsService.capturedEvents.filterIsInstance<CreatedRoom>()).isEmpty()
awaitItem().also { state ->
assertThat(state.startDmAction).isEqualTo(startDMSuccessResult)
}
// Retry with success
fakeMatrixClient.givenCreateDmError(null)
stateAfterSecondAttempt.eventSink(CreateRoomRootEvents.StartDM(matrixUser))
assertThat(awaitItem().startDmAction).isInstanceOf(Async.Loading::class.java)
val stateAfterRetryStartDM = awaitItem()
assertThat(stateAfterRetryStartDM.startDmAction).isInstanceOf(Async.Success::class.java)
assertThat(stateAfterRetryStartDM.startDmAction.dataOrNull()).isEqualTo(createDmResult.getOrNull())
}
}
private fun createCreateRoomRootPresenter(
startDMAction: StartDMAction = FakeStartDMAction(),
): CreateRoomRootPresenter {
return CreateRoomRootPresenter(
presenterFactory = FakeUserListPresenterFactory(FakeUserListPresenter()),
userRepository = FakeUserRepository(),
userListDataStore = UserListDataStore(),
startDMAction = startDMAction,
buildMeta = aBuildMeta(),
)
}
}

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
plugins {
id("io.element.android-compose-library")
}
android {
namespace = "io.element.android.features.createroom.test"
}
dependencies {
implementation(libs.coroutines.core)
implementation(projects.libraries.matrix.api)
implementation(projects.libraries.matrix.test)
implementation(projects.libraries.architecture)
api(projects.features.createroom.api)
}

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.createroom.test
import androidx.compose.runtime.MutableState
import io.element.android.features.createroom.api.StartDMAction
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.test.A_ROOM_ID
import kotlinx.coroutines.delay
class FakeStartDMAction : StartDMAction {
private var executeResult: Async<RoomId> = Async.Success(A_ROOM_ID)
fun givenExecuteResult(result: Async<RoomId>) {
executeResult = result
}
override suspend fun execute(userId: UserId, actionState: MutableState<Async<RoomId>>) {
actionState.value = Async.Loading()
delay(1)
actionState.value = executeResult
}
}

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_migration_message">"Ez egy egyszeri folyamat, köszönjük a türelmét."</string>
<string name="screen_migration_title">"A fiók beállítása."</string>
<string name="screen_notification_optin_subtitle">"A beállításokat később is módosíthatja."</string>
<string name="screen_notification_optin_title">"Értesítések engedélyezése, hogy soha ne maradjon le egyetlen üzenetről sem"</string>
<string name="screen_welcome_bullet_1">"A hívások, szavazások, keresések és egyebek az év további részében kerülnek hozzáadásra."</string>
<string name="screen_welcome_bullet_2">"A titkosított szobák üzenetelőzményei nem lesznek elérhetők ebben a frissítésben."</string>
<string name="screen_welcome_bullet_3">"Szeretnénk hallani a véleményét, ossza meg velünk a beállítások oldalon."</string>
<string name="screen_welcome_button">"Lássunk neki!"</string>
<string name="screen_welcome_subtitle">"A következőket kell tudnia:"</string>
<string name="screen_welcome_title">"Üdvözli az %1$s!"</string>
</resources>

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_migration_message">"Ini adalah proses satu kali, terima kasih telah menunggu."</string>
<string name="screen_migration_title">"Menyiapkan akun Anda."</string>
<string name="screen_notification_optin_subtitle">"Anda dapat mengubah pengaturan Anda nanti."</string>
<string name="screen_notification_optin_title">"Izinkan pemberitahuan dan jangan pernah melewatkan pesan"</string>
<string name="screen_welcome_bullet_1">"Panggilan, pemungutan suara, pencarian, dan lainnya akan ditambahkan di tahun ini."</string>
<string name="screen_welcome_bullet_2">"Riwayat pesan untuk ruangan terenkripsi tidak akan tersedia dalam pembaruan ini."</string>
<string name="screen_welcome_bullet_3">"Kami ingin mendengar dari Anda, beri tahu kami pendapat Anda melalui halaman pengaturan."</string>
<string name="screen_welcome_button">"Ayo!"</string>
<string name="screen_welcome_subtitle">"Berikut adalah yang perlu Anda ketahui:"</string>
<string name="screen_welcome_title">"Selamat datang di %1$s!"</string>
</resources>

View file

@ -20,7 +20,7 @@ import android.os.Build
import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.permissions.api.PermissionStateProvider
import io.element.android.libraries.permissions.api.PermissionsPresenter
import io.element.android.libraries.permissions.impl.FakePermissionStateProvider
@ -52,7 +52,7 @@ class NotificationsOptInPresenterTests {
presenter.present()
}.test {
val initialState = awaitItem()
Truth.assertThat(initialState.notificationsPermissionState.showDialog).isFalse()
assertThat(initialState.notificationsPermissionState.showDialog).isFalse()
}
}
@ -65,7 +65,7 @@ class NotificationsOptInPresenterTests {
}.test {
val initialState = awaitItem()
initialState.eventSink(NotificationsOptInEvents.ContinueClicked)
Truth.assertThat(awaitItem().notificationsPermissionState.showDialog).isTrue()
assertThat(awaitItem().notificationsPermissionState.showDialog).isTrue()
}
}
@ -80,7 +80,7 @@ class NotificationsOptInPresenterTests {
}.test {
val initialState = awaitItem()
initialState.eventSink(NotificationsOptInEvents.ContinueClicked)
Truth.assertThat(isFinished).isTrue()
assertThat(isFinished).isTrue()
}
}
@ -96,7 +96,7 @@ class NotificationsOptInPresenterTests {
}.test {
val initialState = awaitItem()
initialState.eventSink(NotificationsOptInEvents.NotNowClicked)
Truth.assertThat(isFinished).isTrue()
assertThat(isFinished).isTrue()
}
}
@ -122,7 +122,7 @@ class NotificationsOptInPresenterTests {
val isPermissionDenied = runBlocking {
permissionStateProvider.isPermissionDenied("notifications").first()
}
Truth.assertThat(isPermissionDenied).isTrue()
assertThat(isPermissionDenied).isTrue()
}
}

View file

@ -33,7 +33,7 @@ data class InviteListInviteSummary(
val isNew: Boolean = false,
)
data class InviteSender constructor(
data class InviteSender(
val userId: UserId,
val displayName: String,
val avatarData: AvatarData = AvatarData(userId.value, displayName, size = AvatarSize.InviteSender),

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_invites_decline_chat_message">"Biztos, hogy elutasítja a meghívást, hogy csatlakozzon ehhez: %1$s?"</string>
<string name="screen_invites_decline_chat_title">"Meghívás elutasítása"</string>
<string name="screen_invites_decline_direct_chat_message">"Biztos, hogy elutasítja ezt a privát csevegést vele: %1$s?"</string>
<string name="screen_invites_decline_direct_chat_title">"Csevegés elutasítása"</string>
<string name="screen_invites_empty_list">"Nincsenek meghívások"</string>
<string name="screen_invites_invited_you">"%1$s (%2$s) meghívta"</string>
</resources>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_invites_decline_chat_message">"Apakah Anda yakin ingin menolak undangan untuk bergabung ke %1$s?"</string>
<string name="screen_invites_decline_chat_title">"Tolak undangan"</string>
<string name="screen_invites_decline_direct_chat_message">"Apakah Anda yakin ingin menolak obrolan pribadi dengan %1$s?"</string>
<string name="screen_invites_decline_direct_chat_title">"Tolak obrolan"</string>
<string name="screen_invites_empty_list">"Tidak ada undangan"</string>
<string name="screen_invites_invited_you">"%1$s (%2$s) mengundang Anda"</string>
</resources>

View file

@ -19,7 +19,7 @@ package io.element.android.features.invitelist.impl
import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth
import com.google.common.truth.Truth.assertThat
import io.element.android.features.invitelist.api.SeenInvitesStore
import io.element.android.features.invitelist.test.FakeSeenInvitesStore
import io.element.android.libraries.architecture.Async
@ -63,14 +63,14 @@ class InviteListPresenterTests {
presenter.present()
}.test {
val initialState = awaitItem()
Truth.assertThat(initialState.inviteList).isEmpty()
assertThat(initialState.inviteList).isEmpty()
roomListService.postInviteRooms(listOf(aRoomSummary()))
val withInviteState = awaitItem()
Truth.assertThat(withInviteState.inviteList.size).isEqualTo(1)
Truth.assertThat(withInviteState.inviteList[0].roomId).isEqualTo(A_ROOM_ID)
Truth.assertThat(withInviteState.inviteList[0].roomName).isEqualTo(A_ROOM_NAME)
assertThat(withInviteState.inviteList.size).isEqualTo(1)
assertThat(withInviteState.inviteList[0].roomId).isEqualTo(A_ROOM_ID)
assertThat(withInviteState.inviteList[0].roomName).isEqualTo(A_ROOM_NAME)
}
}
@ -84,11 +84,11 @@ class InviteListPresenterTests {
presenter.present()
}.test {
val withInviteState = awaitItem()
Truth.assertThat(withInviteState.inviteList.size).isEqualTo(1)
Truth.assertThat(withInviteState.inviteList[0].roomId).isEqualTo(A_ROOM_ID)
Truth.assertThat(withInviteState.inviteList[0].roomAlias).isEqualTo(A_USER_ID.value)
Truth.assertThat(withInviteState.inviteList[0].roomName).isEqualTo(A_ROOM_NAME)
Truth.assertThat(withInviteState.inviteList[0].roomAvatarData).isEqualTo(
assertThat(withInviteState.inviteList.size).isEqualTo(1)
assertThat(withInviteState.inviteList[0].roomId).isEqualTo(A_ROOM_ID)
assertThat(withInviteState.inviteList[0].roomAlias).isEqualTo(A_USER_ID.value)
assertThat(withInviteState.inviteList[0].roomName).isEqualTo(A_ROOM_NAME)
assertThat(withInviteState.inviteList[0].roomAvatarData).isEqualTo(
AvatarData(
id = A_USER_ID.value,
name = A_USER_NAME,
@ -96,7 +96,7 @@ class InviteListPresenterTests {
size = AvatarSize.RoomInviteItem,
)
)
Truth.assertThat(withInviteState.inviteList[0].sender).isNull()
assertThat(withInviteState.inviteList[0].sender).isNull()
}
}
@ -110,10 +110,10 @@ class InviteListPresenterTests {
presenter.present()
}.test {
val withInviteState = awaitItem()
Truth.assertThat(withInviteState.inviteList.size).isEqualTo(1)
Truth.assertThat(withInviteState.inviteList[0].sender?.displayName).isEqualTo(A_USER_NAME)
Truth.assertThat(withInviteState.inviteList[0].sender?.userId).isEqualTo(A_USER_ID)
Truth.assertThat(withInviteState.inviteList[0].sender?.avatarData).isEqualTo(
assertThat(withInviteState.inviteList.size).isEqualTo(1)
assertThat(withInviteState.inviteList[0].sender?.displayName).isEqualTo(A_USER_NAME)
assertThat(withInviteState.inviteList[0].sender?.userId).isEqualTo(A_USER_ID)
assertThat(withInviteState.inviteList[0].sender?.avatarData).isEqualTo(
AvatarData(
id = A_USER_ID.value,
name = A_USER_NAME,
@ -142,11 +142,11 @@ class InviteListPresenterTests {
originalState.eventSink(InviteListEvents.DeclineInvite(originalState.inviteList[0]))
val newState = awaitItem()
Truth.assertThat(newState.declineConfirmationDialog).isInstanceOf(InviteDeclineConfirmationDialog.Visible::class.java)
assertThat(newState.declineConfirmationDialog).isInstanceOf(InviteDeclineConfirmationDialog.Visible::class.java)
val confirmDialog = newState.declineConfirmationDialog as InviteDeclineConfirmationDialog.Visible
Truth.assertThat(confirmDialog.isDirect).isTrue()
Truth.assertThat(confirmDialog.name).isEqualTo(A_ROOM_NAME)
assertThat(confirmDialog.isDirect).isTrue()
assertThat(confirmDialog.name).isEqualTo(A_ROOM_NAME)
}
}
@ -163,11 +163,11 @@ class InviteListPresenterTests {
originalState.eventSink(InviteListEvents.DeclineInvite(originalState.inviteList[0]))
val newState = awaitItem()
Truth.assertThat(newState.declineConfirmationDialog).isInstanceOf(InviteDeclineConfirmationDialog.Visible::class.java)
assertThat(newState.declineConfirmationDialog).isInstanceOf(InviteDeclineConfirmationDialog.Visible::class.java)
val confirmDialog = newState.declineConfirmationDialog as InviteDeclineConfirmationDialog.Visible
Truth.assertThat(confirmDialog.isDirect).isFalse()
Truth.assertThat(confirmDialog.name).isEqualTo(A_ROOM_NAME)
assertThat(confirmDialog.isDirect).isFalse()
assertThat(confirmDialog.name).isEqualTo(A_ROOM_NAME)
}
}
@ -188,7 +188,7 @@ class InviteListPresenterTests {
originalState.eventSink(InviteListEvents.CancelDeclineInvite)
val newState = awaitItem()
Truth.assertThat(newState.declineConfirmationDialog).isInstanceOf(InviteDeclineConfirmationDialog.Hidden::class.java)
assertThat(newState.declineConfirmationDialog).isInstanceOf(InviteDeclineConfirmationDialog.Hidden::class.java)
}
}
@ -215,7 +215,7 @@ class InviteListPresenterTests {
skipItems(2)
Truth.assertThat(fakeNotificationDrawerManager.getClearMembershipNotificationForRoomCount(client.sessionId, A_ROOM_ID)).isEqualTo(1)
assertThat(fakeNotificationDrawerManager.getClearMembershipNotificationForRoomCount(client.sessionId, A_ROOM_ID)).isEqualTo(1)
}
}
@ -245,7 +245,7 @@ class InviteListPresenterTests {
val newState = awaitItem()
Truth.assertThat(newState.declinedAction).isEqualTo(Async.Failure<Unit>(ex))
assertThat(newState.declinedAction).isEqualTo(Async.Failure<Unit>(ex))
}
}
@ -277,7 +277,7 @@ class InviteListPresenterTests {
val newState = awaitItem()
Truth.assertThat(newState.declinedAction).isEqualTo(Async.Uninitialized)
assertThat(newState.declinedAction).isEqualTo(Async.Uninitialized)
}
}
@ -300,8 +300,8 @@ class InviteListPresenterTests {
val newState = awaitItem()
Truth.assertThat(newState.acceptedAction).isEqualTo(Async.Success(A_ROOM_ID))
Truth.assertThat(fakeNotificationDrawerManager.getClearMembershipNotificationForRoomCount(client.sessionId, A_ROOM_ID)).isEqualTo(1)
assertThat(newState.acceptedAction).isEqualTo(Async.Success(A_ROOM_ID))
assertThat(fakeNotificationDrawerManager.getClearMembershipNotificationForRoomCount(client.sessionId, A_ROOM_ID)).isEqualTo(1)
}
}
@ -323,7 +323,7 @@ class InviteListPresenterTests {
val originalState = awaitItem()
originalState.eventSink(InviteListEvents.AcceptInvite(originalState.inviteList[0]))
Truth.assertThat(awaitItem().acceptedAction).isEqualTo(Async.Failure<RoomId>(ex))
assertThat(awaitItem().acceptedAction).isEqualTo(Async.Failure<RoomId>(ex))
}
}
@ -350,7 +350,7 @@ class InviteListPresenterTests {
originalState.eventSink(InviteListEvents.DismissAcceptError)
val newState = awaitItem()
Truth.assertThat(newState.acceptedAction).isEqualTo(Async.Uninitialized)
assertThat(newState.acceptedAction).isEqualTo(Async.Uninitialized)
}
}
@ -375,19 +375,19 @@ class InviteListPresenterTests {
roomListService.postInviteRooms(listOf(aRoomSummary()))
awaitItem()
Truth.assertThat(store.getProvidedRoomIds()).isEqualTo(setOf(A_ROOM_ID))
assertThat(store.getProvidedRoomIds()).isEqualTo(setOf(A_ROOM_ID))
// When a second is added, both are saved
roomListService.postInviteRooms(listOf(aRoomSummary(), aRoomSummary(A_ROOM_ID_2)))
awaitItem()
Truth.assertThat(store.getProvidedRoomIds()).isEqualTo(setOf(A_ROOM_ID, A_ROOM_ID_2))
assertThat(store.getProvidedRoomIds()).isEqualTo(setOf(A_ROOM_ID, A_ROOM_ID_2))
// When they're both dismissed, an empty set is saved
roomListService.postInviteRooms(listOf())
awaitItem()
Truth.assertThat(store.getProvidedRoomIds()).isEmpty()
assertThat(store.getProvidedRoomIds()).isEmpty()
}
}
@ -413,11 +413,11 @@ class InviteListPresenterTests {
skipItems(1)
val withInviteState = awaitItem()
Truth.assertThat(withInviteState.inviteList.size).isEqualTo(2)
Truth.assertThat(withInviteState.inviteList[0].roomId).isEqualTo(A_ROOM_ID)
Truth.assertThat(withInviteState.inviteList[0].isNew).isFalse()
Truth.assertThat(withInviteState.inviteList[1].roomId).isEqualTo(A_ROOM_ID_2)
Truth.assertThat(withInviteState.inviteList[1].isNew).isTrue()
assertThat(withInviteState.inviteList.size).isEqualTo(2)
assertThat(withInviteState.inviteList[0].roomId).isEqualTo(A_ROOM_ID)
assertThat(withInviteState.inviteList[0].isNew).isFalse()
assertThat(withInviteState.inviteList[1].roomId).isEqualTo(A_ROOM_ID_2)
assertThat(withInviteState.inviteList[1].isNew).isTrue()
}
}

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="leave_room_alert_empty_subtitle">"Biztos, hogy elhagyja ezt a szobát? Ön az egyedüli ember itt. Ha kilép, akkor senki sem fog tudni csatlakozni a jövőben, Önt is beleértve."</string>
<string name="leave_room_alert_private_subtitle">"Biztos, hogy elhagyja ezt a szobát? Ez a szoba nem nyilvános, és meghívó nélkül nem fog tudni újra belépni."</string>
<string name="leave_room_alert_subtitle">"Biztos, hogy elhagyja a szobát?"</string>
</resources>

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="leave_room_alert_empty_subtitle">"Apakah Anda yakin ingin meninggalkan ruangan ini? Anda adalah orang satu-satunya di sini. Jika Anda pergi, tidak akan ada yang bisa bergabung di masa depan, termasuk Anda."</string>
<string name="leave_room_alert_private_subtitle">"Apakah Anda yakin ingin meninggalkan ruangan ini? Ruangan ini tidak umum dan Anda tidak akan dapat bergabung kembali tanpa undangan."</string>
<string name="leave_room_alert_subtitle">"Apakah Anda yakin ingin meninggalkan ruangan?"</string>
</resources>

View file

@ -19,7 +19,7 @@ package io.element.android.features.location.impl.send
import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth
import com.google.common.truth.Truth.assertThat
import im.vector.app.features.analytics.plan.Composer
import io.element.android.features.location.api.Location
import io.element.android.features.location.impl.aPermissionsState
@ -77,16 +77,16 @@ class SendLocationPresenterTest {
}.test {
val initialState = awaitItem()
Truth.assertThat(initialState.permissionDialog).isEqualTo(SendLocationState.Dialog.None)
Truth.assertThat(initialState.mode).isEqualTo(SendLocationState.Mode.SenderLocation)
Truth.assertThat(initialState.hasLocationPermission).isEqualTo(true)
assertThat(initialState.permissionDialog).isEqualTo(SendLocationState.Dialog.None)
assertThat(initialState.mode).isEqualTo(SendLocationState.Mode.SenderLocation)
assertThat(initialState.hasLocationPermission).isTrue()
// Swipe the map to switch mode
initialState.eventSink(SendLocationEvents.SwitchToPinLocationMode)
val myLocationState = awaitItem()
Truth.assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.None)
Truth.assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
Truth.assertThat(myLocationState.hasLocationPermission).isEqualTo(true)
assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.None)
assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
assertThat(myLocationState.hasLocationPermission).isTrue()
}
}
@ -104,16 +104,16 @@ class SendLocationPresenterTest {
}.test {
val initialState = awaitItem()
Truth.assertThat(initialState.permissionDialog).isEqualTo(SendLocationState.Dialog.None)
Truth.assertThat(initialState.mode).isEqualTo(SendLocationState.Mode.SenderLocation)
Truth.assertThat(initialState.hasLocationPermission).isEqualTo(true)
assertThat(initialState.permissionDialog).isEqualTo(SendLocationState.Dialog.None)
assertThat(initialState.mode).isEqualTo(SendLocationState.Mode.SenderLocation)
assertThat(initialState.hasLocationPermission).isTrue()
// Swipe the map to switch mode
initialState.eventSink(SendLocationEvents.SwitchToPinLocationMode)
val myLocationState = awaitItem()
Truth.assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.None)
Truth.assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
Truth.assertThat(myLocationState.hasLocationPermission).isEqualTo(true)
assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.None)
assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
assertThat(myLocationState.hasLocationPermission).isTrue()
}
}
@ -130,16 +130,16 @@ class SendLocationPresenterTest {
sendLocationPresenter.present()
}.test {
val initialState = awaitItem()
Truth.assertThat(initialState.permissionDialog).isEqualTo(SendLocationState.Dialog.None)
Truth.assertThat(initialState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
Truth.assertThat(initialState.hasLocationPermission).isEqualTo(false)
assertThat(initialState.permissionDialog).isEqualTo(SendLocationState.Dialog.None)
assertThat(initialState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
assertThat(initialState.hasLocationPermission).isFalse()
// Click on the button to switch mode
initialState.eventSink(SendLocationEvents.SwitchToMyLocationMode)
val myLocationState = awaitItem()
Truth.assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.PermissionDenied)
Truth.assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
Truth.assertThat(myLocationState.hasLocationPermission).isEqualTo(false)
assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.PermissionDenied)
assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
assertThat(myLocationState.hasLocationPermission).isFalse()
}
}
@ -156,16 +156,16 @@ class SendLocationPresenterTest {
sendLocationPresenter.present()
}.test {
val initialState = awaitItem()
Truth.assertThat(initialState.permissionDialog).isEqualTo(SendLocationState.Dialog.None)
Truth.assertThat(initialState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
Truth.assertThat(initialState.hasLocationPermission).isEqualTo(false)
assertThat(initialState.permissionDialog).isEqualTo(SendLocationState.Dialog.None)
assertThat(initialState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
assertThat(initialState.hasLocationPermission).isFalse()
// Click on the button to switch mode
initialState.eventSink(SendLocationEvents.SwitchToMyLocationMode)
val myLocationState = awaitItem()
Truth.assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.PermissionRationale)
Truth.assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
Truth.assertThat(myLocationState.hasLocationPermission).isEqualTo(false)
assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.PermissionRationale)
assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
assertThat(myLocationState.hasLocationPermission).isFalse()
}
}
@ -187,16 +187,16 @@ class SendLocationPresenterTest {
// Click on the button to switch mode
initialState.eventSink(SendLocationEvents.SwitchToMyLocationMode)
val myLocationState = awaitItem()
Truth.assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.PermissionRationale)
Truth.assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
Truth.assertThat(myLocationState.hasLocationPermission).isEqualTo(false)
assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.PermissionRationale)
assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
assertThat(myLocationState.hasLocationPermission).isFalse()
// Dismiss the dialog
myLocationState.eventSink(SendLocationEvents.DismissDialog)
val dialogDismissedState = awaitItem()
Truth.assertThat(dialogDismissedState.permissionDialog).isEqualTo(SendLocationState.Dialog.None)
Truth.assertThat(dialogDismissedState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
Truth.assertThat(dialogDismissedState.hasLocationPermission).isEqualTo(false)
assertThat(dialogDismissedState.permissionDialog).isEqualTo(SendLocationState.Dialog.None)
assertThat(dialogDismissedState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
assertThat(dialogDismissedState.hasLocationPermission).isFalse()
}
}
@ -218,13 +218,13 @@ class SendLocationPresenterTest {
// Click on the button to switch mode
initialState.eventSink(SendLocationEvents.SwitchToMyLocationMode)
val myLocationState = awaitItem()
Truth.assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.PermissionRationale)
Truth.assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
Truth.assertThat(myLocationState.hasLocationPermission).isEqualTo(false)
assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.PermissionRationale)
assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
assertThat(myLocationState.hasLocationPermission).isFalse()
// Continue the dialog sends permission request to the permissions presenter
myLocationState.eventSink(SendLocationEvents.RequestPermissions)
Truth.assertThat(permissionsPresenterFake.events.last()).isEqualTo(PermissionsEvents.RequestPermissions)
assertThat(permissionsPresenterFake.events.last()).isEqualTo(PermissionsEvents.RequestPermissions)
}
}
@ -246,16 +246,16 @@ class SendLocationPresenterTest {
// Click on the button to switch mode
initialState.eventSink(SendLocationEvents.SwitchToMyLocationMode)
val myLocationState = awaitItem()
Truth.assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.PermissionDenied)
Truth.assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
Truth.assertThat(myLocationState.hasLocationPermission).isEqualTo(false)
assertThat(myLocationState.permissionDialog).isEqualTo(SendLocationState.Dialog.PermissionDenied)
assertThat(myLocationState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
assertThat(myLocationState.hasLocationPermission).isFalse()
// Dismiss the dialog
myLocationState.eventSink(SendLocationEvents.DismissDialog)
val dialogDismissedState = awaitItem()
Truth.assertThat(dialogDismissedState.permissionDialog).isEqualTo(SendLocationState.Dialog.None)
Truth.assertThat(dialogDismissedState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
Truth.assertThat(dialogDismissedState.hasLocationPermission).isEqualTo(false)
assertThat(dialogDismissedState.permissionDialog).isEqualTo(SendLocationState.Dialog.None)
assertThat(dialogDismissedState.mode).isEqualTo(SendLocationState.Mode.PinLocation)
assertThat(dialogDismissedState.hasLocationPermission).isFalse()
}
}
@ -292,8 +292,8 @@ class SendLocationPresenterTest {
delay(1) // Wait for the coroutine to finish
Truth.assertThat(fakeMatrixRoom.sentLocations.size).isEqualTo(1)
Truth.assertThat(fakeMatrixRoom.sentLocations.last()).isEqualTo(
assertThat(fakeMatrixRoom.sentLocations.size).isEqualTo(1)
assertThat(fakeMatrixRoom.sentLocations.last()).isEqualTo(
SendLocationInvocation(
body = "Location was shared at geo:3.0,4.0;u=5.0",
geoUri = "geo:3.0,4.0;u=5.0",
@ -303,8 +303,8 @@ class SendLocationPresenterTest {
)
)
Truth.assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(1)
Truth.assertThat(fakeAnalyticsService.capturedEvents.last()).isEqualTo(
assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(1)
assertThat(fakeAnalyticsService.capturedEvents.last()).isEqualTo(
Composer(
inThread = false,
isEditing = false,
@ -348,8 +348,8 @@ class SendLocationPresenterTest {
delay(1) // Wait for the coroutine to finish
Truth.assertThat(fakeMatrixRoom.sentLocations.size).isEqualTo(1)
Truth.assertThat(fakeMatrixRoom.sentLocations.last()).isEqualTo(
assertThat(fakeMatrixRoom.sentLocations.size).isEqualTo(1)
assertThat(fakeMatrixRoom.sentLocations.last()).isEqualTo(
SendLocationInvocation(
body = "Location was shared at geo:0.0,1.0",
geoUri = "geo:0.0,1.0",
@ -359,8 +359,8 @@ class SendLocationPresenterTest {
)
)
Truth.assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(1)
Truth.assertThat(fakeAnalyticsService.capturedEvents.last()).isEqualTo(
assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(1)
assertThat(fakeAnalyticsService.capturedEvents.last()).isEqualTo(
Composer(
inThread = false,
isEditing = false,
@ -405,8 +405,8 @@ class SendLocationPresenterTest {
delay(1) // Wait for the coroutine to finish
Truth.assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(1)
Truth.assertThat(fakeAnalyticsService.capturedEvents.last()).isEqualTo(
assertThat(fakeAnalyticsService.capturedEvents.size).isEqualTo(1)
assertThat(fakeAnalyticsService.capturedEvents.last()).isEqualTo(
Composer(
inThread = false,
isEditing = true,
@ -444,8 +444,8 @@ class SendLocationPresenterTest {
dialogShownState.eventSink(SendLocationEvents.OpenAppSettings)
val settingsOpenedState = awaitItem()
Truth.assertThat(settingsOpenedState.permissionDialog).isEqualTo(SendLocationState.Dialog.None)
Truth.assertThat(fakeLocationActions.openSettingsInvocationsCount).isEqualTo(1)
assertThat(settingsOpenedState.permissionDialog).isEqualTo(SendLocationState.Dialog.None)
assertThat(fakeLocationActions.openSettingsInvocationsCount).isEqualTo(1)
}
}
@ -455,7 +455,7 @@ class SendLocationPresenterTest {
sendLocationPresenter.present()
}.test {
val initialState = awaitItem()
Truth.assertThat(initialState.appName).isEqualTo("app name")
assertThat(initialState.appName).isEqualTo("app name")
}
}
}

View file

@ -19,7 +19,7 @@ package io.element.android.features.location.impl.show
import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth
import com.google.common.truth.Truth.assertThat
import io.element.android.features.location.api.Location
import io.element.android.features.location.impl.aPermissionsState
import io.element.android.features.location.impl.common.actions.FakeLocationActions
@ -66,10 +66,10 @@ class ShowLocationPresenterTest {
presenter.present()
}.test {
val initialState = awaitItem()
Truth.assertThat(initialState.location).isEqualTo(location)
Truth.assertThat(initialState.description).isEqualTo(A_DESCRIPTION)
Truth.assertThat(initialState.hasLocationPermission).isEqualTo(false)
Truth.assertThat(initialState.isTrackMyLocation).isEqualTo(false)
assertThat(initialState.location).isEqualTo(location)
assertThat(initialState.description).isEqualTo(A_DESCRIPTION)
assertThat(initialState.hasLocationPermission).isFalse()
assertThat(initialState.isTrackMyLocation).isFalse()
}
}
@ -86,10 +86,10 @@ class ShowLocationPresenterTest {
presenter.present()
}.test {
val initialState = awaitItem()
Truth.assertThat(initialState.location).isEqualTo(location)
Truth.assertThat(initialState.description).isEqualTo(A_DESCRIPTION)
Truth.assertThat(initialState.hasLocationPermission).isEqualTo(false)
Truth.assertThat(initialState.isTrackMyLocation).isEqualTo(false)
assertThat(initialState.location).isEqualTo(location)
assertThat(initialState.description).isEqualTo(A_DESCRIPTION)
assertThat(initialState.hasLocationPermission).isFalse()
assertThat(initialState.isTrackMyLocation).isFalse()
}
}
@ -101,10 +101,10 @@ class ShowLocationPresenterTest {
presenter.present()
}.test {
val initialState = awaitItem()
Truth.assertThat(initialState.location).isEqualTo(location)
Truth.assertThat(initialState.description).isEqualTo(A_DESCRIPTION)
Truth.assertThat(initialState.hasLocationPermission).isEqualTo(true)
Truth.assertThat(initialState.isTrackMyLocation).isEqualTo(false)
assertThat(initialState.location).isEqualTo(location)
assertThat(initialState.description).isEqualTo(A_DESCRIPTION)
assertThat(initialState.hasLocationPermission).isTrue()
assertThat(initialState.isTrackMyLocation).isFalse()
}
}
@ -116,10 +116,10 @@ class ShowLocationPresenterTest {
presenter.present()
}.test {
val initialState = awaitItem()
Truth.assertThat(initialState.location).isEqualTo(location)
Truth.assertThat(initialState.description).isEqualTo(A_DESCRIPTION)
Truth.assertThat(initialState.hasLocationPermission).isEqualTo(true)
Truth.assertThat(initialState.isTrackMyLocation).isEqualTo(false)
assertThat(initialState.location).isEqualTo(location)
assertThat(initialState.description).isEqualTo(A_DESCRIPTION)
assertThat(initialState.hasLocationPermission).isTrue()
assertThat(initialState.isTrackMyLocation).isFalse()
}
}
@ -131,8 +131,8 @@ class ShowLocationPresenterTest {
val initialState = awaitItem()
initialState.eventSink(ShowLocationEvents.Share)
Truth.assertThat(fakeLocationActions.sharedLocation).isEqualTo(location)
Truth.assertThat(fakeLocationActions.sharedLabel).isEqualTo(A_DESCRIPTION)
assertThat(fakeLocationActions.sharedLocation).isEqualTo(location)
assertThat(fakeLocationActions.sharedLabel).isEqualTo(A_DESCRIPTION)
}
}
@ -144,23 +144,23 @@ class ShowLocationPresenterTest {
presenter.present()
}.test {
val initialState = awaitItem()
Truth.assertThat(initialState.hasLocationPermission).isEqualTo(true)
Truth.assertThat(initialState.isTrackMyLocation).isEqualTo(false)
assertThat(initialState.hasLocationPermission).isTrue()
assertThat(initialState.isTrackMyLocation).isFalse()
initialState.eventSink(ShowLocationEvents.TrackMyLocation(true))
val trackMyLocationState = awaitItem()
delay(1)
Truth.assertThat(trackMyLocationState.hasLocationPermission).isEqualTo(true)
Truth.assertThat(trackMyLocationState.isTrackMyLocation).isEqualTo(true)
assertThat(trackMyLocationState.hasLocationPermission).isTrue()
assertThat(trackMyLocationState.isTrackMyLocation).isTrue()
// Swipe the map to switch mode
initialState.eventSink(ShowLocationEvents.TrackMyLocation(false))
val trackLocationDisabledState = awaitItem()
Truth.assertThat(trackLocationDisabledState.permissionDialog).isEqualTo(ShowLocationState.Dialog.None)
Truth.assertThat(trackLocationDisabledState.isTrackMyLocation).isEqualTo(false)
Truth.assertThat(trackLocationDisabledState.hasLocationPermission).isEqualTo(true)
assertThat(trackLocationDisabledState.permissionDialog).isEqualTo(ShowLocationState.Dialog.None)
assertThat(trackLocationDisabledState.isTrackMyLocation).isFalse()
assertThat(trackLocationDisabledState.hasLocationPermission).isTrue()
}
}
@ -182,16 +182,16 @@ class ShowLocationPresenterTest {
// Click on the button to switch mode
initialState.eventSink(ShowLocationEvents.TrackMyLocation(true))
val trackLocationState = awaitItem()
Truth.assertThat(trackLocationState.permissionDialog).isEqualTo(ShowLocationState.Dialog.PermissionRationale)
Truth.assertThat(trackLocationState.isTrackMyLocation).isEqualTo(false)
Truth.assertThat(trackLocationState.hasLocationPermission).isEqualTo(false)
assertThat(trackLocationState.permissionDialog).isEqualTo(ShowLocationState.Dialog.PermissionRationale)
assertThat(trackLocationState.isTrackMyLocation).isFalse()
assertThat(trackLocationState.hasLocationPermission).isFalse()
// Dismiss the dialog
initialState.eventSink(ShowLocationEvents.DismissDialog)
val dialogDismissedState = awaitItem()
Truth.assertThat(dialogDismissedState.permissionDialog).isEqualTo(ShowLocationState.Dialog.None)
Truth.assertThat(dialogDismissedState.isTrackMyLocation).isEqualTo(false)
Truth.assertThat(dialogDismissedState.hasLocationPermission).isEqualTo(false)
assertThat(dialogDismissedState.permissionDialog).isEqualTo(ShowLocationState.Dialog.None)
assertThat(dialogDismissedState.isTrackMyLocation).isFalse()
assertThat(dialogDismissedState.hasLocationPermission).isFalse()
}
}
@ -213,13 +213,13 @@ class ShowLocationPresenterTest {
// Click on the button to switch mode
initialState.eventSink(ShowLocationEvents.TrackMyLocation(true))
val trackLocationState = awaitItem()
Truth.assertThat(trackLocationState.permissionDialog).isEqualTo(ShowLocationState.Dialog.PermissionRationale)
Truth.assertThat(trackLocationState.isTrackMyLocation).isEqualTo(false)
Truth.assertThat(trackLocationState.hasLocationPermission).isEqualTo(false)
assertThat(trackLocationState.permissionDialog).isEqualTo(ShowLocationState.Dialog.PermissionRationale)
assertThat(trackLocationState.isTrackMyLocation).isFalse()
assertThat(trackLocationState.hasLocationPermission).isFalse()
// Continue the dialog sends permission request to the permissions presenter
trackLocationState.eventSink(ShowLocationEvents.RequestPermissions)
Truth.assertThat(permissionsPresenterFake.events.last()).isEqualTo(PermissionsEvents.RequestPermissions)
assertThat(permissionsPresenterFake.events.last()).isEqualTo(PermissionsEvents.RequestPermissions)
}
}
@ -241,16 +241,16 @@ class ShowLocationPresenterTest {
// Click on the button to switch mode
initialState.eventSink(ShowLocationEvents.TrackMyLocation(true))
val trackLocationState = awaitItem()
Truth.assertThat(trackLocationState.permissionDialog).isEqualTo(ShowLocationState.Dialog.PermissionDenied)
Truth.assertThat(trackLocationState.isTrackMyLocation).isEqualTo(false)
Truth.assertThat(trackLocationState.hasLocationPermission).isEqualTo(false)
assertThat(trackLocationState.permissionDialog).isEqualTo(ShowLocationState.Dialog.PermissionDenied)
assertThat(trackLocationState.isTrackMyLocation).isFalse()
assertThat(trackLocationState.hasLocationPermission).isFalse()
// Dismiss the dialog
initialState.eventSink(ShowLocationEvents.DismissDialog)
val dialogDismissedState = awaitItem()
Truth.assertThat(dialogDismissedState.permissionDialog).isEqualTo(ShowLocationState.Dialog.None)
Truth.assertThat(dialogDismissedState.isTrackMyLocation).isEqualTo(false)
Truth.assertThat(dialogDismissedState.hasLocationPermission).isEqualTo(false)
assertThat(dialogDismissedState.permissionDialog).isEqualTo(ShowLocationState.Dialog.None)
assertThat(dialogDismissedState.isTrackMyLocation).isFalse()
assertThat(dialogDismissedState.hasLocationPermission).isFalse()
}
}
@ -276,8 +276,8 @@ class ShowLocationPresenterTest {
dialogShownState.eventSink(ShowLocationEvents.OpenAppSettings)
val settingsOpenedState = awaitItem()
Truth.assertThat(settingsOpenedState.permissionDialog).isEqualTo(ShowLocationState.Dialog.None)
Truth.assertThat(fakeLocationActions.openSettingsInvocationsCount).isEqualTo(1)
assertThat(settingsOpenedState.permissionDialog).isEqualTo(ShowLocationState.Dialog.None)
assertThat(fakeLocationActions.openSettingsInvocationsCount).isEqualTo(1)
}
}
@ -287,7 +287,7 @@ class ShowLocationPresenterTest {
presenter.present()
}.test {
val initialState = awaitItem()
Truth.assertThat(initialState.appName).isEqualTo("app name")
assertThat(initialState.appName).isEqualTo("app name")
}
}

View file

@ -1,4 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<plurals name="screen_app_lock_subtitle">
<item quantity="one">"Du hast %1$d Versuch zu entsperren"</item>
<item quantity="other">"Du hast %1$d Versuche zum Entsperren"</item>
</plurals>
<plurals name="screen_app_lock_subtitle_wrong_pin">
<item quantity="one">"Falsche PIN. Du hast %1$d weitere Chance"</item>
<item quantity="other">"Falsche PIN. Du hast %1$d weitere Chancen"</item>
</plurals>
<string name="screen_app_lock_biometric_authentication">"biometrische Authentifizierung"</string>
<string name="screen_app_lock_biometric_unlock">"biometrisches Entsperren"</string>
<string name="screen_app_lock_biometric_unlock_title_android">"Mit Biometrie entsperren"</string>
<string name="screen_app_lock_forgot_pin">"PIN vergessen?"</string>
<string name="screen_app_lock_settings_change_pin">"PIN-Code ändern"</string>
<string name="screen_app_lock_settings_enable_biometric_unlock">"Biometrisches Entsperren zulassen"</string>
<string name="screen_app_lock_settings_remove_pin">"Pin entfernen"</string>
<string name="screen_app_lock_settings_remove_pin_alert_message">"Bist du sicher, dass du die PIN entfernen willst?"</string>
<string name="screen_app_lock_settings_remove_pin_alert_title">"PIN entfernen?"</string>
<string name="screen_app_lock_setup_biometric_unlock_allow_title">"%1$s zulassen"</string>
<string name="screen_app_lock_setup_biometric_unlock_skip">"Ich verwende lieber die PIN"</string>
<string name="screen_app_lock_setup_biometric_unlock_subtitle">"Spare dir etwas Zeit und benutze %1$s, um die App jedes Mal zu entsperren"</string>
<string name="screen_app_lock_setup_choose_pin">"PIN wählen"</string>
<string name="screen_app_lock_setup_confirm_pin">"PIN bestätigen"</string>
<string name="screen_app_lock_setup_pin_blacklisted_dialog_content">"Aus Sicherheitsgründen kannst du das nicht als deinen PIN-Code wählen"</string>
<string name="screen_app_lock_setup_pin_blacklisted_dialog_title">"Wähle eine andere PIN"</string>
<string name="screen_app_lock_setup_pin_context">"Sperre %1$s, um deine Chats noch sicherer zu machen.
Wähle etwas Einprägsames. Wenn du diese PIN vergisst, wirst du aus der App ausgeloggt."</string>
<string name="screen_app_lock_setup_pin_mismatch_dialog_content">"Bitte gib die gleiche PIN zweimal ein"</string>
<string name="screen_app_lock_setup_pin_mismatch_dialog_title">"Die PINs stimmen nicht überein"</string>
<string name="screen_app_lock_signout_alert_message">"Um fortzufahren, musst du dich erneut anmelden und eine neue PIN erstellen"</string>
<string name="screen_app_lock_signout_alert_title">"Du wirst abgemeldet"</string>
<string name="screen_app_lock_use_biometric_android">"Biometrie verwenden"</string>
<string name="screen_app_lock_use_pin_android">"PIN verwenden"</string>
<string name="screen_signout_in_progress_dialog_content">"Abmelden…"</string>
</resources>

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<plurals name="screen_app_lock_subtitle">
<item quantity="one">"%1$d próbálkozása van a feloldáshoz"</item>
<item quantity="other">"%1$d próbálkozása van a feloldáshoz"</item>
</plurals>
<plurals name="screen_app_lock_subtitle_wrong_pin">
<item quantity="one">"Hibás PIN-kód. Még %1$d próbálkozási lehetősége maradt."</item>
<item quantity="other">"Hibás PIN-kód. Még %1$d próbálkozási lehetősége maradt."</item>
</plurals>
<string name="screen_app_lock_biometric_authentication">"biometrikus hitelesítés"</string>
<string name="screen_app_lock_biometric_unlock">"biometrikus feloldás"</string>
<string name="screen_app_lock_biometric_unlock_title_android">"Feloldás biometrikus adatokkal"</string>
<string name="screen_app_lock_forgot_pin">"Elfelejtette a PIN-kódot?"</string>
<string name="screen_app_lock_settings_change_pin">"PIN-kód módosítása"</string>
<string name="screen_app_lock_settings_enable_biometric_unlock">"Biometrikus feloldás engedélyezése"</string>
<string name="screen_app_lock_settings_remove_pin">"PIN-kód eltávolítása"</string>
<string name="screen_app_lock_settings_remove_pin_alert_message">"Biztos, hogy eltávolítja a PIN-kódot?"</string>
<string name="screen_app_lock_settings_remove_pin_alert_title">"PIN-kód eltávolítása?"</string>
<string name="screen_app_lock_setup_biometric_unlock_allow_title">"A %1$s engedélyezése"</string>
<string name="screen_app_lock_setup_biometric_unlock_skip">"Inkább PIN-kód használata"</string>
<string name="screen_app_lock_setup_biometric_unlock_subtitle">"Spóroljon meg némi időt, és használja a %1$st az alkalmazás feloldásához"</string>
<string name="screen_app_lock_setup_choose_pin">"PIN-kód kiválasztása"</string>
<string name="screen_app_lock_setup_confirm_pin">"PIN-kód megerősítése"</string>
<string name="screen_app_lock_setup_pin_blacklisted_dialog_content">"Ezt biztonsági okokból nem választhatja PIN-kódként"</string>
<string name="screen_app_lock_setup_pin_blacklisted_dialog_title">"Válasszon egy másik PIN-kódot"</string>
<string name="screen_app_lock_setup_pin_context">"Az %1$s zárolása a csevegései nagyobb biztonsága érdekében.
Válasszon valami megjegyezhetőt. Ha elfelejti a PIN-kódot, akkor ki lesz jelentkeztetve az alkalmazásból."</string>
<string name="screen_app_lock_setup_pin_mismatch_dialog_content">"Adja meg a PIN-kódját kétszer"</string>
<string name="screen_app_lock_setup_pin_mismatch_dialog_title">"A PIN-kódok nem egyeznek"</string>
<string name="screen_app_lock_signout_alert_message">"A folytatáshoz újra be kell jelentkeznie, és létre kell hoznia egy új PIN-kódot"</string>
<string name="screen_app_lock_signout_alert_title">"Kijelentkeztetésre kerül"</string>
<string name="screen_app_lock_use_biometric_android">"Biometrikus adatok használata"</string>
<string name="screen_app_lock_use_pin_android">"PIN-kód használata"</string>
<string name="screen_signout_in_progress_dialog_content">"Kijelentkezés…"</string>
</resources>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<plurals name="screen_app_lock_subtitle">
<item quantity="other">"Anda memiliki %1$d percobaan lagi untuk membuka kunci"</item>
</plurals>
<plurals name="screen_app_lock_subtitle_wrong_pin">
<item quantity="other">"PIN salah. Anda memiliki %1$d percobaan lagi"</item>
</plurals>
<string name="screen_app_lock_biometric_authentication">"autentikasi biometrik"</string>
<string name="screen_app_lock_biometric_unlock">"pembukaan biometrik"</string>
<string name="screen_app_lock_biometric_unlock_title_android">"Buka kunci dengan biometrik"</string>
<string name="screen_app_lock_forgot_pin">"Lupa PIN?"</string>
<string name="screen_app_lock_settings_change_pin">"Ubah kode PIN"</string>
<string name="screen_app_lock_settings_enable_biometric_unlock">"Perbolehkan pembukaan biometrik"</string>
<string name="screen_app_lock_settings_remove_pin">"Hapus PIN"</string>
<string name="screen_app_lock_settings_remove_pin_alert_message">"Apakah Anda yakin ingin menghapus PIN?"</string>
<string name="screen_app_lock_settings_remove_pin_alert_title">"Hapus PIN?"</string>
<string name="screen_app_lock_setup_biometric_unlock_allow_title">"Perbolehkan %1$s"</string>
<string name="screen_app_lock_setup_biometric_unlock_skip">"Saya lebih suka menggunakan PIN"</string>
<string name="screen_app_lock_setup_biometric_unlock_subtitle">"Hemat waktu Anda dan gunakan %1$s untuk membuka kunci aplikasi setiap kalinya"</string>
<string name="screen_app_lock_setup_choose_pin">"Pilih PIN"</string>
<string name="screen_app_lock_setup_confirm_pin">"Konfirmasi PIN"</string>
<string name="screen_app_lock_setup_pin_blacklisted_dialog_content">"Anda tidak dapat memilih PIN ini demi keamanan"</string>
<string name="screen_app_lock_setup_pin_blacklisted_dialog_title">"Pilih PIN yang lain"</string>
<string name="screen_app_lock_setup_pin_context">"Kunci %1$s untuk menambahkan keamanan tambahan pada percakapan Anda.
Pilih sesuatu yang mudah untuk diingat. Jika Anda lupa PIN ini, Anda akan dikeluarkan dari aplikasi."</string>
<string name="screen_app_lock_setup_pin_mismatch_dialog_content">"Silakan masukkan PIN yang sama dua kali"</string>
<string name="screen_app_lock_setup_pin_mismatch_dialog_title">"PIN tidak cocok"</string>
<string name="screen_app_lock_signout_alert_message">"Anda harus masuk ulang dan membuat PIN baru untuk melanjutkan"</string>
<string name="screen_app_lock_signout_alert_title">"Anda sedang dikeluarkan"</string>
<string name="screen_app_lock_use_biometric_android">"Gunakan biometrik"</string>
<string name="screen_app_lock_use_pin_android">"Gunakan PIN"</string>
<string name="screen_signout_in_progress_dialog_content">"Mengeluarkan dari akun…"</string>
</resources>

View file

@ -94,8 +94,8 @@ class PinUnlockPresenterTest {
}
awaitLastSequentialItem().also { state ->
assertThat(state.remainingAttempts.dataOrNull()).isEqualTo(0)
assertThat(state.showSignOutPrompt).isEqualTo(true)
assertThat(state.isSignOutPromptCancellable).isEqualTo(false)
assertThat(state.showSignOutPrompt).isTrue()
assertThat(state.isSignOutPromptCancellable).isFalse()
}
}
}
@ -112,16 +112,16 @@ class PinUnlockPresenterTest {
state.eventSink(PinUnlockEvents.OnForgetPin)
}
awaitLastSequentialItem().also { state ->
assertThat(state.showSignOutPrompt).isEqualTo(true)
assertThat(state.isSignOutPromptCancellable).isEqualTo(true)
assertThat(state.showSignOutPrompt).isTrue()
assertThat(state.isSignOutPromptCancellable).isTrue()
state.eventSink(PinUnlockEvents.ClearSignOutPrompt)
}
awaitLastSequentialItem().also { state ->
assertThat(state.showSignOutPrompt).isEqualTo(false)
assertThat(state.showSignOutPrompt).isFalse()
state.eventSink(PinUnlockEvents.OnForgetPin)
}
awaitLastSequentialItem().also { state ->
assertThat(state.showSignOutPrompt).isEqualTo(true)
assertThat(state.showSignOutPrompt).isTrue()
state.eventSink(PinUnlockEvents.SignOut)
}
consumeItemsUntilPredicate { state ->

View file

@ -16,7 +16,7 @@
package io.element.android.features.login.impl.resolver
data class HomeserverData constructor(
data class HomeserverData(
// The computed homeserver url, for which a wellknown file has been retrieved, or just a valid Url
val homeserverUrl: String,
// True if a wellknown file has been found and is valid. If false, it means that the [homeserverUrl] is valid

View file

@ -18,6 +18,7 @@
<string name="screen_change_server_form_header">"Homeserver-URL"</string>
<string name="screen_change_server_form_notice">"Du kannst nur eine Verbindung zu einem vorhandenen Server herstellen, der Sliding Sync unterstützt. Dein Homeserver-Administrator muss das konfigurieren. %1$s"</string>
<string name="screen_change_server_subtitle">"Wie lautet die Adresse deines Servers?"</string>
<string name="screen_change_server_title">"Wähle deinen Server aus"</string>
<string name="screen_login_error_deactivated_account">"Dieses Konto wurde deaktiviert."</string>
<string name="screen_login_error_invalid_credentials">"Falscher Benutzername und/oder Passwort"</string>
<string name="screen_login_error_invalid_user_id">"Dies ist keine gültige Benutzerkennung. Erwartetes Format: \'@user:homeserver.org\'"</string>

View file

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_account_provider_change">"Fiókszolgáltató módosítása"</string>
<string name="screen_account_provider_form_hint">"Matrix-kiszolgáló webcíme"</string>
<string name="screen_account_provider_form_notice">"Adjon meg egy keresési kifejezést vagy egy tartománycímet."</string>
<string name="screen_account_provider_form_subtitle">"Keresés egy cégre, közösségre vagy privát kiszolgálóra."</string>
<string name="screen_account_provider_form_title">"Fiókszolgáltató keresése"</string>
<string name="screen_account_provider_signin_subtitle">"Itt lesznek a beszélgetései ahogyan egy e-mail-szolgáltatást is használna a levelei kezeléséhez."</string>
<string name="screen_account_provider_signin_title">"Hamarosan bejelentkezik ide: %s"</string>
<string name="screen_account_provider_signup_subtitle">"Itt lesznek a beszélgetései ahogyan egy e-mail-szolgáltatást is használna a levelei kezeléséhez."</string>
<string name="screen_account_provider_signup_title">"Hamarosan létrehoz egy fiókot itt: %s"</string>
<string name="screen_change_account_provider_matrix_org_subtitle">"A Matrix.org egy nagy, ingyenes kiszolgáló a nyilvános Matrix-hálózaton, a biztonságos, decentralizált kommunikáció érdekében, amelyet a Matrix.org Alapítvány üzemeltet."</string>
<string name="screen_change_account_provider_other">"Egyéb"</string>
<string name="screen_change_account_provider_subtitle">"Másik fiókszolgáltató, például a saját privát kiszolgáló vagy egy munkahelyi fiók használata."</string>
<string name="screen_change_account_provider_title">"Fiókszolgáltató módosítása"</string>
<string name="screen_change_server_error_invalid_homeserver">"Nem sikerült elérni ezt a Matrix-kiszolgálót. Ellenőrizze, hogy helyesen adta-e meg a Matrix-kiszolgáló webcímét. Ha a webcím helyes, akkor további segítségért lépjen kapcsolatba a Matrix-kiszolgáló rendszergazdájával."</string>
<string name="screen_change_server_error_no_sliding_sync_message">"A kiszolgáló jelenleg nem támogatja a Sliding sync protokollt."</string>
<string name="screen_change_server_form_header">"Matrix-kiszolgáló webcíme"</string>
<string name="screen_change_server_form_notice">"Csak olyan meglévő kiszolgálóhoz csatlakozhat, amely támogatja a Sliding sync protokollt. Ezt a Matrix-kiszolgáló rendszergazdájának kell beállítania. %1$s"</string>
<string name="screen_change_server_subtitle">"Mi a kiszolgálója címe?"</string>
<string name="screen_change_server_title">"Válassza ki a kiszolgálóját"</string>
<string name="screen_login_error_deactivated_account">"Ez a fiók deaktiválva lett."</string>
<string name="screen_login_error_invalid_credentials">"Helytelen felhasználónév vagy jelszó"</string>
<string name="screen_login_error_invalid_user_id">"Ez nem érvényes felhasználóazonosító. A várt formátum: „@user:homeserver.org”"</string>
<string name="screen_login_error_unsupported_authentication">"A kiválasztott Matrix-kiszolgáló nem támogatja a jelszavas vagy OIDC-alapú bejelentkezést. Lépjen kapcsolatba a kiszolgáló rendszergazdájával, vagy válasszon másik Matrix-kiszolgálót."</string>
<string name="screen_login_form_header">"Adja meg adatait"</string>
<string name="screen_login_title">"Örülünk, hogy visszatért!"</string>
<string name="screen_login_title_with_homeserver">"Bejelentkezés ide: %1$s"</string>
<string name="screen_server_confirmation_change_server">"Fiókszolgáltató módosítása"</string>
<string name="screen_server_confirmation_message_login_element_dot_io">"Egy privát kiszolgáló az Element alkalmazottai számára."</string>
<string name="screen_server_confirmation_message_login_matrix_dot_org">"A Matrix egy nyitott hálózat a biztonságos, decentralizált kommunikációhoz."</string>
<string name="screen_server_confirmation_message_register">"Itt lesznek a beszélgetései ahogyan egy e-mail-szolgáltatást is használna a levelei kezeléséhez."</string>
<string name="screen_server_confirmation_title_login">"Hamarosan bejelentkezik ebbe: %1$s"</string>
<string name="screen_server_confirmation_title_register">"Hamarosan létrehoz egy fiókot ezen: %1$s"</string>
<string name="screen_waitlist_message">"Jelenleg nagy a kereslet a(z) %2$s oldalon futó %1$s iránt. Térjen vissza néhány nap múlva az alkalmazáshoz, és próbálja újra.
Köszönjük a türelmét!"</string>
<string name="screen_waitlist_title">"Már majdnem kész van."</string>
<string name="screen_waitlist_title_success">"Bent van."</string>
<string name="screen_login_subtitle">"A Matrix egy nyitott hálózat a biztonságos, decentralizált kommunikációhoz."</string>
<string name="screen_waitlist_message_success">"Üdvözli az %1$s!"</string>
</resources>

View file

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_account_provider_change">"Ubah penyedia akun"</string>
<string name="screen_account_provider_form_hint">"Alamat homeserver"</string>
<string name="screen_account_provider_form_notice">"Masukkan istilah pencarian atau alamat domain."</string>
<string name="screen_account_provider_form_subtitle">"Cari perusahaan, komunitas, atau server pribadi."</string>
<string name="screen_account_provider_form_title">"Cari penyedia akun"</string>
<string name="screen_account_provider_signin_subtitle">"Di sinilah percakapan Anda akan berlangsung — sama seperti Anda menggunakan penyedia surel untuk menyimpan surel Anda."</string>
<string name="screen_account_provider_signin_title">"Anda akan masuk ke %s"</string>
<string name="screen_account_provider_signup_subtitle">"Di sinilah percakapan Anda akan berlangsung — sama seperti Anda menggunakan penyedia surel untuk menyimpan surel Anda."</string>
<string name="screen_account_provider_signup_title">"Anda akan membuat akun di %s"</string>
<string name="screen_change_account_provider_matrix_org_subtitle">"Matrix.org adalah server besar dan gratis di jaringan Matrix publik untuk komunikasi yang aman dan terdesentralisasi, disediakan oleh Yayasan Matrix.org."</string>
<string name="screen_change_account_provider_other">"Lainnya"</string>
<string name="screen_change_account_provider_subtitle">"Gunakan penyedia akun yang berbeda, seperti server pribadi Anda sendiri atau akun kerja."</string>
<string name="screen_change_account_provider_title">"Ubah penyedia akun"</string>
<string name="screen_change_server_error_invalid_homeserver">"Kami tidak dapat menjangkau server ini. Periksa apakah Anda telah memasukkan URL homeserver dengan benar. Jika URL sudah benar, hubungi administrator homeserver Anda untuk bantuan lebih lanjut."</string>
<string name="screen_change_server_error_no_sliding_sync_message">"Server ini saat ini tidak mendukung sinkronisasi geser."</string>
<string name="screen_change_server_form_header">"URL Homeserver"</string>
<string name="screen_change_server_form_notice">"Anda hanya dapat terhubung ke server yang ada yang mendukung sinkronisasi geser. Admin homeserver Anda perlu mengaturnya. %1$s"</string>
<string name="screen_change_server_subtitle">"Apa alamat server Anda?"</string>
<string name="screen_change_server_title">"Pilih server Anda"</string>
<string name="screen_login_error_deactivated_account">"Akun ini telah dinonaktifkan."</string>
<string name="screen_login_error_invalid_credentials">"Nama pengguna dan/atau kata sandi salah"</string>
<string name="screen_login_error_invalid_user_id">"Ini bukan pengenal pengguna yang valid. Format yang diharapkan: \'@pengguna:homeserver.org\'"</string>
<string name="screen_login_error_unsupported_authentication">"Homeserver yang dipilih tidak mendukung log masuk kata sandi atau OIDC. Silakan hubungi admin Anda atau pilih homeserver yang lain."</string>
<string name="screen_login_form_header">"Masukkan detail Anda"</string>
<string name="screen_login_title">"Selamat datang kembali!"</string>
<string name="screen_login_title_with_homeserver">"Masuk ke %1$s"</string>
<string name="screen_server_confirmation_change_server">"Ubah penyedia akun"</string>
<string name="screen_server_confirmation_message_login_element_dot_io">"Server pribadi untuk karyawan Element."</string>
<string name="screen_server_confirmation_message_login_matrix_dot_org">"Matrix adalah jaringan terbuka untuk komunikasi yang aman dan terdesentralisasi."</string>
<string name="screen_server_confirmation_message_register">"Di sinilah percakapan Anda akan berlangsung — sama seperti Anda menggunakan penyedia surel untuk menyimpan surel Anda."</string>
<string name="screen_server_confirmation_title_login">"Anda akan masuk ke %1$s"</string>
<string name="screen_server_confirmation_title_register">"Anda akan membuat akun di %1$s"</string>
<string name="screen_waitlist_message">"Ada permintaan tinggi untuk %1$s di %2$s saat ini. Kembalilah ke aplikasi dalam beberapa hari dan coba lagi.
Terima kasih atas kesabaran Anda!"</string>
<string name="screen_waitlist_title">"Anda hampir selesai."</string>
<string name="screen_waitlist_title_success">"Anda sudah masuk."</string>
<string name="screen_login_subtitle">"Matrix adalah jaringan terbuka untuk komunikasi yang aman dan terdesentralisasi."</string>
<string name="screen_waitlist_message_success">"Selamat datang di %1$s!"</string>
</resources>

View file

@ -2,4 +2,17 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_signout_confirmation_dialog_content">"Bist du sicher, dass du dich abmelden willst?"</string>
<string name="screen_signout_in_progress_dialog_content">"Abmelden…"</string>
<string name="screen_signout_key_backup_disabled_subtitle">"Du bist dabei, dich von deiner letzten Sitzung abzumelden. Wenn du dich jetzt abmeldest, verlierst du den Zugriff auf deine verschlüsselten Nachrichten."</string>
<string name="screen_signout_key_backup_disabled_title">"Du hast das Backup ausgeschaltet"</string>
<string name="screen_signout_key_backup_offline_subtitle">"Deine Schlüssel wurden noch gesichert, als du offline gegangen bist. Stelle die Verbindung wieder her, damit deine Schlüssel gesichert werden können, bevor du dich abmeldest."</string>
<string name="screen_signout_key_backup_offline_title">"Deine Schlüssel werden noch gesichert"</string>
<string name="screen_signout_key_backup_ongoing_subtitle">"Bitte warte, bis der Vorgang abgeschlossen ist, bevor du dich abmeldest."</string>
<string name="screen_signout_key_backup_ongoing_title">"Deine Schlüssel werden noch gesichert"</string>
<string name="screen_signout_recovery_disabled_subtitle">"Du bist dabei, dich von deiner letzten Sitzung abzumelden. Wenn du dich jetzt abmeldest, verlierst du den Zugriff auf deine verschlüsselten Nachrichten."</string>
<string name="screen_signout_recovery_disabled_title">"Wiederherstellung nicht eingerichtet"</string>
<string name="screen_signout_save_recovery_key_subtitle">"Du bist dabei, dich von deiner letzten Sitzung abzumelden. Wenn du dich jetzt abmeldest, verlierst du möglicherweise den Zugriff auf deine verschlüsselten Nachrichten."</string>
<string name="screen_signout_save_recovery_key_title">"Hast du deinen Wiederherstellungsschlüssel gespeichert?"</string>
<string name="screen_signout_confirmation_dialog_submit">"Abmelden"</string>
<string name="screen_signout_confirmation_dialog_title">"Abmelden"</string>
<string name="screen_signout_preference_item">"Abmelden"</string>
</resources>

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