Merge branch 'release/26.03.0'

This commit is contained in:
Jorge Martín 2026-02-24 17:06:22 +01:00
commit c2cb32abd4
1265 changed files with 5114 additions and 3964 deletions

View file

@ -25,6 +25,20 @@ jobs:
group: ${{ github.ref == 'refs/heads/develop' && format('build-develop-{0}-{1}', matrix.variant, github.sha) || format('build-{0}-{1}', matrix.variant, github.ref) }} group: ${{ github.ref == 'refs/heads/develop' && format('build-develop-{0}-{1}', matrix.variant, github.sha) || format('build-{0}-{1}', matrix.variant, github.ref) }}
cancel-in-progress: true cancel-in-progress: true
steps: steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
tool-cache: true
# All of these default to true, but we should only need the 'android' one (and maybe swap-storage?)
android: false
dotnet: true
haskell: true
# This takes way too long to run (~2 minutes) and it saves only ~5.5GB
large-packages: false
docker-images: true
swap-storage: false
- uses: actions/checkout@v6 - uses: actions/checkout@v6
with: with:
# Ensure we are building the branch and not the branch after being merged on develop # Ensure we are building the branch and not the branch after being merged on develop
@ -60,16 +74,6 @@ jobs:
path: | path: |
app/build/outputs/apk/gplay/debug/*-universal-debug.apk app/build/outputs/apk/gplay/debug/*-universal-debug.apk
app/build/outputs/apk/fdroid/debug/*-universal-debug.apk app/build/outputs/apk/fdroid/debug/*-universal-debug.apk
- name: Upload x86_64 APK for Maestro
if: ${{ matrix.variant == 'debug' }}
uses: actions/upload-artifact@v6
with:
name: elementx-apk-maestro
path: |
app/build/outputs/apk/gplay/debug/app-gplay-x86_64-debug.apk
retention-days: 5
overwrite: true
if-no-files-found: error
- uses: rnkdsh/action-upload-diawi@4e1421305be7cfc510d05f47850262eeaf345108 # v1.5.12 - uses: rnkdsh/action-upload-diawi@4e1421305be7cfc510d05f47850262eeaf345108 # v1.5.12
id: diawi id: diawi
# Do not fail the whole build if Diawi upload fails # Do not fail the whole build if Diawi upload fails

View file

@ -27,6 +27,20 @@ jobs:
group: ${{ github.ref == 'refs/heads/develop' && format('build-develop-enterprise-{0}-{1}', matrix.variant, github.sha) || format('build-enterprise-{0}-{1}', matrix.variant, github.ref) }} group: ${{ github.ref == 'refs/heads/develop' && format('build-develop-enterprise-{0}-{1}', matrix.variant, github.sha) || format('build-enterprise-{0}-{1}', matrix.variant, github.ref) }}
cancel-in-progress: true cancel-in-progress: true
steps: steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
tool-cache: true
# All of these default to true, but we should only need the 'android' one (and maybe swap-storage?)
android: false
dotnet: true
haskell: true
# This takes way too long to run (~2 minutes) and it saves only ~5.5GB
large-packages: false
docker-images: true
swap-storage: false
- uses: actions/checkout@v6 - uses: actions/checkout@v6
with: with:
# Ensure we are building the branch and not the branch after being merged on develop # Ensure we are building the branch and not the branch after being merged on develop

View file

@ -18,11 +18,24 @@ jobs:
build-apk: build-apk:
name: Build APK name: Build APK
runs-on: ubuntu-latest runs-on: ubuntu-latest
# Allow one per PR.
concurrency: concurrency:
group: ${{ format('maestro-{0}', github.ref) }} group: ${{ format('maestro-build-{0}', github.ref) }}
cancel-in-progress: true cancel-in-progress: true
steps: steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
tool-cache: true
# All of these default to true, but we should only need the 'android' one (and maybe swap-storage?)
android: false
dotnet: true
haskell: true
# This takes way too long to run (~2 minutes) and it saves only ~5.5GB
large-packages: false
docker-images: true
swap-storage: false
- uses: actions/checkout@v6 - uses: actions/checkout@v6
with: with:
# Ensure we are building the branch and not the branch after being merged on develop # Ensure we are building the branch and not the branch after being merged on develop
@ -57,10 +70,10 @@ jobs:
name: Maestro test suite name: Maestro test suite
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [ build-apk ] needs: [ build-apk ]
# Allow one per PR. # Allow only one to run at a time, since they use the same environment.
# Otherwise, tests running in parallel can break each other.
concurrency: concurrency:
group: ${{ format('maestro-{0}', github.ref) }} group: maestro-test
cancel-in-progress: true
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
if: (github.event_name == 'pull_request' && github.event.pull_request.fork == null) || github.event_name == 'workflow_dispatch' if: (github.event_name == 'pull_request' && github.event.pull_request.fork == null) || github.event_name == 'workflow_dispatch'
@ -110,6 +123,21 @@ jobs:
retention-days: 5 retention-days: 5
overwrite: true overwrite: true
if-no-files-found: error if-no-files-found: error
- name: Update summary (success)
if: steps.maestro_test.outcome == 'success'
run: |
echo "### Maestro tests worked :rocket:!"
- name: Update summary (failure)
if: steps.maestro_test.outcome != 'success'
run: |
LOG_FILE=$(find ~/.maestro/tests/ -name maestro.log)
echo "Log file: $LOG_FILE"
LOG_LINES="$(tail -n 30 $LOG_FILE)"
echo "### :x: Maestro tests failed...
\`\`\`
$LOG_LINES
\`\`\`" >> $GITHUB_STEP_SUMMARY
- name: Fail the workflow in case of error in test - name: Fail the workflow in case of error in test
if: steps.maestro_test.outcome != 'success' if: steps.maestro_test.outcome != 'success'
run: | run: |

View file

@ -16,6 +16,20 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.repository == 'element-hq/element-x-android' }} if: ${{ github.repository == 'element-hq/element-x-android' }}
steps: steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
tool-cache: true
# All of these default to true, but we should only need the 'android' one (and maybe swap-storage?)
android: false
dotnet: true
haskell: true
# This takes way too long to run (~2 minutes) and it saves only ~5.5GB
large-packages: false
docker-images: true
swap-storage: false
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- name: Use JDK 21 - name: Use JDK 21
uses: actions/setup-java@v5 uses: actions/setup-java@v5

View file

@ -17,6 +17,20 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.repository == 'element-hq/element-x-android' }} if: ${{ github.repository == 'element-hq/element-x-android' }}
steps: steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
tool-cache: true
# All of these default to true, but we should only need the 'android' one (and maybe swap-storage?)
android: false
dotnet: true
haskell: true
# This takes way too long to run (~2 minutes) and it saves only ~5.5GB
large-packages: false
docker-images: true
swap-storage: false
- name: ⏬ Checkout with LFS - name: ⏬ Checkout with LFS
uses: nschloe/action-cached-lfs-checkout@f46300cd8952454b9f0a21a3d133d4bd5684cfc2 # v1.2.3 uses: nschloe/action-cached-lfs-checkout@f46300cd8952454b9f0a21a3d133d4bd5684cfc2 # v1.2.3

View file

@ -17,6 +17,20 @@ jobs:
name: Search for forbidden patterns name: Search for forbidden patterns
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
tool-cache: true
# All of these default to true, but we should only need the 'android' one (and maybe swap-storage?)
android: false
dotnet: true
haskell: true
# This takes way too long to run (~2 minutes) and it saves only ~5.5GB
large-packages: false
docker-images: true
swap-storage: false
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- name: Add SSH private keys for submodule repositories - name: Add SSH private keys for submodule repositories
uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd # v0.9.1 uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd # v0.9.1

View file

@ -17,6 +17,20 @@ jobs:
if: github.event_name == 'workflow_dispatch' || github.event.label.name == 'Record-Screenshots' if: github.event_name == 'workflow_dispatch' || github.event.label.name == 'Record-Screenshots'
steps: steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
tool-cache: true
# All of these default to true, but we should only need the 'android' one (and maybe swap-storage?)
android: false
dotnet: true
haskell: true
# This takes way too long to run (~2 minutes) and it saves only ~5.5GB
large-packages: false
docker-images: true
swap-storage: false
- name: Remove Record-Screenshots label - name: Remove Record-Screenshots label
if: github.event.label.name == 'Record-Screenshots' if: github.event.label.name == 'Record-Screenshots'
uses: actions-ecosystem/action-remove-labels@2ce5d41b4b6aa8503e285553f75ed56e0a40bae0 # v1.3.0 uses: actions-ecosystem/action-remove-labels@2ce5d41b4b6aa8503e285553f75ed56e0a40bae0 # v1.3.0

View file

@ -18,6 +18,20 @@ jobs:
group: ${{ format('build-release-main-gplay-{0}', github.sha) }} group: ${{ format('build-release-main-gplay-{0}', github.sha) }}
cancel-in-progress: true cancel-in-progress: true
steps: steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
tool-cache: true
# All of these default to true, but we should only need the 'android' one (and maybe swap-storage?)
android: false
dotnet: true
haskell: true
# This takes way too long to run (~2 minutes) and it saves only ~5.5GB
large-packages: false
docker-images: true
swap-storage: false
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- name: Use JDK 21 - name: Use JDK 21
uses: actions/setup-java@v5 uses: actions/setup-java@v5
@ -88,6 +102,20 @@ jobs:
group: ${{ format('build-release-main-fdroid-{0}', github.sha) }} group: ${{ format('build-release-main-fdroid-{0}', github.sha) }}
cancel-in-progress: true cancel-in-progress: true
steps: steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
tool-cache: true
# All of these default to true, but we should only need the 'android' one (and maybe swap-storage?)
android: false
dotnet: true
haskell: true
# This takes way too long to run (~2 minutes) and it saves only ~5.5GB
large-packages: false
docker-images: true
swap-storage: false
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- name: Use JDK 21 - name: Use JDK 21
uses: actions/setup-java@v5 uses: actions/setup-java@v5

View file

@ -8,6 +8,13 @@
# Please see LICENSE in the repository root for full details. # Please see LICENSE in the repository root for full details.
# #
# First we disable the onboarding flow on Chrome, which is a source of issues
# (see https://stackoverflow.com/a/64629745)
echo "Disabling Chrome onboarding flow"
adb shell am set-debug-app --persistent com.android.chrome
adb shell 'echo "chrome --disable-fre --no-default-browser-check --no-first-run" > /data/local/tmp/chrome-command-line'
adb shell am start -n com.android.chrome/com.google.android.apps.chrome.Main
adb install -r $1 adb install -r $1
echo "Starting the screen recording..." echo "Starting the screen recording..."
adb push .github/workflows/scripts/maestro/local-recording.sh /data/local/tmp/ adb push .github/workflows/scripts/maestro/local-recording.sh /data/local/tmp/

View file

@ -22,6 +22,20 @@ jobs:
group: ${{ format('sonar-{0}', github.ref) }} group: ${{ format('sonar-{0}', github.ref) }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/develop' }} cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/develop' }}
steps: steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
tool-cache: true
# All of these default to true, but we should only need the 'android' one (and maybe swap-storage?)
android: false
dotnet: true
haskell: true
# This takes way too long to run (~2 minutes) and it saves only ~5.5GB
large-packages: false
docker-images: true
swap-storage: false
- uses: actions/checkout@v6 - uses: actions/checkout@v6
with: with:
# Ensure we are building the branch and not the branch after being merged on develop # Ensure we are building the branch and not the branch after being merged on develop

View file

@ -22,6 +22,20 @@ jobs:
group: ${{ github.ref == 'refs/heads/main' && format('unit-tests-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('unit-tests-develop-{0}', github.sha) || format('unit-tests-{0}', github.ref) }} group: ${{ github.ref == 'refs/heads/main' && format('unit-tests-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('unit-tests-develop-{0}', github.sha) || format('unit-tests-{0}', github.ref) }}
cancel-in-progress: true cancel-in-progress: true
steps: steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
tool-cache: true
# All of these default to true, but we should only need the 'android' one (and maybe swap-storage?)
android: false
dotnet: true
haskell: true
# This takes way too long to run (~2 minutes) and it saves only ~5.5GB
large-packages: false
docker-images: true
swap-storage: false
# Increase swapfile size to prevent screenshot tests getting terminated # Increase swapfile size to prevent screenshot tests getting terminated
# https://github.com/actions/runner-images/discussions/7188#discussioncomment-6750749 # https://github.com/actions/runner-images/discussions/7188#discussioncomment-6750749
- name: 💽 Increase swapfile size - name: 💽 Increase swapfile size

View file

@ -8,27 +8,6 @@ appId: ${MAESTRO_APP_ID}
- tapOn: - tapOn:
id: "login-continue" id: "login-continue"
## MAS page ## MAS page
## Conditional workflow to pass the Chrome first launch welcome page.
- retry:
maxRetries: 3
commands:
- runFlow:
when:
visible: 'Use without an account'
commands:
- tapOn: "Use without an account"
## For older chrome versions
- runFlow:
when:
visible: 'Accept & continue'
commands:
- tapOn: "Accept & continue"
- runFlow:
when:
visible: 'No thanks'
commands:
- tapOn: "No thanks"
## Working when running Maestro locally, but not on the CI yet.
- retry: - retry:
maxRetries: 3 maxRetries: 3
commands: commands:

View file

@ -1,5 +1,8 @@
appId: ${MAESTRO_APP_ID} appId: ${MAESTRO_APP_ID}
--- ---
- extendedWaitUntil:
visible: "Enter recovery key"
timeout: 30000
- takeScreenshot: build/maestro/150-Verify - takeScreenshot: build/maestro/150-Verify
- tapOn: "Enter recovery key" - tapOn: "Enter recovery key"
- tapOn: - tapOn:
@ -7,7 +10,10 @@ appId: ${MAESTRO_APP_ID}
- inputText: ${MAESTRO_RECOVERY_KEY} - inputText: ${MAESTRO_RECOVERY_KEY}
- hideKeyboard - hideKeyboard
- tapOn: "Continue" - tapOn: "Continue"
- extendedWaitUntil: - retry:
visible: "Device verified" maxRetries: 3
timeout: 30000 commands:
- extendedWaitUntil:
visible: "Device verified"
timeout: 30000
- tapOn: "Continue" - tapOn: "Continue"

View file

@ -2,4 +2,4 @@ appId: ${MAESTRO_APP_ID}
--- ---
- extendedWaitUntil: - extendedWaitUntil:
visible: "Be in your element" visible: "Be in your element"
timeout: 10000 timeout: 30000

View file

@ -2,4 +2,4 @@ appId: ${MAESTRO_APP_ID}
--- ---
- extendedWaitUntil: - extendedWaitUntil:
visible: "Confirm your identity" visible: "Confirm your identity"
timeout: 20000 timeout: 60000

View file

@ -1,3 +1,77 @@
Changes in Element X v26.02.0
=============================
<!-- Release notes generated using configuration in .github/release.yml at v26.02.0 -->
## What's Changed
### ✨ Features
* When a background SDK task fails, react in the client by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6166
* Enable space feature flags by default by @ganfra in https://github.com/element-hq/element-x-android/pull/6171
### 🙌 Improvements
* Improve space management with pagination and partial failure handling by @ganfra in https://github.com/element-hq/element-x-android/pull/6099
* Iterate on QrCode login error buttons by @bmarty in https://github.com/element-hq/element-x-android/pull/6101
* Update icon shown for world_readable rooms by @richvdh in https://github.com/element-hq/element-x-android/pull/6111
* QRCode login: treat not found error as expired error. by @bmarty in https://github.com/element-hq/element-x-android/pull/6161
* Iterate on Space related UI by @ganfra in https://github.com/element-hq/element-x-android/pull/6150
### 🔒 Security
* Ensure aspect ratio of images in the timeline is restricted by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6168
### 🐛 Bugfixes
* Ensure that Element Call activity is not closed when using an external link by @bmarty in https://github.com/element-hq/element-x-android/pull/6114
* Refresh a Space's room list after creating a room in it by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6135
* When creating a DM, set room history visibility to `invited` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6138
* Fix back navigation after creating a room in a space by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6134
* Fix `LinkifyHelper` index out of bounds with parenthesis by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6140
* Change role screen won't be dismissed until changes take effect by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6141
### 🗣 Translations
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/6122
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/6155
### 🧱 Build
* Try fixing Maestro tests (again) by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6149
* Add a stale bot for X-Needs-Info issues. by @bmarty in https://github.com/element-hq/element-x-android/pull/6153
* [Release script] Ensure that the release version will match the next Monday date by @bmarty in https://github.com/element-hq/element-x-android/pull/6152
### 🚧 In development 🚧
* Add Space Filters feature for Room List by @ganfra in https://github.com/element-hq/element-x-android/pull/6136
* Add history sharing badges to room details by @kaylendog in https://github.com/element-hq/element-x-android/pull/6132
### Dependency upgrades
* Update dependency androidx.work:work-runtime-ktx to v2.11.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6105
* Update metro to v0.10.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6106
* Update camera to v1.5.3 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6103
* Update activity to v1.12.3 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6104
* Update dependency com.posthog:posthog-android to v3.30.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6120
* Update kotlin by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6102
* Update roborazzi to v1.58.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6124
* Update kover by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6139
* Update dependency com.posthog:posthog-android to v3.31.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6145
* Update kotlin by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6142
* Update media3 to v1.9.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6151
* Update dependency org.matrix.rustcomponents:sdk-android to v26.02.6 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6144
* Update firebaseAppDistribution to v5.2.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6146
* Update dependency com.google.firebase:firebase-bom to v34.9.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6148
* Update dependency io.sentry:sentry-android to v8.32.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6157
* Update metro to v0.10.3 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6164
* Update dependency org.matrix.rustcomponents:sdk-android to v26.2.10 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6169
* chore(deps): update plugin paparazzi to v2.0.0-alpha04 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6048
* fix(deps): update dependency org.jetbrains.kotlinx:kover-gradle-plugin to v0.9.7 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6173
* fix(deps): update haze to v1.7.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6175
### Others
* Improve favorite wording and icon of room by @bmarty in https://github.com/element-hq/element-x-android/pull/6097
* Add special flow for leaving a space as the last owner by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6112
* Remove `runBlocking` in `ThreadedMessagesNode` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6108
* Revert "Add "call.pro.element.io" in the list of known hosts for Element Call." by @bmarty in https://github.com/element-hq/element-x-android/pull/6118
* Refactor room list filtering to use Rust SDK by @ganfra in https://github.com/element-hq/element-x-android/pull/6117
* Ensure http 429 are retried 3 times before failing. by @bmarty in https://github.com/element-hq/element-x-android/pull/6119
* Remove `JoinRule.Private` from the codebase by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6129
* Fix voice message recording not starting after permission is granted by @kknappe in https://github.com/element-hq/element-x-android/pull/6109
* Use correct bg color. by @bmarty in https://github.com/element-hq/element-x-android/pull/6165
* Document "Developer options" and remove outdated instructions by @MadLittleMods in https://github.com/element-hq/element-x-android/pull/6162
* Update SpaceFilterButton selected state color by @ganfra in https://github.com/element-hq/element-x-android/pull/6178
## New Contributors
* @kknappe made their first contribution in https://github.com/element-hq/element-x-android/pull/6109
* @MadLittleMods made their first contribution in https://github.com/element-hq/element-x-android/pull/6162
**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v26.01.2...v26.02.0
Changes in Element X v26.01.2 Changes in Element X v26.01.2
============================= =============================

View file

@ -79,7 +79,6 @@ import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.di.annotations.SessionCoroutineScope import io.element.android.libraries.di.annotations.SessionCoroutineScope
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.MAIN_SPACE
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
@ -113,6 +112,10 @@ import kotlin.time.Duration.Companion.seconds
import kotlin.time.toKotlinDuration import kotlin.time.toKotlinDuration
import im.vector.app.features.analytics.plan.JoinedRoom as JoinedRoomAnalyticsEvent import im.vector.app.features.analytics.plan.JoinedRoom as JoinedRoomAnalyticsEvent
// The maximum number of room nodes that should be kept in the backstack at the same time.
// Having 5 rooms in the backstack seems reasonable and shouldn't grow the saved state size too much.
private const val MAX_ROOM_NODE_COUNT = 5
@ContributesNode(SessionScope::class) @ContributesNode(SessionScope::class)
@AssistedInject @AssistedInject
class LoggedInFlowNode( class LoggedInFlowNode(
@ -211,8 +214,6 @@ class LoggedInFlowNode(
onCreate = { onCreate = {
analyticsRoomListStateWatcher.start() analyticsRoomListStateWatcher.start()
appNavigationStateService.onNavigateToSession(id, matrixClient.sessionId) appNavigationStateService.onNavigateToSession(id, matrixClient.sessionId)
// TODO We do not support Space yet, so directly navigate to main space
appNavigationStateService.onNavigateToSpace(id, MAIN_SPACE)
loggedInFlowProcessor.observeEvents(sessionCoroutineScope) loggedInFlowProcessor.observeEvents(sessionCoroutineScope)
matrixClient.sessionVerificationService.setListener(verificationListener) matrixClient.sessionVerificationService.setListener(verificationListener)
mediaPreviewConfigMigration() mediaPreviewConfigMigration()
@ -242,7 +243,6 @@ class LoggedInFlowNode(
} }
}, },
onDestroy = { onDestroy = {
appNavigationStateService.onLeavingSpace(id)
appNavigationStateService.onLeavingSession(id) appNavigationStateService.onLeavingSession(id)
loggedInFlowProcessor.stopObserving() loggedInFlowProcessor.stopObserving()
matrixClient.sessionVerificationService.setListener(null) matrixClient.sessionVerificationService.setListener(null)
@ -327,12 +327,13 @@ class LoggedInFlowNode(
NavTarget.Home -> { NavTarget.Home -> {
val callback = object : HomeEntryPoint.Callback { val callback = object : HomeEntryPoint.Callback {
override fun navigateToRoom(roomId: RoomId, joinedRoom: JoinedRoom?) { override fun navigateToRoom(roomId: RoomId, joinedRoom: JoinedRoom?) {
backstack.push( lifecycleScope.launch {
NavTarget.Room( attachRoom(
roomIdOrAlias = roomId.toRoomIdOrAlias(), roomIdOrAlias = roomId.toRoomIdOrAlias(),
initialElement = RoomNavigationTarget.Root(joinedRoom = joinedRoom) initialElement = RoomNavigationTarget.Root(joinedRoom = joinedRoom),
clearBackstack = false,
) )
) }
} }
override fun navigateToSettings() { override fun navigateToSettings() {
@ -356,7 +357,13 @@ class LoggedInFlowNode(
} }
override fun navigateToRoomSettings(roomId: RoomId) { override fun navigateToRoomSettings(roomId: RoomId) {
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.Details)) lifecycleScope.launch {
attachRoom(
roomIdOrAlias = roomId.toRoomIdOrAlias(),
initialElement = RoomNavigationTarget.Details,
clearBackstack = false
)
}
} }
override fun navigateToBugReport() { override fun navigateToBugReport() {
@ -372,7 +379,9 @@ class LoggedInFlowNode(
is NavTarget.Room -> { is NavTarget.Room -> {
val joinedRoomCallback = object : JoinedRoomLoadedFlowNode.Callback { val joinedRoomCallback = object : JoinedRoomLoadedFlowNode.Callback {
override fun navigateToRoom(roomId: RoomId, serverNames: List<String>) { override fun navigateToRoom(roomId: RoomId, serverNames: List<String>) {
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), serverNames)) lifecycleScope.launch {
attachRoom(roomIdOrAlias = roomId.toRoomIdOrAlias(), serverNames = serverNames, clearBackstack = false)
}
} }
override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) { override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) {
@ -382,16 +391,25 @@ class LoggedInFlowNode(
Timber.e("User link clicked: ${data.userId}.") Timber.e("User link clicked: ${data.userId}.")
} }
is PermalinkData.RoomLink -> { is PermalinkData.RoomLink -> {
val target = NavTarget.Room(
roomIdOrAlias = data.roomIdOrAlias,
serverNames = data.viaParameters,
trigger = JoinedRoomAnalyticsEvent.Trigger.Timeline,
initialElement = RoomNavigationTarget.Root(data.eventId),
)
if (pushToBackstack) { if (pushToBackstack) {
backstack.push(target) lifecycleScope.launch {
attachRoom(
roomIdOrAlias = data.roomIdOrAlias,
serverNames = data.viaParameters,
trigger = JoinedRoomAnalyticsEvent.Trigger.Timeline,
initialElement = RoomNavigationTarget.Root(data.eventId),
clearBackstack = false
)
}
} else { } else {
backstack.replace(target) backstack.replace(
NavTarget.Room(
roomIdOrAlias = data.roomIdOrAlias,
serverNames = data.viaParameters,
trigger = JoinedRoomAnalyticsEvent.Trigger.Timeline,
initialElement = RoomNavigationTarget.Root(data.eventId),
)
)
} }
} }
is PermalinkData.FallbackLink, is PermalinkData.FallbackLink,
@ -417,7 +435,9 @@ class LoggedInFlowNode(
is NavTarget.UserProfile -> { is NavTarget.UserProfile -> {
val callback = object : UserProfileEntryPoint.Callback { val callback = object : UserProfileEntryPoint.Callback {
override fun navigateToRoom(roomId: RoomId) { override fun navigateToRoom(roomId: RoomId) {
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias())) lifecycleScope.launch {
attachRoom(roomIdOrAlias = roomId.toRoomIdOrAlias(), clearBackstack = false)
}
} }
} }
userProfileEntryPoint.createNode( userProfileEntryPoint.createNode(
@ -446,11 +466,22 @@ class LoggedInFlowNode(
} }
override fun navigateToRoomNotificationSettings(roomId: RoomId) { override fun navigateToRoomNotificationSettings(roomId: RoomId) {
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.NotificationSettings)) lifecycleScope.launch {
attachRoom(
roomIdOrAlias = roomId.toRoomIdOrAlias(),
initialElement = RoomNavigationTarget.NotificationSettings,
)
}
} }
override fun navigateToEvent(roomId: RoomId, eventId: EventId) { override fun navigateToEvent(roomId: RoomId, eventId: EventId) {
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.Root(eventId))) lifecycleScope.launch {
attachRoom(
roomIdOrAlias = roomId.toRoomIdOrAlias(),
initialElement = RoomNavigationTarget.Root(eventId),
clearBackstack = false
)
}
} }
} }
val inputs = PreferencesEntryPoint.Params(navTarget.initialElement) val inputs = PreferencesEntryPoint.Params(navTarget.initialElement)
@ -481,7 +512,13 @@ class LoggedInFlowNode(
is NavTarget.CreateSpace -> { is NavTarget.CreateSpace -> {
val callback = object : CreateRoomEntryPoint.Callback { val callback = object : CreateRoomEntryPoint.Callback {
override fun onRoomCreated(roomId: RoomId) { override fun onRoomCreated(roomId: RoomId) {
backstack.replace(NavTarget.Room(roomIdOrAlias = RoomIdOrAlias.Id(roomId), serverNames = emptyList())) lifecycleScope.launch {
attachRoom(
roomIdOrAlias = roomId.toRoomIdOrAlias(),
serverNames = emptyList(),
clearBackstack = false,
)
}
} }
} }
createRoomEntryPoint createRoomEntryPoint
@ -518,13 +555,13 @@ class LoggedInFlowNode(
buildContext = buildContext, buildContext = buildContext,
callback = object : RoomDirectoryEntryPoint.Callback { callback = object : RoomDirectoryEntryPoint.Callback {
override fun navigateToRoom(roomDescription: RoomDescription) { override fun navigateToRoom(roomDescription: RoomDescription) {
backstack.push( lifecycleScope.launch {
NavTarget.Room( attachRoom(
roomIdOrAlias = roomDescription.roomId.toRoomIdOrAlias(), roomIdOrAlias = roomDescription.roomId.toRoomIdOrAlias(),
roomDescription = roomDescription, roomDescription = roomDescription,
trigger = JoinedRoomAnalyticsEvent.Trigger.RoomDirectory, trigger = JoinedRoomAnalyticsEvent.Trigger.RoomDirectory,
) )
) }
} }
}, },
) )
@ -541,7 +578,7 @@ class LoggedInFlowNode(
// Navigate to the room if the text/media was shared to a single one // Navigate to the room if the text/media was shared to a single one
roomIds.singleOrNull()?.let { roomId -> roomIds.singleOrNull()?.let { roomId ->
sessionCoroutineScope.launch { lifecycleScope.launch {
// Wait until the incoming share screen is removed // Wait until the incoming share screen is removed
backstack.elements.first { it.lastOrNull()?.key?.navTarget !is NavTarget.IncomingShare } backstack.elements.first { it.lastOrNull()?.key?.navTarget !is NavTarget.IncomingShare }
@ -572,8 +609,9 @@ class LoggedInFlowNode(
roomIdOrAlias: RoomIdOrAlias, roomIdOrAlias: RoomIdOrAlias,
serverNames: List<String> = emptyList(), serverNames: List<String> = emptyList(),
trigger: JoinedRoomAnalyticsEvent.Trigger? = null, trigger: JoinedRoomAnalyticsEvent.Trigger? = null,
eventId: EventId? = null, roomDescription: RoomDescription? = null,
clearBackstack: Boolean, initialElement: RoomNavigationTarget = RoomNavigationTarget.Root(),
clearBackstack: Boolean = false,
): RoomFlowNode { ): RoomFlowNode {
waitForNavTargetAttached { navTarget -> waitForNavTargetAttached { navTarget ->
navTarget is NavTarget.Home navTarget is NavTarget.Home
@ -582,8 +620,9 @@ class LoggedInFlowNode(
val roomNavTarget = NavTarget.Room( val roomNavTarget = NavTarget.Room(
roomIdOrAlias = roomIdOrAlias, roomIdOrAlias = roomIdOrAlias,
serverNames = serverNames, serverNames = serverNames,
roomDescription = roomDescription,
trigger = trigger, trigger = trigger,
initialElement = RoomNavigationTarget.Root(eventId = eventId) initialElement = initialElement,
) )
backstack.accept(AttachRoomOperation(roomNavTarget, clearBackstack)) backstack.accept(AttachRoomOperation(roomNavTarget, clearBackstack))
} }
@ -593,8 +632,7 @@ class LoggedInFlowNode(
return waitForChildAttached<RoomFlowNode, NavTarget> { return waitForChildAttached<RoomFlowNode, NavTarget> {
it is NavTarget.Room && it is NavTarget.Room &&
it.roomIdOrAlias == roomIdOrAlias && it.roomIdOrAlias == roomIdOrAlias &&
it.initialElement is RoomNavigationTarget.Root && it.initialElement == initialElement
it.initialElement.eventId == eventId
} }
} }
@ -640,7 +678,7 @@ class LoggedInFlowNode(
) { contentModifier -> ) { contentModifier ->
Box(modifier = contentModifier) { Box(modifier = contentModifier) {
val ftueState by ftueService.state.collectAsState() val ftueState by ftueService.state.collectAsState()
BackstackView() BackstackView(transitionHandler = rememberLoggedInFlowTransitionHandler(backstack))
if (ftueState is FtueState.Complete) { if (ftueState is FtueState.Complete) {
PermanentChild(permanentNavModel = permanentNavModel, navTarget = NavTarget.LoggedInPermanent) PermanentChild(permanentNavModel = permanentNavModel, navTarget = NavTarget.LoggedInPermanent)
} }
@ -655,6 +693,15 @@ private class AttachRoomOperation(
val roomTarget: LoggedInFlowNode.NavTarget.Room, val roomTarget: LoggedInFlowNode.NavTarget.Room,
val clearBackstack: Boolean, val clearBackstack: Boolean,
) : BackStackOperation<LoggedInFlowNode.NavTarget> { ) : BackStackOperation<LoggedInFlowNode.NavTarget> {
/**
* Returns a list containing last [count] elements that match [predicate] while preserving other elements.
*/
private fun <T> List<T>.keepingLast(count: Int, predicate: (T) -> Boolean): List<T> {
val matchingIndices = indices.filter { predicate(this[it]) }
val indicesToRemove = matchingIndices.dropLast(count).toSet()
return filterIndexed { index, _ -> index !in indicesToRemove }
}
override fun isApplicable(elements: NavElements<LoggedInFlowNode.NavTarget, BackStack.State>) = true override fun isApplicable(elements: NavElements<LoggedInFlowNode.NavTarget, BackStack.State>) = true
override fun invoke(elements: BackStackElements<LoggedInFlowNode.NavTarget>): BackStackElements<LoggedInFlowNode.NavTarget> { override fun invoke(elements: BackStackElements<LoggedInFlowNode.NavTarget>): BackStackElements<LoggedInFlowNode.NavTarget> {
@ -677,16 +724,34 @@ private class AttachRoomOperation(
val roomNavTarget = it.key.navTarget as? LoggedInFlowNode.NavTarget.Room val roomNavTarget = it.key.navTarget as? LoggedInFlowNode.NavTarget.Room
roomNavTarget?.roomIdOrAlias == roomTarget.roomIdOrAlias roomNavTarget?.roomIdOrAlias == roomTarget.roomIdOrAlias
} }
// Make sure the backstack of rooms can't grow indefinitely when opening permalinks.
val roomElementCount = elements.count { it.key.navTarget is LoggedInFlowNode.NavTarget.Room }
Timber.d("Current room nodes: $roomElementCount/$MAX_ROOM_NODE_COUNT")
// Crate a new list keeping all the elements, but for Room ones just keep the last MAX_ROOM_NODE_COUNT
val currentElements = elements.keepingLast(MAX_ROOM_NODE_COUNT) { element ->
element.key.navTarget is LoggedInFlowNode.NavTarget.Room
}
// If the room already existed, remove it from the stack and add a new node at the end
if (existingRoomElement != null) { if (existingRoomElement != null) {
elements.mapNotNull { element -> currentElements.mapNotNull { element ->
if (element == existingRoomElement) { if (element == existingRoomElement) {
null null
} else { } else {
element.transitionTo(STASHED, this) element.transitionTo(STASHED, this)
} }
} + existingRoomElement.transitionTo(ACTIVE, this) } + // Always create a new element, otherwise we wouldn't be navigating to the target event id or child node
BackStackElement(
key = NavKey(roomTarget),
fromState = CREATED,
targetState = ACTIVE,
operation = this
)
} else { } else {
Push<LoggedInFlowNode.NavTarget>(roomTarget).invoke(elements) // Otherwise, just push the new node to the end of the backstack
Push<LoggedInFlowNode.NavTarget>(roomTarget).invoke(currentElements)
} }
} }
} }

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2026 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.appnav
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.Transition
import androidx.compose.animation.core.spring
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import com.bumble.appyx.core.navigation.transition.ModifierTransitionHandler
import com.bumble.appyx.core.navigation.transition.TransitionDescriptor
import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.transitionhandler.rememberBackstackFader
import com.bumble.appyx.navmodel.backstack.transitionhandler.rememberBackstackSlider
/**
* A TransitionHandler that uses fade transition when Placeholder is being removed,
* and slide transition for all other cases.
*/
class LoggedInFlowTransitionHandler(
private val backstack: BackStack<LoggedInFlowNode.NavTarget>,
private val slider: ModifierTransitionHandler<LoggedInFlowNode.NavTarget, BackStack.State>,
private val fader: ModifierTransitionHandler<LoggedInFlowNode.NavTarget, BackStack.State>,
) : ModifierTransitionHandler<LoggedInFlowNode.NavTarget, BackStack.State>() {
override fun createModifier(
modifier: Modifier,
transition: Transition<BackStack.State>,
descriptor: TransitionDescriptor<LoggedInFlowNode.NavTarget, BackStack.State>
): Modifier {
val isPlaceholderBeingRemoved = backstack.elements.value.any { element ->
element.key.navTarget == LoggedInFlowNode.NavTarget.Placeholder &&
element.targetState != BackStack.State.ACTIVE
}
val handler = if (isPlaceholderBeingRemoved) fader else slider
return handler.createModifier(modifier, transition, descriptor)
}
}
@Composable
fun rememberLoggedInFlowTransitionHandler(
backstack: BackStack<LoggedInFlowNode.NavTarget>,
): ModifierTransitionHandler<LoggedInFlowNode.NavTarget, BackStack.State> {
val slider = rememberBackstackSlider<LoggedInFlowNode.NavTarget>(
transitionSpec = { spring(stiffness = Spring.StiffnessMediumLow) },
)
val fader = rememberBackstackFader<LoggedInFlowNode.NavTarget>(
transitionSpec = { spring(stiffness = Spring.StiffnessMediumLow) },
)
return remember(backstack, slider, fader) {
LoggedInFlowTransitionHandler(backstack, slider, fader)
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2026 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.appnav
import com.bumble.appyx.core.navigation.NavElements
import com.bumble.appyx.core.navigation.Operation
import com.bumble.appyx.navmodel.backstack.BackStack
import kotlinx.parcelize.Parcelize
/**
* Replaces all the current elements with the provided [navElements], keeping their [BackStack.State] too.
*/
@Parcelize
class ReplaceAllOperation<NavTarget : Any>(
private val navElements: NavElements<NavTarget, BackStack.State>
) : Operation<NavTarget, BackStack.State> {
override fun isApplicable(elements: NavElements<NavTarget, BackStack.State>): Boolean {
return true
}
override fun invoke(existing: NavElements<NavTarget, BackStack.State>): NavElements<NavTarget, BackStack.State> {
return navElements
}
}

View file

@ -16,9 +16,12 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.navigation.NavElements
import com.bumble.appyx.core.navigation.NavKey
import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.core.state.MutableSavedStateMap import com.bumble.appyx.core.state.MutableSavedStateMap
import com.bumble.appyx.core.state.SavedStateMap
import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.pop
import com.bumble.appyx.navmodel.backstack.operation.push import com.bumble.appyx.navmodel.backstack.operation.push
@ -33,6 +36,7 @@ import io.element.android.appnav.di.MatrixSessionCache
import io.element.android.appnav.intent.IntentResolver import io.element.android.appnav.intent.IntentResolver
import io.element.android.appnav.intent.ResolvedIntent import io.element.android.appnav.intent.ResolvedIntent
import io.element.android.appnav.room.RoomFlowNode import io.element.android.appnav.room.RoomFlowNode
import io.element.android.appnav.room.RoomNavigationTarget
import io.element.android.appnav.root.RootNavStateFlowFactory import io.element.android.appnav.root.RootNavStateFlowFactory
import io.element.android.appnav.root.RootPresenter import io.element.android.appnav.root.RootPresenter
import io.element.android.appnav.root.RootView import io.element.android.appnav.root.RootView
@ -49,6 +53,7 @@ import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.architecture.waitForChildAttached import io.element.android.libraries.architecture.waitForChildAttached
import io.element.android.libraries.core.uri.ensureProtocol import io.element.android.libraries.core.uri.ensureProtocol
import io.element.android.libraries.deeplink.api.DeeplinkData import io.element.android.libraries.deeplink.api.DeeplinkData
import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
@ -66,7 +71,9 @@ import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction
import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.analytics.api.watchers.AnalyticsColdStartWatcher import io.element.android.services.analytics.api.watchers.AnalyticsColdStartWatcher
import io.element.android.services.appnavstate.api.ROOM_OPENED_FROM_NOTIFICATION import io.element.android.services.appnavstate.api.ROOM_OPENED_FROM_NOTIFICATION
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -92,19 +99,27 @@ class RootFlowNode(
private val announcementService: AnnouncementService, private val announcementService: AnnouncementService,
private val analyticsService: AnalyticsService, private val analyticsService: AnalyticsService,
private val analyticsColdStartWatcher: AnalyticsColdStartWatcher, private val analyticsColdStartWatcher: AnalyticsColdStartWatcher,
@AppCoroutineScope private val appCoroutineScope: CoroutineScope,
) : BaseFlowNode<RootFlowNode.NavTarget>( ) : BaseFlowNode<RootFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
initialElement = NavTarget.SplashScreen, initialElement = NavTarget.SplashScreen,
savedStateMap = buildContext.savedStateMap, savedStateMap = null,
), ),
buildContext = buildContext, buildContext = buildContext,
plugins = plugins plugins = plugins
) { ) {
override fun onBuilt() { override fun onBuilt() {
analyticsColdStartWatcher.start() analyticsColdStartWatcher.start()
matrixSessionCache.restoreWithSavedState(buildContext.savedStateMap) appCoroutineScope.launch {
matrixSessionCache.restoreWithSavedState(buildContext.savedStateMap)
if (buildContext.savedStateMap != null) {
restoreSavedState(buildContext.savedStateMap)
observeNavState(true)
} else {
observeNavState(false)
}
}
super.onBuilt() super.onBuilt()
observeNavState()
} }
override fun onSaveInstanceState(state: MutableSavedStateMap) { override fun onSaveInstanceState(state: MutableSavedStateMap) {
@ -113,25 +128,68 @@ class RootFlowNode(
navStateFlowFactory.saveIntoSavedState(state) navStateFlowFactory.saveIntoSavedState(state)
} }
private fun observeNavState() { private fun observeNavState(skipFirst: Boolean) {
navStateFlowFactory.create(buildContext.savedStateMap).distinctUntilChanged().onEach { navState -> navStateFlowFactory.create(buildContext.savedStateMap)
Timber.v("navState=$navState") .distinctUntilChanged()
when (navState.loggedInState) { .drop(if (skipFirst) 1 else 0)
is LoggedInState.LoggedIn -> { .onEach { navState ->
if (navState.loggedInState.isTokenValid) { Timber.v("navState=$navState")
tryToRestoreLatestSession( when (navState.loggedInState) {
onSuccess = { sessionId -> switchToLoggedInFlow(sessionId, navState.cacheIndex) }, is LoggedInState.LoggedIn -> {
onFailure = { switchToNotLoggedInFlow(null) } if (navState.loggedInState.isTokenValid) {
) val sessionId = SessionId(navState.loggedInState.sessionId)
} else { if (matrixSessionCache.getOrNull(sessionId) != null) {
switchToSignedOutFlow(SessionId(navState.loggedInState.sessionId)) switchToLoggedInFlow(sessionId, navState.cacheIndex)
} else {
tryToRestoreLatestSession(
onSuccess = { sessionId -> switchToLoggedInFlow(sessionId, navState.cacheIndex) },
onFailure = { switchToNotLoggedInFlow(null) }
)
}
} else {
switchToSignedOutFlow(SessionId(navState.loggedInState.sessionId))
}
}
LoggedInState.NotLoggedIn -> {
switchToNotLoggedInFlow(null)
} }
} }
LoggedInState.NotLoggedIn -> {
switchToNotLoggedInFlow(null)
}
} }
}.launchIn(lifecycleScope) .launchIn(lifecycleScope)
}
/**
* Restore the saved state for navigation in the current backstack.
*
* **WARNING:** this is an unsafe operation abusing the internals of the Appyx library, but it's the only way allow async state
* restoration and not having to block the main thread when the app starts.
*
* Modify with utmost care and double check any possible Appyx updates that might break this.
*/
@Suppress("UNCHECKED_CAST")
private fun restoreSavedState(savedStateMap: SavedStateMap?) {
if (savedStateMap == null) return
// 'NavModel' is the key used for storing the nav model state data in the map in Appyx
val savedElements = buildContext.savedStateMap?.get("NavModel") as? NavElements<NavTarget, BackStack.State>
if (savedElements != null) {
backstack.accept(ReplaceAllOperation(savedElements))
}
}
/**
* Extract the saved state for navigation in the [navTarget].
*
* **WARNING:** this is an unsafe operation abusing the internals of the Appyx library, but it's the only way allow async state
* restoration and not having to block the main thread when the app starts.
*
* Modify with utmost care and double check any possible Appyx updates that might break this.
*/
@Suppress("UNCHECKED_CAST")
private fun extractSavedStateForNavTarget(navTarget: NavTarget, savedStateMap: SavedStateMap?): SavedStateMap? {
// 'ChildrenState' is the key used for storing the children state data in the map in Appyx
val childrenState = savedStateMap?.get("ChildrenState") as? Map<NavKey<NavTarget>, SavedStateMap> ?: return null
return childrenState.entries.find { (key, _) -> key.navTarget == navTarget }?.value
} }
private fun switchToLoggedInFlow(sessionId: SessionId, navId: Int) { private fun switchToLoggedInFlow(sessionId: SessionId, navId: Int) {
@ -243,6 +301,13 @@ class RootFlowNode(
backstack.push(NavTarget.NotLoggedInFlow(null)) backstack.push(NavTarget.NotLoggedInFlow(null))
} }
} }
val savedNavState = extractSavedStateForNavTarget(navTarget, this.buildContext.savedStateMap)
val buildContext = if (savedNavState != null) {
Timber.d("Creating a $navTarget with restored saved state")
buildContext.copy(savedStateMap = savedNavState)
} else {
buildContext.copy(savedStateMap = savedNavState)
}
createNode<LoggedInAppScopeFlowNode>(buildContext, plugins = listOf(inputs, callback)) createNode<LoggedInAppScopeFlowNode>(buildContext, plugins = listOf(inputs, callback))
} }
is NavTarget.NotLoggedInFlow -> { is NavTarget.NotLoggedInFlow -> {
@ -430,7 +495,7 @@ class RootFlowNode(
roomIdOrAlias = permalinkData.roomIdOrAlias, roomIdOrAlias = permalinkData.roomIdOrAlias,
trigger = JoinedRoom.Trigger.MobilePermalink, trigger = JoinedRoom.Trigger.MobilePermalink,
serverNames = permalinkData.viaParameters, serverNames = permalinkData.viaParameters,
eventId = focusedEventId, initialElement = RoomNavigationTarget.Root(eventId = focusedEventId),
clearBackstack = true clearBackstack = true
).maybeAttachThread(permalinkData.threadId, permalinkData.eventId) ).maybeAttachThread(permalinkData.threadId, permalinkData.eventId)
} }
@ -454,7 +519,7 @@ class RootFlowNode(
is DeeplinkData.Room -> { is DeeplinkData.Room -> {
loggedInFlowNode.attachRoom( loggedInFlowNode.attachRoom(
roomIdOrAlias = deeplinkData.roomId.toRoomIdOrAlias(), roomIdOrAlias = deeplinkData.roomId.toRoomIdOrAlias(),
eventId = if (deeplinkData.threadId != null) deeplinkData.threadId?.asEventId() else deeplinkData.eventId, initialElement = RoomNavigationTarget.Root(eventId = deeplinkData.threadId?.asEventId() ?: deeplinkData.eventId),
clearBackstack = true, clearBackstack = true,
).maybeAttachThread(deeplinkData.threadId, deeplinkData.eventId) ).maybeAttachThread(deeplinkData.threadId, deeplinkData.eventId)
} }

View file

@ -21,7 +21,6 @@ import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.analyticsproviders.api.AnalyticsUserData import io.element.android.services.analyticsproviders.api.AnalyticsUserData
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import timber.log.Timber import timber.log.Timber
@ -77,20 +76,18 @@ class MatrixSessionCache(
} }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun restoreWithSavedState(state: SavedStateMap?) { suspend fun restoreWithSavedState(state: SavedStateMap?) {
Timber.d("Restore state") Timber.d("Restore state")
if (state == null || sessionIdsToMatrixSession.isNotEmpty()) { if (state == null || sessionIdsToMatrixSession.isNotEmpty()) {
Timber.w("Restore with non-empty map") Timber.w("No need to restore saved state")
return return
} }
val sessionIds = state[SAVE_INSTANCE_KEY] as? Array<SessionId> val sessionIds = state[SAVE_INSTANCE_KEY] as? Array<SessionId>
Timber.d("Restore matrix session keys = ${sessionIds?.map { it.value }}") Timber.d("Restore matrix session keys = ${sessionIds?.map { it.value }}")
if (sessionIds.isNullOrEmpty()) return if (sessionIds.isNullOrEmpty()) return
// Not ideal but should only happens in case of process recreation. This ensure we restore all the active sessions before restoring the node graphs. // Not ideal but should only happens in case of process recreation. This ensure we restore all the active sessions before restoring the node graphs.
runBlocking { sessionIds.forEach { sessionId ->
sessionIds.forEach { sessionId -> getOrRestore(sessionId)
getOrRestore(sessionId)
}
} }
} }

@ -1 +1 @@
Subproject commit 6207ddc1cb7dac7fdb6212c0158497a1d9752c75 Subproject commit 1fd0d297d944186e3af2773e1c5db2938d60f74b

View file

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

View file

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

View file

@ -29,6 +29,7 @@ class DeclineCallBroadcastReceiver : BroadcastReceiver() {
companion object { companion object {
const val EXTRA_NOTIFICATION_DATA = "EXTRA_NOTIFICATION_DATA" const val EXTRA_NOTIFICATION_DATA = "EXTRA_NOTIFICATION_DATA"
} }
@Inject @Inject
lateinit var activeCallManager: ActiveCallManager lateinit var activeCallManager: ActiveCallManager
@ -40,7 +41,13 @@ class DeclineCallBroadcastReceiver : BroadcastReceiver() {
?: return ?: return
context.bindings<CallBindings>().inject(this) context.bindings<CallBindings>().inject(this)
appCoroutineScope.launch { appCoroutineScope.launch {
activeCallManager.hungUpCall(callType = CallType.RoomCall(notificationData.sessionId, notificationData.roomId)) activeCallManager.hangUpCall(
callType = CallType.RoomCall(
sessionId = notificationData.sessionId,
roomId = notificationData.roomId,
),
notificationData = notificationData,
)
} }
} }
} }

View file

@ -100,7 +100,7 @@ class CallScreenPresenter(
) )
} }
onDispose { onDispose {
appCoroutineScope.launch { activeCallManager.hungUpCall(callType) } appCoroutineScope.launch { activeCallManager.hangUpCall(callType) }
} }
} }

View file

@ -118,7 +118,7 @@ class IncomingCallActivity : AppCompatActivity() {
private fun onCancel() { private fun onCancel() {
val activeCall = activeCallManager.activeCall.value ?: return val activeCall = activeCallManager.activeCall.value ?: return
appCoroutineScope.launch { appCoroutineScope.launch {
activeCallManager.hungUpCall(callType = activeCall.callType) activeCallManager.hangUpCall(callType = activeCall.callType)
} }
} }
} }

View file

@ -51,6 +51,9 @@ import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
/**
* Ref: https://www.figma.com/design/0MMNu7cTOzLOlWb7ctTkv3/Element-X?node-id=16501-5740
*/
@Composable @Composable
internal fun IncomingCallScreen( internal fun IncomingCallScreen(
notificationData: CallNotificationData, notificationData: CallNotificationData,
@ -94,11 +97,8 @@ internal fun IncomingCallScreen(
) )
} }
Row( Row(
modifier = Modifier modifier = Modifier.padding(bottom = 64.dp),
.fillMaxWidth() horizontalArrangement = Arrangement.spacedBy(48.dp),
.padding(start = 24.dp, end = 24.dp, bottom = 64.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) { ) {
ActionButton( ActionButton(
size = 64.dp, size = 64.dp,
@ -108,7 +108,6 @@ internal fun IncomingCallScreen(
backgroundColor = ElementTheme.colors.iconSuccessPrimary, backgroundColor = ElementTheme.colors.iconSuccessPrimary,
borderColor = ElementTheme.colors.borderSuccessSubtle borderColor = ElementTheme.colors.borderSuccessSubtle
) )
ActionButton( ActionButton(
size = 64.dp, size = 64.dp,
onClick = onCancel, onClick = onCancel,
@ -143,7 +142,7 @@ private fun ActionButton(
onClick = onClick, onClick = onClick,
colors = IconButtonDefaults.filledIconButtonColors( colors = IconButtonDefaults.filledIconButtonColors(
containerColor = backgroundColor, containerColor = backgroundColor,
contentColor = Color.White, contentColor = ElementTheme.colors.iconOnSolidPrimary,
) )
) { ) {
Icon( Icon(

View file

@ -72,10 +72,14 @@ interface ActiveCallManager {
suspend fun registerIncomingCall(notificationData: CallNotificationData) suspend fun registerIncomingCall(notificationData: CallNotificationData)
/** /**
* Called when the active call has been hung up. It will remove any existing UI and the active call. * Called to hang up the active call. It will hang up the call and remove any existing UI and the active call.
* @param callType The type of call that the user hung up, either an external url one or a room one. * @param callType The type of call that the user hangs up, either an external url one or a room one.
* @param notificationData The data for the incoming call notification.
*/ */
suspend fun hungUpCall(callType: CallType) suspend fun hangUpCall(
callType: CallType,
notificationData: CallNotificationData? = null,
)
/** /**
* Called after the user joined a call. It will remove any existing UI and set the call state as [CallState.InCall]. * Called after the user joined a call. It will remove any existing UI and set the call state as [CallState.InCall].
@ -192,12 +196,28 @@ class DefaultActiveCallManager(
} }
} }
override suspend fun hungUpCall(callType: CallType) = mutex.withLock { override suspend fun hangUpCall(
Timber.tag(tag).d("Hung up call: $callType") callType: CallType,
notificationData: CallNotificationData?,
) = mutex.withLock {
Timber.tag(tag).d("Hang up call: $callType")
cancelIncomingCallNotification()
val currentActiveCall = activeCall.value ?: run { val currentActiveCall = activeCall.value ?: run {
// activeCall.value can be null if the application has been killed while the call was ringing
// Build a currentActiveCall with the provided parameters.
notificationData?.let {
ActiveCall(
callType = callType,
callState = CallState.Ringing(
notificationData = notificationData,
)
)
}
} ?: run {
Timber.tag(tag).w("No active call, ignoring hang up") Timber.tag(tag).w("No active call, ignoring hang up")
return@withLock return@withLock
} }
if (currentActiveCall.callType != callType) { if (currentActiveCall.callType != callType) {
Timber.tag(tag).w("Call type $callType does not match the active call type, ignoring") Timber.tag(tag).w("Call type $callType does not match the active call type, ignoring")
return@withLock return@withLock
@ -208,9 +228,13 @@ class DefaultActiveCallManager(
matrixClientProvider.getOrRestore(notificationData.sessionId).getOrNull() matrixClientProvider.getOrRestore(notificationData.sessionId).getOrNull()
?.getRoom(notificationData.roomId) ?.getRoom(notificationData.roomId)
?.declineCall(notificationData.eventId) ?.declineCall(notificationData.eventId)
?.onFailure {
Timber.e(it, "Failed to decline incoming call")
}
?: run {
Timber.tag(tag).d("Couldn't find session or room to decline call for incoming call")
}
} }
cancelIncomingCallNotification()
if (activeWakeLock?.isHeld == true) { if (activeWakeLock?.isHeld == true) {
Timber.tag(tag).d("Releasing partial wakelock after hang up") Timber.tag(tag).d("Releasing partial wakelock after hang up")
activeWakeLock.release() activeWakeLock.release()
@ -221,7 +245,6 @@ class DefaultActiveCallManager(
override suspend fun joinedCall(callType: CallType) = mutex.withLock { override suspend fun joinedCall(callType: CallType) = mutex.withLock {
Timber.tag(tag).d("Joined call: $callType") Timber.tag(tag).d("Joined call: $callType")
cancelIncomingCallNotification() cancelIncomingCallNotification()
if (activeWakeLock?.isHeld == true) { if (activeWakeLock?.isHeld == true) {
Timber.tag(tag).d("Releasing partial wakelock after joining call") Timber.tag(tag).d("Releasing partial wakelock after joining call")

View file

@ -18,6 +18,7 @@ import android.webkit.JavascriptInterface
import android.webkit.WebView import android.webkit.WebView
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import io.element.android.libraries.core.extensions.runCatchingExceptions
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -82,6 +83,13 @@ class WebViewAudioManager(
AudioDeviceInfo.TYPE_BUILTIN_EARPIECE, AudioDeviceInfo.TYPE_BUILTIN_EARPIECE,
) )
private val audioDeviceComparator = Comparator<AudioDeviceInfo> { a, b ->
// If the device type is not in the wantedDeviceTypes list, we give it a high index, (i.e. low priority)
val indexOfA = wantedDeviceTypes.indexOf(a.type).let { if (it == -1) Int.MAX_VALUE else it }
val indexOfB = wantedDeviceTypes.indexOf(b.type).let { if (it == -1) Int.MAX_VALUE else it }
indexOfA.compareTo(indexOfB)
}
private val audioManager = webView.context.getSystemService(Context.AUDIO_SERVICE) as AudioManager private val audioManager = webView.context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
/** /**
@ -134,7 +142,7 @@ class WebViewAudioManager(
if (validNewDevices.isEmpty()) return if (validNewDevices.isEmpty()) return
// We need to calculate the available devices ourselves, since calling `listAudioDevices` will return an outdated list // We need to calculate the available devices ourselves, since calling `listAudioDevices` will return an outdated list
val audioDevices = (listAudioDevices() + validNewDevices).distinctBy { it.id } val audioDevices = (listAudioDevices() + validNewDevices).distinctBy { it.id }.sortedWith(audioDeviceComparator)
setAvailableAudioDevices(audioDevices.map(SerializableAudioDevice::fromAudioDeviceInfo)) setAvailableAudioDevices(audioDevices.map(SerializableAudioDevice::fromAudioDeviceInfo))
// This should automatically switch to a new device if it has a higher priority than the current one // This should automatically switch to a new device if it has a higher priority than the current one
selectDefaultAudioDevice(audioDevices) selectDefaultAudioDevice(audioDevices)
@ -294,7 +302,7 @@ class WebViewAudioManager(
} }
/** /**
* Returns the list of available audio devices. * Returns the list of available audio devices, sorted by likelihood of it being used for communication.
* *
* On Android 11 ([Build.VERSION_CODES.R]) and lower, it returns the list of output devices as a fallback. * On Android 11 ([Build.VERSION_CODES.R]) and lower, it returns the list of output devices as a fallback.
*/ */
@ -304,7 +312,7 @@ class WebViewAudioManager(
} else { } else {
val rawAudioDevices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS) val rawAudioDevices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
rawAudioDevices.filter { it.type in wantedDeviceTypes && it.isSink } rawAudioDevices.filter { it.type in wantedDeviceTypes && it.isSink }
} }.sortedWith(audioDeviceComparator)
} }
/** /**
@ -323,19 +331,12 @@ class WebViewAudioManager(
} }
/** /**
* Selects the default audio device based on the available devices. * Selects the default audio device based on the sorted available devices.
* *
* @param availableDevices The list of available audio devices to select from. If not provided, it will use the current list of audio devices. * @param availableDevices The list of available audio devices to select from. If not provided, it will use the current list of audio devices.
*/ */
private fun selectDefaultAudioDevice(availableDevices: List<AudioDeviceInfo> = listAudioDevices()) { private fun selectDefaultAudioDevice(availableDevices: List<AudioDeviceInfo> = listAudioDevices()) {
val selectedDevice = availableDevices val selectedDevice = availableDevices.firstOrNull()
.minByOrNull {
wantedDeviceTypes.indexOf(it.type).let { index ->
// If the device type is not in the wantedDeviceTypes list, we give it a low priority
if (index == -1) Int.MAX_VALUE else index
}
}
expectedNewCommunicationDeviceId = selectedDevice?.id expectedNewCommunicationDeviceId = selectedDevice?.id
audioManager.selectAudioDevice(selectedDevice) audioManager.selectAudioDevice(selectedDevice)
@ -385,10 +386,18 @@ class WebViewAudioManager(
currentDeviceId = device?.id currentDeviceId = device?.id
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if (device != null) { if (device != null) {
Timber.d("Setting communication device: ${device.id} - ${deviceName(device.type, device.productName.toString())}") runCatchingExceptions {
setCommunicationDevice(device) Timber.d("Setting communication device: ${device.id} - ${deviceName(device.type, device.productName.toString())}")
setCommunicationDevice(device)
}.onFailure {
Timber.e(it, "Could not set communication device.")
}
} else { } else {
audioManager.clearCommunicationDevice() runCatchingExceptions {
clearCommunicationDevice()
}.onFailure {
Timber.e(it, "Could not clear communication device.")
}
} }
} else { } else {
// On Android 11 and lower, we don't have the concept of communication devices // On Android 11 and lower, we don't have the concept of communication devices

View file

@ -155,7 +155,7 @@ class DefaultActiveCallManagerTest {
} }
@Test @Test
fun `hungUpCall - removes existing call if the CallType matches`() = runTest { fun `hangUpCall - removes existing call if the CallType matches`() = runTest {
setupShadowPowerManager() setupShadowPowerManager()
val notificationManagerCompat = mockk<NotificationManagerCompat>(relaxed = true) val notificationManagerCompat = mockk<NotificationManagerCompat>(relaxed = true)
val manager = createActiveCallManager(notificationManagerCompat = notificationManagerCompat) val manager = createActiveCallManager(notificationManagerCompat = notificationManagerCompat)
@ -165,7 +165,7 @@ class DefaultActiveCallManagerTest {
assertThat(manager.activeCall.value).isNotNull() assertThat(manager.activeCall.value).isNotNull()
assertThat(manager.activeWakeLock?.isHeld).isTrue() assertThat(manager.activeWakeLock?.isHeld).isTrue()
manager.hungUpCall(CallType.RoomCall(notificationData.sessionId, notificationData.roomId)) manager.hangUpCall(CallType.RoomCall(notificationData.sessionId, notificationData.roomId))
assertThat(manager.activeCall.value).isNull() assertThat(manager.activeCall.value).isNull()
assertThat(manager.activeWakeLock?.isHeld).isFalse() assertThat(manager.activeWakeLock?.isHeld).isFalse()
@ -192,13 +192,41 @@ class DefaultActiveCallManagerTest {
val notificationData = aCallNotificationData(roomId = A_ROOM_ID) val notificationData = aCallNotificationData(roomId = A_ROOM_ID)
manager.registerIncomingCall(notificationData) manager.registerIncomingCall(notificationData)
manager.hungUpCall(CallType.RoomCall(notificationData.sessionId, notificationData.roomId)) manager.hangUpCall(CallType.RoomCall(notificationData.sessionId, notificationData.roomId))
coVerify { coVerify {
room.declineCall(notificationEventId = notificationData.eventId) room.declineCall(notificationEventId = notificationData.eventId)
} }
} }
@Test
fun `Decline event - Hangup on a unknown call should send a decline event`() = runTest {
setupShadowPowerManager()
val notificationManagerCompat = mockk<NotificationManagerCompat>(relaxed = true)
val room = mockk<JoinedRoom>(relaxed = true)
val matrixClient = FakeMatrixClient().apply {
givenGetRoomResult(A_ROOM_ID, room)
}
val clientProvider = FakeMatrixClientProvider({ Result.success(matrixClient) })
val manager = createActiveCallManager(
matrixClientProvider = clientProvider,
notificationManagerCompat = notificationManagerCompat
)
val notificationData = aCallNotificationData(roomId = A_ROOM_ID)
// Do not register the incoming call, so the manager doesn't know about it
manager.hangUpCall(
callType = CallType.RoomCall(notificationData.sessionId, notificationData.roomId),
notificationData = notificationData,
)
coVerify {
room.declineCall(notificationEventId = notificationData.eventId)
}
}
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
@Test @Test
fun `Decline event - Declining from another session should stop ringing`() = runTest { fun `Decline event - Declining from another session should stop ringing`() = runTest {
@ -269,7 +297,7 @@ class DefaultActiveCallManagerTest {
} }
@Test @Test
fun `hungUpCall - does nothing if the CallType doesn't match`() = runTest { fun `hangUpCall - does nothing if the CallType doesn't match`() = runTest {
setupShadowPowerManager() setupShadowPowerManager()
val notificationManagerCompat = mockk<NotificationManagerCompat>(relaxed = true) val notificationManagerCompat = mockk<NotificationManagerCompat>(relaxed = true)
val manager = createActiveCallManager(notificationManagerCompat = notificationManagerCompat) val manager = createActiveCallManager(notificationManagerCompat = notificationManagerCompat)
@ -278,11 +306,12 @@ class DefaultActiveCallManagerTest {
assertThat(manager.activeCall.value).isNotNull() assertThat(manager.activeCall.value).isNotNull()
assertThat(manager.activeWakeLock?.isHeld).isTrue() assertThat(manager.activeWakeLock?.isHeld).isTrue()
manager.hungUpCall(CallType.ExternalUrl("https://example.com")) manager.hangUpCall(CallType.ExternalUrl("https://example.com"))
assertThat(manager.activeCall.value).isNotNull() assertThat(manager.activeCall.value).isNotNull()
assertThat(manager.activeWakeLock?.isHeld).isTrue() assertThat(manager.activeWakeLock?.isHeld).isTrue()
verify(exactly = 0) { notificationManagerCompat.cancel(notificationId) } // The notification is always cancelled do not block the user
verify(exactly = 1) { notificationManagerCompat.cancel(notificationId) }
} }
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)

View file

@ -17,7 +17,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
class FakeActiveCallManager( class FakeActiveCallManager(
var registerIncomingCallResult: (CallNotificationData) -> Unit = {}, var registerIncomingCallResult: (CallNotificationData) -> Unit = {},
var hungUpCallResult: (CallType) -> Unit = {}, var hangUpCallResult: (CallType, CallNotificationData?) -> Unit = { _, _ -> },
var joinedCallResult: (CallType) -> Unit = {}, var joinedCallResult: (CallType) -> Unit = {},
) : ActiveCallManager { ) : ActiveCallManager {
override val activeCall = MutableStateFlow<ActiveCall?>(null) override val activeCall = MutableStateFlow<ActiveCall?>(null)
@ -26,8 +26,8 @@ class FakeActiveCallManager(
registerIncomingCallResult(notificationData) registerIncomingCallResult(notificationData)
} }
override suspend fun hungUpCall(callType: CallType) = simulateLongTask { override suspend fun hangUpCall(callType: CallType, notificationData: CallNotificationData?) = simulateLongTask {
hungUpCallResult(callType) hangUpCallResult(callType, notificationData)
} }
override suspend fun joinedCall(callType: CallType) = simulateLongTask { override suspend fun joinedCall(callType: CallType) = simulateLongTask {

View file

@ -6,8 +6,6 @@
<string name="screen_create_room_private_option_description">"Толькі запрошаныя людзі могуць атрымаць доступ да гэтага пакоя. Усе паведамленні абаронены end-to-end шыфраваннем."</string> <string name="screen_create_room_private_option_description">"Толькі запрошаныя людзі могуць атрымаць доступ да гэтага пакоя. Усе паведамленні абаронены end-to-end шыфраваннем."</string>
<string name="screen_create_room_public_option_description">"Любы можа знайсці гэты пакой. <string name="screen_create_room_public_option_description">"Любы можа знайсці гэты пакой.
Вы можаце змяніць гэта ў любы час у наладах пакоя."</string> Вы можаце змяніць гэта ў любы час у наладах пакоя."</string>
<string name="screen_create_room_public_option_title">"Публічны пакой (для ўсіх)"</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Папрасіце далучыцца"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"Папрасіце далучыцца"</string>
<string name="screen_create_room_room_access_section_public_option_title">"Хто заўгодна"</string>
<string name="screen_create_room_topic_label">"Тэма (неабавязкова)"</string> <string name="screen_create_room_topic_label">"Тэма (неабавязкова)"</string>
</resources> </resources>

View file

@ -6,9 +6,7 @@
<string name="screen_create_room_private_option_description">"Само поканени хора имат достъп до тази стая. Всички съобщения са шифровани от край до край."</string> <string name="screen_create_room_private_option_description">"Само поканени хора имат достъп до тази стая. Всички съобщения са шифровани от край до край."</string>
<string name="screen_create_room_public_option_description">"Всеки може да намери тази стая. <string name="screen_create_room_public_option_description">"Всеки може да намери тази стая.
Можете да промените това по всяко време в настройките на стаята."</string> Можете да промените това по всяко време в настройките на стаята."</string>
<string name="screen_create_room_public_option_title">"Публична стая (всеки)"</string>
<string name="screen_create_room_room_access_section_public_option_description">"Всеки може да се присъедини към тази стая"</string> <string name="screen_create_room_room_access_section_public_option_description">"Всеки може да се присъедини към тази стая"</string>
<string name="screen_create_room_room_access_section_public_option_title">"Всеки"</string>
<string name="screen_create_room_room_address_section_footer">"За да бъде тази стая видима в директорията на общодостъпните стаи, ще ви е необходим адрес на стаята."</string> <string name="screen_create_room_room_address_section_footer">"За да бъде тази стая видима в директорията на общодостъпните стаи, ще ви е необходим адрес на стаята."</string>
<string name="screen_create_room_room_visibility_section_title">"Видимост на стаята"</string> <string name="screen_create_room_room_visibility_section_title">"Видимост на стаята"</string>
<string name="screen_create_room_topic_label">"Тема за разговор (незадължително)"</string> <string name="screen_create_room_topic_label">"Тема за разговор (незадължително)"</string>

View file

@ -8,19 +8,19 @@
<string name="screen_create_room_new_room_title">"Nová místnost"</string> <string name="screen_create_room_new_room_title">"Nová místnost"</string>
<string name="screen_create_room_new_space_title">"Nový prostor"</string> <string name="screen_create_room_new_space_title">"Nový prostor"</string>
<string name="screen_create_room_private_option_description">"Do této místnosti mohou vstoupit pouze pozvaní."</string> <string name="screen_create_room_private_option_description">"Do této místnosti mohou vstoupit pouze pozvaní."</string>
<string name="screen_create_room_private_option_title">"Soukromý"</string> <string name="screen_create_room_private_option_title">"Soukromé"</string>
<string name="screen_create_room_public_option_description">"Tuto místnost může najít kdokoli. <string name="screen_create_room_public_option_description">"Tuto místnost může najít kdokoli.
To můžete kdykoli změnit v nastavení místnosti."</string> To můžete kdykoli změnit v nastavení místnosti."</string>
<string name="screen_create_room_public_option_short_description">"Vstoupit může kdokoli."</string> <string name="screen_create_room_public_option_short_description">"Vstoupit může kdokoli."</string>
<string name="screen_create_room_public_option_title">"Veřejná místnost"</string> <string name="screen_create_room_public_option_title">"Veřejné"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"Kdokoli může požádat o vstup do místnosti, ale správce nebo moderátor bude muset žádost přijmout."</string> <string name="screen_create_room_room_access_section_knocking_option_description">"Kdokoli může požádat o vstup do místnosti, ale správce nebo moderátor bude muset žádost přijmout."</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Povolit žádost o vstup"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"Povolit žádost o vstup"</string>
<string name="screen_create_room_room_access_section_knocking_restricted_option_description">"Kdokoli v %1$s může vstoupit, ale všichni ostatní si musí o přístup požádat."</string> <string name="screen_create_room_room_access_section_knocking_restricted_option_description">"Kdokoli v %1$s může vstoupit, ale všichni ostatní si musí o přístup požádat."</string>
<string name="screen_create_room_room_access_section_knocking_restricted_option_title">"Požádat o vstup"</string> <string name="screen_create_room_room_access_section_knocking_restricted_option_title">"Požádat o vstup"</string>
<string name="screen_create_room_room_access_section_private_option_description">"Vstoupit mohou pouze pozvaní lidé."</string> <string name="screen_create_room_room_access_section_private_option_description">"Vstoupit mohou pouze pozvaní lidé."</string>
<string name="screen_create_room_room_access_section_private_option_title">"Soukromý"</string> <string name="screen_create_room_room_access_section_private_option_title">"Soukromé"</string>
<string name="screen_create_room_room_access_section_public_option_description">"Vstoupit může kdokoli."</string> <string name="screen_create_room_room_access_section_public_option_description">"Vstoupit může kdokoli."</string>
<string name="screen_create_room_room_access_section_public_option_title">"Kdokoliv"</string> <string name="screen_create_room_room_access_section_public_option_title">"Veřejné"</string>
<string name="screen_create_room_room_access_section_restricted_option_description">"Kdokoli může vstoupit do %1$s."</string> <string name="screen_create_room_room_access_section_restricted_option_description">"Kdokoli může vstoupit do %1$s."</string>
<string name="screen_create_room_room_access_section_restricted_option_title">"Standard"</string> <string name="screen_create_room_room_access_section_restricted_option_title">"Standard"</string>
<string name="screen_create_room_room_access_section_title">"Kdo má přístup"</string> <string name="screen_create_room_room_access_section_title">"Kdo má přístup"</string>
@ -28,7 +28,8 @@ To můžete kdykoli změnit v nastavení místnosti."</string>
<string name="screen_create_room_room_address_section_title">"Adresa"</string> <string name="screen_create_room_room_address_section_title">"Adresa"</string>
<string name="screen_create_room_room_visibility_section_title">"Viditelnost místnosti"</string> <string name="screen_create_room_room_visibility_section_title">"Viditelnost místnosti"</string>
<string name="screen_create_room_space_selection_no_space_description">"(bez prostoru)"</string> <string name="screen_create_room_space_selection_no_space_description">"(bez prostoru)"</string>
<string name="screen_create_room_space_selection_no_space_title">"Domov"</string> <string name="screen_create_room_space_selection_no_space_option">"Nepřidávejte do prostoru"</string>
<string name="screen_create_room_space_selection_no_space_title">"Není vybrán žádný prostor"</string>
<string name="screen_create_room_space_selection_sheet_title">"Přidat do prostoru"</string> <string name="screen_create_room_space_selection_sheet_title">"Přidat do prostoru"</string>
<string name="screen_create_room_topic_label">"Téma (nepovinné)"</string> <string name="screen_create_room_topic_label">"Téma (nepovinné)"</string>
<string name="screen_create_room_topic_placeholder">"Přidat popis…"</string> <string name="screen_create_room_topic_placeholder">"Přidat popis…"</string>

View file

@ -6,11 +6,9 @@
<string name="screen_create_room_private_option_description">"Dim ond pobl wahoddwyd all gael mynediad i\'r ystafell hon. Mae pob neges wedi\'i hamgryptio o\'r dechrau i\'r diwedd."</string> <string name="screen_create_room_private_option_description">"Dim ond pobl wahoddwyd all gael mynediad i\'r ystafell hon. Mae pob neges wedi\'i hamgryptio o\'r dechrau i\'r diwedd."</string>
<string name="screen_create_room_public_option_description">"Gall unrhyw un ddod o hyd i\'r ystafell hon. <string name="screen_create_room_public_option_description">"Gall unrhyw un ddod o hyd i\'r ystafell hon.
Gallwch newid hyn unrhyw bryd yng ngosodiadau ystafell."</string> Gallwch newid hyn unrhyw bryd yng ngosodiadau ystafell."</string>
<string name="screen_create_room_public_option_title">"Ystafell gyhoeddus"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"Gall unrhyw un ofyn am gael ymuno â\'r ystafell ond bydd rhaid i weinyddwr neu gymedrolwr dderbyn y cais"</string> <string name="screen_create_room_room_access_section_knocking_option_description">"Gall unrhyw un ofyn am gael ymuno â\'r ystafell ond bydd rhaid i weinyddwr neu gymedrolwr dderbyn y cais"</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Gofyn i gael ymuno"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"Gofyn i gael ymuno"</string>
<string name="screen_create_room_room_access_section_public_option_description">"Gall unrhyw un ymuno â\'r ystafell hon"</string> <string name="screen_create_room_room_access_section_public_option_description">"Gall unrhyw un ymuno â\'r ystafell hon"</string>
<string name="screen_create_room_room_access_section_public_option_title">"Unrhyw un"</string>
<string name="screen_create_room_room_address_section_footer">"Er mwyn i\'r ystafell hon fod yn weladwy yn y cyfeiriadur ystafelloedd cyhoeddus, bydd angen cyfeiriad ystafell arnoch."</string> <string name="screen_create_room_room_address_section_footer">"Er mwyn i\'r ystafell hon fod yn weladwy yn y cyfeiriadur ystafelloedd cyhoeddus, bydd angen cyfeiriad ystafell arnoch."</string>
<string name="screen_create_room_room_address_section_title">"Cyfeiriad yr ystafell"</string> <string name="screen_create_room_room_address_section_title">"Cyfeiriad yr ystafell"</string>
<string name="screen_create_room_room_visibility_section_title">"Gwelededd yr ystafell"</string> <string name="screen_create_room_room_visibility_section_title">"Gwelededd yr ystafell"</string>

View file

@ -8,19 +8,15 @@
<string name="screen_create_room_new_room_title">"Nyt rum"</string> <string name="screen_create_room_new_room_title">"Nyt rum"</string>
<string name="screen_create_room_new_space_title">"Ny gruppe"</string> <string name="screen_create_room_new_space_title">"Ny gruppe"</string>
<string name="screen_create_room_private_option_description">"Kun inviterede personer kan deltage."</string> <string name="screen_create_room_private_option_description">"Kun inviterede personer kan deltage."</string>
<string name="screen_create_room_private_option_title">"Privat"</string>
<string name="screen_create_room_public_option_description">"Alle kan finde dette rum. <string name="screen_create_room_public_option_description">"Alle kan finde dette rum.
Du kan ændre dette når som helst i rummets indstillinger."</string> Du kan ændre dette når som helst i rummets indstillinger."</string>
<string name="screen_create_room_public_option_short_description">"Alle kan deltage."</string> <string name="screen_create_room_public_option_short_description">"Alle kan deltage."</string>
<string name="screen_create_room_public_option_title">"Offentlig"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"Alle kan bede om at deltage i rummet, men en administrator eller en moderator skal acceptere anmodningen"</string> <string name="screen_create_room_room_access_section_knocking_option_description">"Alle kan bede om at deltage i rummet, men en administrator eller en moderator skal acceptere anmodningen"</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Tillad at man kan anmode om deltagelse"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"Tillad at man kan anmode om deltagelse"</string>
<string name="screen_create_room_room_access_section_knocking_restricted_option_description">"Enhver i %1$s kan deltage, men alle andre skal anmode om adgang."</string> <string name="screen_create_room_room_access_section_knocking_restricted_option_description">"Enhver i %1$s kan deltage, men alle andre skal anmode om adgang."</string>
<string name="screen_create_room_room_access_section_knocking_restricted_option_title">"Anmod om at deltage"</string> <string name="screen_create_room_room_access_section_knocking_restricted_option_title">"Anmod om at deltage"</string>
<string name="screen_create_room_room_access_section_private_option_description">"Kun inviterede brugere kan deltage."</string> <string name="screen_create_room_room_access_section_private_option_description">"Kun inviterede brugere kan deltage."</string>
<string name="screen_create_room_room_access_section_private_option_title">"Privat"</string>
<string name="screen_create_room_room_access_section_public_option_description">"Alle kan deltage i dette rum"</string> <string name="screen_create_room_room_access_section_public_option_description">"Alle kan deltage i dette rum"</string>
<string name="screen_create_room_room_access_section_public_option_title">"Offentlig"</string>
<string name="screen_create_room_room_access_section_restricted_option_description">"Alle i %1$s kan deltage."</string> <string name="screen_create_room_room_access_section_restricted_option_description">"Alle i %1$s kan deltage."</string>
<string name="screen_create_room_room_access_section_restricted_option_title">"Standard"</string> <string name="screen_create_room_room_access_section_restricted_option_title">"Standard"</string>
<string name="screen_create_room_room_access_section_title">"Hvem har adgang"</string> <string name="screen_create_room_room_access_section_title">"Hvem har adgang"</string>

View file

@ -8,19 +8,15 @@
<string name="screen_create_room_new_room_title">"Neuer Chat"</string> <string name="screen_create_room_new_room_title">"Neuer Chat"</string>
<string name="screen_create_room_new_space_title">"Neuer Space"</string> <string name="screen_create_room_new_space_title">"Neuer Space"</string>
<string name="screen_create_room_private_option_description">"Nur eingeladene Personen haben Zutritt zu diesem Chat."</string> <string name="screen_create_room_private_option_description">"Nur eingeladene Personen haben Zutritt zu diesem Chat."</string>
<string name="screen_create_room_private_option_title">"Privat"</string>
<string name="screen_create_room_public_option_description">"Jeder kann diesen Chat finden. <string name="screen_create_room_public_option_description">"Jeder kann diesen Chat finden.
Du kannst dies jederzeit in den Einstellungen des Chats ändern."</string> Du kannst dies jederzeit in den Einstellungen des Chats ändern."</string>
<string name="screen_create_room_public_option_short_description">"Jeder kann beitreten."</string> <string name="screen_create_room_public_option_short_description">"Jeder kann beitreten."</string>
<string name="screen_create_room_public_option_title">"Öffentlicher Chatroom"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"Jeder kann den Beitritt zum Chat erbitten, aber ein Admin oder Moderator muss die Anfrage akzeptieren."</string> <string name="screen_create_room_room_access_section_knocking_option_description">"Jeder kann den Beitritt zum Chat erbitten, aber ein Admin oder Moderator muss die Anfrage akzeptieren."</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Anfrage zum Beitritt zulassen"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"Anfrage zum Beitritt zulassen"</string>
<string name="screen_create_room_room_access_section_knocking_restricted_option_description">"Jeder in %1$s kann beitreten, aber alle anderen müssen den Beitritt anfragen."</string> <string name="screen_create_room_room_access_section_knocking_restricted_option_description">"Jeder in %1$s kann beitreten, aber alle anderen müssen den Beitritt anfragen."</string>
<string name="screen_create_room_room_access_section_knocking_restricted_option_title">"Beitritt anfragen"</string> <string name="screen_create_room_room_access_section_knocking_restricted_option_title">"Beitritt anfragen"</string>
<string name="screen_create_room_room_access_section_private_option_description">"Nur eingeladene Personen können beitreten."</string> <string name="screen_create_room_room_access_section_private_option_description">"Nur eingeladene Personen können beitreten."</string>
<string name="screen_create_room_room_access_section_private_option_title">"Privat"</string>
<string name="screen_create_room_room_access_section_public_option_description">"Jeder darf diesem Chat beitreten."</string> <string name="screen_create_room_room_access_section_public_option_description">"Jeder darf diesem Chat beitreten."</string>
<string name="screen_create_room_room_access_section_public_option_title">"Jeder"</string>
<string name="screen_create_room_room_access_section_restricted_option_description">"Jeder in %1$s kann beitreten."</string> <string name="screen_create_room_room_access_section_restricted_option_description">"Jeder in %1$s kann beitreten."</string>
<string name="screen_create_room_room_access_section_restricted_option_title">"Standard"</string> <string name="screen_create_room_room_access_section_restricted_option_title">"Standard"</string>
<string name="screen_create_room_room_access_section_title">"Wer hat Zugang"</string> <string name="screen_create_room_room_access_section_title">"Wer hat Zugang"</string>

View file

@ -6,11 +6,9 @@
<string name="screen_create_room_private_option_description">"Μόνο τα άτομα που έχουν προσκληθεί μπορούν να έχουν πρόσβαση σε αυτή την αίθουσα. Όλα τα μηνύματα είναι κρυπτογραφημένα από άκρο σε άκρο."</string> <string name="screen_create_room_private_option_description">"Μόνο τα άτομα που έχουν προσκληθεί μπορούν να έχουν πρόσβαση σε αυτή την αίθουσα. Όλα τα μηνύματα είναι κρυπτογραφημένα από άκρο σε άκρο."</string>
<string name="screen_create_room_public_option_description">"Ο καθένας μπορεί να βρει αυτή την αίθουσα. <string name="screen_create_room_public_option_description">"Ο καθένας μπορεί να βρει αυτή την αίθουσα.
Αυτό μπορείτε να το αλλάξετε ανά πάσα στιγμή στις ρυθμίσεις της αίθουσας."</string> Αυτό μπορείτε να το αλλάξετε ανά πάσα στιγμή στις ρυθμίσεις της αίθουσας."</string>
<string name="screen_create_room_public_option_title">"Δημόσιο δωμάτιο"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"Οποιοσδήποτε μπορεί να ζητήσει να συμμετάσχει στην αίθουσα, αλλά ένας διαχειριστής ή ένας συντονιστής θα πρέπει να αποδεχτεί το αίτημα"</string> <string name="screen_create_room_room_access_section_knocking_option_description">"Οποιοσδήποτε μπορεί να ζητήσει να συμμετάσχει στην αίθουσα, αλλά ένας διαχειριστής ή ένας συντονιστής θα πρέπει να αποδεχτεί το αίτημα"</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Αίτημα συμμετοχής"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"Αίτημα συμμετοχής"</string>
<string name="screen_create_room_room_access_section_public_option_description">"Οποιοσδήποτε μπορεί να συμμετάσχει σε αυτή την αίθουσα"</string> <string name="screen_create_room_room_access_section_public_option_description">"Οποιοσδήποτε μπορεί να συμμετάσχει σε αυτή την αίθουσα"</string>
<string name="screen_create_room_room_access_section_public_option_title">"Οποιοσδήποτε"</string>
<string name="screen_create_room_room_address_section_footer">"Για να είναι ορατή αυτή η αίθουσα στον δημόσιο κατάλογο αιθουσών, θα χρειαστείτε μια διεύθυνση αίθουσας."</string> <string name="screen_create_room_room_address_section_footer">"Για να είναι ορατή αυτή η αίθουσα στον δημόσιο κατάλογο αιθουσών, θα χρειαστείτε μια διεύθυνση αίθουσας."</string>
<string name="screen_create_room_room_address_section_title">"Διεύθυνση δωματίου"</string> <string name="screen_create_room_room_address_section_title">"Διεύθυνση δωματίου"</string>
<string name="screen_create_room_room_visibility_section_title">"Ορατότητα αίθουσας"</string> <string name="screen_create_room_room_visibility_section_title">"Ορατότητα αίθουσας"</string>

View file

@ -6,11 +6,9 @@
<string name="screen_create_room_private_option_description">"Solo las personas invitadas pueden acceder a esta sala. Todos los mensajes están cifrados de extremo a extremo."</string> <string name="screen_create_room_private_option_description">"Solo las personas invitadas pueden acceder a esta sala. Todos los mensajes están cifrados de extremo a extremo."</string>
<string name="screen_create_room_public_option_description">"Cualquiera puede encontrar esta sala. <string name="screen_create_room_public_option_description">"Cualquiera puede encontrar esta sala.
Puedes cambiar esto en cualquier momento en los ajustes de la sala."</string> Puedes cambiar esto en cualquier momento en los ajustes de la sala."</string>
<string name="screen_create_room_public_option_title">"Sala pública (cualquiera)"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"Cualquiera puede solicitar unirse a la sala, pero un administrador o un moderador tendrá que aceptar la solicitud"</string> <string name="screen_create_room_room_access_section_knocking_option_description">"Cualquiera puede solicitar unirse a la sala, pero un administrador o un moderador tendrá que aceptar la solicitud"</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Solicitud para unirse"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"Solicitud para unirse"</string>
<string name="screen_create_room_room_access_section_public_option_description">"Cualquiera puede unirse a esta sala"</string> <string name="screen_create_room_room_access_section_public_option_description">"Cualquiera puede unirse a esta sala"</string>
<string name="screen_create_room_room_access_section_public_option_title">"Cualquiera"</string>
<string name="screen_create_room_room_address_section_footer">"Para que esta sala sea visible en el directorio de salas públicas, necesitarás una dirección de sala."</string> <string name="screen_create_room_room_address_section_footer">"Para que esta sala sea visible en el directorio de salas públicas, necesitarás una dirección de sala."</string>
<string name="screen_create_room_room_visibility_section_title">"Visibilidad de la sala"</string> <string name="screen_create_room_room_visibility_section_title">"Visibilidad de la sala"</string>
<string name="screen_create_room_topic_label">"Tema (opcional)"</string> <string name="screen_create_room_topic_label">"Tema (opcional)"</string>

View file

@ -12,7 +12,7 @@
<string name="screen_create_room_public_option_description">"Kõik saavad seda jututuba leida. <string name="screen_create_room_public_option_description">"Kõik saavad seda jututuba leida.
Sa võid seda jututoa seadistustest alati muuta."</string> Sa võid seda jututoa seadistustest alati muuta."</string>
<string name="screen_create_room_public_option_short_description">"Kõik võivad selle jututoaga liituda."</string> <string name="screen_create_room_public_option_short_description">"Kõik võivad selle jututoaga liituda."</string>
<string name="screen_create_room_public_option_title">"Avalik jututuba"</string> <string name="screen_create_room_public_option_title">"Avalik"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"Kõik võivad paluda selle jututoaga liitumist, kuid peakasutaja või moderaator peavad selle kinnitama."</string> <string name="screen_create_room_room_access_section_knocking_option_description">"Kõik võivad paluda selle jututoaga liitumist, kuid peakasutaja või moderaator peavad selle kinnitama."</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Luba küsida liitumisvõimalust"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"Luba küsida liitumisvõimalust"</string>
<string name="screen_create_room_room_access_section_knocking_restricted_option_description">"Kõik „%1$s“ kogukonna liikmed võivad liituda, kuid kõik teised peavad liitumiseks küsima luba."</string> <string name="screen_create_room_room_access_section_knocking_restricted_option_description">"Kõik „%1$s“ kogukonna liikmed võivad liituda, kuid kõik teised peavad liitumiseks küsima luba."</string>
@ -20,7 +20,7 @@ Sa võid seda jututoa seadistustest alati muuta."</string>
<string name="screen_create_room_room_access_section_private_option_description">"Ligipääs siia jututuppa on vaid kutse alusel."</string> <string name="screen_create_room_room_access_section_private_option_description">"Ligipääs siia jututuppa on vaid kutse alusel."</string>
<string name="screen_create_room_room_access_section_private_option_title">"Privaatne"</string> <string name="screen_create_room_room_access_section_private_option_title">"Privaatne"</string>
<string name="screen_create_room_room_access_section_public_option_description">"Kõik võivad selle jututoaga liituda."</string> <string name="screen_create_room_room_access_section_public_option_description">"Kõik võivad selle jututoaga liituda."</string>
<string name="screen_create_room_room_access_section_public_option_title">"Kõik kasutajad"</string> <string name="screen_create_room_room_access_section_public_option_title">"Avalik"</string>
<string name="screen_create_room_room_access_section_restricted_option_description">"Liituda võivad kõik „%1$s“ kogukonna liikmed."</string> <string name="screen_create_room_room_access_section_restricted_option_description">"Liituda võivad kõik „%1$s“ kogukonna liikmed."</string>
<string name="screen_create_room_room_access_section_restricted_option_title">"Standardne"</string> <string name="screen_create_room_room_access_section_restricted_option_title">"Standardne"</string>
<string name="screen_create_room_room_access_section_title">"Kellel on ligipääs"</string> <string name="screen_create_room_room_access_section_title">"Kellel on ligipääs"</string>
@ -28,7 +28,8 @@ Sa võid seda jututoa seadistustest alati muuta."</string>
<string name="screen_create_room_room_address_section_title">"Aadress"</string> <string name="screen_create_room_room_address_section_title">"Aadress"</string>
<string name="screen_create_room_room_visibility_section_title">"Jututoa nähtavus"</string> <string name="screen_create_room_room_visibility_section_title">"Jututoa nähtavus"</string>
<string name="screen_create_room_space_selection_no_space_description">"(kogukonda pole)"</string> <string name="screen_create_room_space_selection_no_space_description">"(kogukonda pole)"</string>
<string name="screen_create_room_space_selection_no_space_title">"Avaleht"</string> <string name="screen_create_room_space_selection_no_space_option">"Ära lisa kogukonda"</string>
<string name="screen_create_room_space_selection_no_space_title">"Ühtegi kogukonda pole valitud"</string>
<string name="screen_create_room_space_selection_sheet_title">"Lisa kogukonda"</string> <string name="screen_create_room_space_selection_sheet_title">"Lisa kogukonda"</string>
<string name="screen_create_room_topic_label">"Teema (kui soovid lisada)"</string> <string name="screen_create_room_topic_label">"Teema (kui soovid lisada)"</string>
<string name="screen_create_room_topic_placeholder">"Lisa kirjeldus…"</string> <string name="screen_create_room_topic_placeholder">"Lisa kirjeldus…"</string>

View file

@ -6,9 +6,7 @@
<string name="screen_create_room_private_option_description">"Gonbidatutako jendea soilik sar daiteke gelara. Mezu guztiak daude ertzetik ertzera zifratuta."</string> <string name="screen_create_room_private_option_description">"Gonbidatutako jendea soilik sar daiteke gelara. Mezu guztiak daude ertzetik ertzera zifratuta."</string>
<string name="screen_create_room_public_option_description">"Edonork aurki dezake gela hau. <string name="screen_create_room_public_option_description">"Edonork aurki dezake gela hau.
Gelaren ezarpenetan aldatu dezakezu hobespena."</string> Gelaren ezarpenetan aldatu dezakezu hobespena."</string>
<string name="screen_create_room_public_option_title">"Gela publikoa"</string>
<string name="screen_create_room_room_access_section_public_option_description">"Edonor sar daiteke gela honetara"</string> <string name="screen_create_room_room_access_section_public_option_description">"Edonor sar daiteke gela honetara"</string>
<string name="screen_create_room_room_access_section_public_option_title">"Edonork"</string>
<string name="screen_create_room_room_address_section_title">"Gelaren helbidea"</string> <string name="screen_create_room_room_address_section_title">"Gelaren helbidea"</string>
<string name="screen_create_room_room_visibility_section_title">"Gelaren ikusgarritasuna"</string> <string name="screen_create_room_room_visibility_section_title">"Gelaren ikusgarritasuna"</string>
<string name="screen_create_room_topic_label">"Mintzagaia (aukerakoa)"</string> <string name="screen_create_room_topic_label">"Mintzagaia (aukerakoa)"</string>

View file

@ -4,13 +4,10 @@
<string name="screen_create_room_add_people_title">"دعوت افراد"</string> <string name="screen_create_room_add_people_title">"دعوت افراد"</string>
<string name="screen_create_room_error_creating_room">"هنگام ایجاد اتاق خطایی رخ داد"</string> <string name="screen_create_room_error_creating_room">"هنگام ایجاد اتاق خطایی رخ داد"</string>
<string name="screen_create_room_private_option_description">"تنها افراد دعوت شده می‌توانند به این اتاق دسترسی داشته باشند. همهٔ پیام‌ها رمزنگاری سرتاسری شده‌اند."</string> <string name="screen_create_room_private_option_description">"تنها افراد دعوت شده می‌توانند به این اتاق دسترسی داشته باشند. همهٔ پیام‌ها رمزنگاری سرتاسری شده‌اند."</string>
<string name="screen_create_room_private_option_title">"اتاق خصوصی"</string>
<string name="screen_create_room_public_option_description">"هرکسی می‌تواند اتاق را بیابد. <string name="screen_create_room_public_option_description">"هرکسی می‌تواند اتاق را بیابد.
می‌توانید بعداً در تظیمات اتاق عوضش کنید."</string> می‌توانید بعداً در تظیمات اتاق عوضش کنید."</string>
<string name="screen_create_room_public_option_title">"اتاق عمومی (هرکسی)"</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"درخواست دعوت"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"درخواست دعوت"</string>
<string name="screen_create_room_room_access_section_public_option_description">"هرکسی می‌تواند به این اتاق بپیوندد"</string> <string name="screen_create_room_room_access_section_public_option_description">"هرکسی می‌تواند به این اتاق بپیوندد"</string>
<string name="screen_create_room_room_access_section_public_option_title">"هرکسی"</string>
<string name="screen_create_room_room_address_section_title">"نشانی اتاق"</string> <string name="screen_create_room_room_address_section_title">"نشانی اتاق"</string>
<string name="screen_create_room_room_visibility_section_title">"نمایانی اتاق"</string> <string name="screen_create_room_room_visibility_section_title">"نمایانی اتاق"</string>
<string name="screen_create_room_topic_label">"موضوع (اختیاری)"</string> <string name="screen_create_room_topic_label">"موضوع (اختیاری)"</string>

View file

@ -6,11 +6,9 @@
<string name="screen_create_room_private_option_description">"Vain kutsutut henkilöt pääsevät tähän huoneeseen. Kaikki viestit ovat päästä päähän salattuja."</string> <string name="screen_create_room_private_option_description">"Vain kutsutut henkilöt pääsevät tähän huoneeseen. Kaikki viestit ovat päästä päähän salattuja."</string>
<string name="screen_create_room_public_option_description">"Kuka tahansa voi löytää tämän huoneen. <string name="screen_create_room_public_option_description">"Kuka tahansa voi löytää tämän huoneen.
Voit muuttaa tämän milloin tahansa huoneen asetuksista."</string> Voit muuttaa tämän milloin tahansa huoneen asetuksista."</string>
<string name="screen_create_room_public_option_title">"Julkinen huone"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"Kuka tahansa voi pyytää saada liittyä huoneeseen, mutta ylläpitäjän tai valvojan on hyväksyttävä pyyntö"</string> <string name="screen_create_room_room_access_section_knocking_option_description">"Kuka tahansa voi pyytää saada liittyä huoneeseen, mutta ylläpitäjän tai valvojan on hyväksyttävä pyyntö"</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Pyydä liittymistä"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"Pyydä liittymistä"</string>
<string name="screen_create_room_room_access_section_public_option_description">"Kuka tahansa voi liittyä tähän huoneeseen"</string> <string name="screen_create_room_room_access_section_public_option_description">"Kuka tahansa voi liittyä tähän huoneeseen"</string>
<string name="screen_create_room_room_access_section_public_option_title">"Kuka tahansa"</string>
<string name="screen_create_room_room_address_section_footer">"Jotta tämä huone näkyisi julkisessa huonehakemistossa, tarvitset huoneen osoitteen."</string> <string name="screen_create_room_room_address_section_footer">"Jotta tämä huone näkyisi julkisessa huonehakemistossa, tarvitset huoneen osoitteen."</string>
<string name="screen_create_room_room_address_section_title">"Huoneen osoite"</string> <string name="screen_create_room_room_address_section_title">"Huoneen osoite"</string>
<string name="screen_create_room_room_visibility_section_title">"Huoneen näkyvyys"</string> <string name="screen_create_room_room_visibility_section_title">"Huoneen näkyvyys"</string>

View file

@ -12,7 +12,7 @@
<string name="screen_create_room_public_option_description">"Nimporte qui peut trouver ce salon. <string name="screen_create_room_public_option_description">"Nimporte qui peut trouver ce salon.
Vous pouvez modifier cela à tout moment dans les paramètres du salon."</string> Vous pouvez modifier cela à tout moment dans les paramètres du salon."</string>
<string name="screen_create_room_public_option_short_description">"Tout le monde peut joindre"</string> <string name="screen_create_room_public_option_short_description">"Tout le monde peut joindre"</string>
<string name="screen_create_room_public_option_title">"Salon public"</string> <string name="screen_create_room_public_option_title">"Public"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"Tout le monde peut demander à joindre, mais un administrateur ou un modérateur devra accepter la demande"</string> <string name="screen_create_room_room_access_section_knocking_option_description">"Tout le monde peut demander à joindre, mais un administrateur ou un modérateur devra accepter la demande"</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Autoriser la demande à joindre"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"Autoriser la demande à joindre"</string>
<string name="screen_create_room_room_access_section_knocking_restricted_option_description">"Tout membre de %1$s peut joindre, mais les autres doivent demander un accès."</string> <string name="screen_create_room_room_access_section_knocking_restricted_option_description">"Tout membre de %1$s peut joindre, mais les autres doivent demander un accès."</string>

View file

@ -9,7 +9,6 @@ To možete u svakom trenutku promijeniti u postavkama sobe."</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"Svatko može zatražiti pridruživanje sobi, ali administrator ili moderator morat će prihvatiti zahtjev."</string> <string name="screen_create_room_room_access_section_knocking_option_description">"Svatko može zatražiti pridruživanje sobi, ali administrator ili moderator morat će prihvatiti zahtjev."</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Zatraži pridruživanje"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"Zatraži pridruživanje"</string>
<string name="screen_create_room_room_access_section_public_option_description">"Svatko se može pridružiti ovoj sobi"</string> <string name="screen_create_room_room_access_section_public_option_description">"Svatko se može pridružiti ovoj sobi"</string>
<string name="screen_create_room_room_access_section_public_option_title">"Svatko"</string>
<string name="screen_create_room_room_address_section_footer">"Da bi ova soba bila vidljiva u javnom direktoriju soba, trebat će vam adresa sobe."</string> <string name="screen_create_room_room_address_section_footer">"Da bi ova soba bila vidljiva u javnom direktoriju soba, trebat će vam adresa sobe."</string>
<string name="screen_create_room_room_address_section_title">"Adresa sobe"</string> <string name="screen_create_room_room_address_section_title">"Adresa sobe"</string>
<string name="screen_create_room_room_visibility_section_title">"Vidljivost sobe"</string> <string name="screen_create_room_room_visibility_section_title">"Vidljivost sobe"</string>

View file

@ -8,19 +8,15 @@
<string name="screen_create_room_new_room_title">"Új szoba"</string> <string name="screen_create_room_new_room_title">"Új szoba"</string>
<string name="screen_create_room_new_space_title">"Új tér"</string> <string name="screen_create_room_new_space_title">"Új tér"</string>
<string name="screen_create_room_private_option_description">"Csak a meghívottak léphetnek be."</string> <string name="screen_create_room_private_option_description">"Csak a meghívottak léphetnek be."</string>
<string name="screen_create_room_private_option_title">"Privát"</string>
<string name="screen_create_room_public_option_description">"Bárki megtalálhatja ezt a szobát. <string name="screen_create_room_public_option_description">"Bárki megtalálhatja ezt a szobát.
Ezt bármikor módosíthatja a szobabeállításokban."</string> Ezt bármikor módosíthatja a szobabeállításokban."</string>
<string name="screen_create_room_public_option_short_description">"Bárki csatlakozhat."</string> <string name="screen_create_room_public_option_short_description">"Bárki csatlakozhat."</string>
<string name="screen_create_room_public_option_title">"Nyilvános szoba"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"Bárki kérheti, hogy csatlakozhasson a szobához, de egy adminisztrátornak vagy moderátornak el kell fogadnia a kérést."</string> <string name="screen_create_room_room_access_section_knocking_option_description">"Bárki kérheti, hogy csatlakozhasson a szobához, de egy adminisztrátornak vagy moderátornak el kell fogadnia a kérést."</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Csatlakozás kérése"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"Csatlakozás kérése"</string>
<string name="screen_create_room_room_access_section_knocking_restricted_option_description">"Bárki csatlakozhat innen: %1$s, de mindenki másnak hozzáférést kell kérnie."</string> <string name="screen_create_room_room_access_section_knocking_restricted_option_description">"Bárki csatlakozhat innen: %1$s, de mindenki másnak hozzáférést kell kérnie."</string>
<string name="screen_create_room_room_access_section_knocking_restricted_option_title">"Csatlakozás kérése"</string> <string name="screen_create_room_room_access_section_knocking_restricted_option_title">"Csatlakozás kérése"</string>
<string name="screen_create_room_room_access_section_private_option_description">"Csak a meghívottak léphetnek be."</string> <string name="screen_create_room_room_access_section_private_option_description">"Csak a meghívottak léphetnek be."</string>
<string name="screen_create_room_room_access_section_private_option_title">"Privát"</string>
<string name="screen_create_room_room_access_section_public_option_description">"Bárki csatlakozhat."</string> <string name="screen_create_room_room_access_section_public_option_description">"Bárki csatlakozhat."</string>
<string name="screen_create_room_room_access_section_public_option_title">"Nyilvános"</string>
<string name="screen_create_room_room_access_section_restricted_option_description">"Bárki csatlakozhat innen: %1$s."</string> <string name="screen_create_room_room_access_section_restricted_option_description">"Bárki csatlakozhat innen: %1$s."</string>
<string name="screen_create_room_room_access_section_restricted_option_title">"Szokásos"</string> <string name="screen_create_room_room_access_section_restricted_option_title">"Szokásos"</string>
<string name="screen_create_room_room_access_section_title">"Hozzáférésre jogosultak"</string> <string name="screen_create_room_room_access_section_title">"Hozzáférésre jogosultak"</string>

View file

@ -6,11 +6,9 @@
<string name="screen_create_room_private_option_description">"Hanya orang-orang yang diundang dapat mengakses ruangan ini. Semua pesan terenkripsi secara ujung ke ujung."</string> <string name="screen_create_room_private_option_description">"Hanya orang-orang yang diundang dapat mengakses ruangan ini. Semua pesan terenkripsi secara ujung ke ujung."</string>
<string name="screen_create_room_public_option_description">"Siapa pun dapat mencari ruangan ini. <string name="screen_create_room_public_option_description">"Siapa pun dapat mencari ruangan ini.
Anda dapat mengubah ini kapan pun dalam pengaturan ruangan."</string> Anda dapat mengubah ini kapan pun dalam pengaturan ruangan."</string>
<string name="screen_create_room_public_option_title">"Ruangan publik"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"Siapa pun dapat meminta untuk bergabung dengan ruangan tetapi administrator atau moderator harus menerima permintaan tersebut"</string> <string name="screen_create_room_room_access_section_knocking_option_description">"Siapa pun dapat meminta untuk bergabung dengan ruangan tetapi administrator atau moderator harus menerima permintaan tersebut"</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Minta untuk bergabung"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"Minta untuk bergabung"</string>
<string name="screen_create_room_room_access_section_public_option_description">"Siapa pun dapat bergabung dengan ruangan ini"</string> <string name="screen_create_room_room_access_section_public_option_description">"Siapa pun dapat bergabung dengan ruangan ini"</string>
<string name="screen_create_room_room_access_section_public_option_title">"Siapa pun"</string>
<string name="screen_create_room_room_address_section_footer">"Supaya ruangan ini terlihat di direktori ruangan publik, Anda memerlukan alamat ruangan."</string> <string name="screen_create_room_room_address_section_footer">"Supaya ruangan ini terlihat di direktori ruangan publik, Anda memerlukan alamat ruangan."</string>
<string name="screen_create_room_room_address_section_title">"Alamat ruangan"</string> <string name="screen_create_room_room_address_section_title">"Alamat ruangan"</string>
<string name="screen_create_room_room_visibility_section_title">"Keterlihatan ruangan"</string> <string name="screen_create_room_room_visibility_section_title">"Keterlihatan ruangan"</string>

View file

@ -6,11 +6,9 @@
<string name="screen_create_room_private_option_description">"Solo le persone invitate possono accedere a questa stanza. Tutti i messaggi sono cifrati end-to-end."</string> <string name="screen_create_room_private_option_description">"Solo le persone invitate possono accedere a questa stanza. Tutti i messaggi sono cifrati end-to-end."</string>
<string name="screen_create_room_public_option_description">"Chiunque può trovare questa stanza. <string name="screen_create_room_public_option_description">"Chiunque può trovare questa stanza.
Puoi modificarlo in qualsiasi momento nelle impostazioni della stanza."</string> Puoi modificarlo in qualsiasi momento nelle impostazioni della stanza."</string>
<string name="screen_create_room_public_option_title">"Stanza pubblica"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"Chiunque può chiedere di entrare nella stanza, ma un amministratore o un moderatore dovrà accettare la richiesta"</string> <string name="screen_create_room_room_access_section_knocking_option_description">"Chiunque può chiedere di entrare nella stanza, ma un amministratore o un moderatore dovrà accettare la richiesta"</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Chiedi di entrare"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"Chiedi di entrare"</string>
<string name="screen_create_room_room_access_section_public_option_description">"Chiunque può entrare in questa stanza"</string> <string name="screen_create_room_room_access_section_public_option_description">"Chiunque può entrare in questa stanza"</string>
<string name="screen_create_room_room_access_section_public_option_title">"Chiunque"</string>
<string name="screen_create_room_room_address_section_footer">"Affinché questa stanza sia visibile nell\'elenco delle stanze pubbliche, è necessario un indirizzo della stanza."</string> <string name="screen_create_room_room_address_section_footer">"Affinché questa stanza sia visibile nell\'elenco delle stanze pubbliche, è necessario un indirizzo della stanza."</string>
<string name="screen_create_room_room_address_section_title">"Indirizzo della stanza"</string> <string name="screen_create_room_room_address_section_title">"Indirizzo della stanza"</string>
<string name="screen_create_room_room_visibility_section_title">"Visibilità della stanza"</string> <string name="screen_create_room_room_visibility_section_title">"Visibilità della stanza"</string>

View file

@ -6,6 +6,5 @@
<string name="screen_create_room_private_option_description">"ამ ოთახში შეტყობინებები დაშიფრულია. შემდგომ დაშიფვრის გამორთვა შეუძლებელია."</string> <string name="screen_create_room_private_option_description">"ამ ოთახში შეტყობინებები დაშიფრულია. შემდგომ დაშიფვრის გამორთვა შეუძლებელია."</string>
<string name="screen_create_room_public_option_description">"ყველას ამ ოთახის მოძებნა შეუძლია. <string name="screen_create_room_public_option_description">"ყველას ამ ოთახის მოძებნა შეუძლია.
თქვენ ნებისმიერ დროს შეგიძლიათ ამის შეცვლა ოთახის პარამეტრებში."</string> თქვენ ნებისმიერ დროს შეგიძლიათ ამის შეცვლა ოთახის პარამეტრებში."</string>
<string name="screen_create_room_public_option_title">"საჯარო ოთახი"</string>
<string name="screen_create_room_topic_label">"თემა (სურვილისამებრ)"</string> <string name="screen_create_room_topic_label">"თემა (სურვილისამებრ)"</string>
</resources> </resources>

View file

@ -6,11 +6,9 @@
<string name="screen_create_room_private_option_description">"초대받은 사람만 이 방에 액세스할 수 있습니다. 모든 메시지는 종단 간 암호화됩니다."</string> <string name="screen_create_room_private_option_description">"초대받은 사람만 이 방에 액세스할 수 있습니다. 모든 메시지는 종단 간 암호화됩니다."</string>
<string name="screen_create_room_public_option_description">"누구나 이 방을 찾을 수 있습니다. <string name="screen_create_room_public_option_description">"누구나 이 방을 찾을 수 있습니다.
방 설정에서 언제든지 변경할 수 있습니다."</string> 방 설정에서 언제든지 변경할 수 있습니다."</string>
<string name="screen_create_room_public_option_title">"공개 방 (모두)"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"누구나 방에 참여 요청을 할 수 있지만, 관리자나 운영자가 요청을 수락해야 합니다."</string> <string name="screen_create_room_room_access_section_knocking_option_description">"누구나 방에 참여 요청을 할 수 있지만, 관리자나 운영자가 요청을 수락해야 합니다."</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"참가 요청"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"참가 요청"</string>
<string name="screen_create_room_room_access_section_public_option_description">"누구나 이 방에 참여할 수 있습니다."</string> <string name="screen_create_room_room_access_section_public_option_description">"누구나 이 방에 참여할 수 있습니다."</string>
<string name="screen_create_room_room_access_section_public_option_title">"누구나"</string>
<string name="screen_create_room_room_address_section_footer">"이 방이 공개 방 디렉토리에 표시되려면 방 주소가 필요합니다."</string> <string name="screen_create_room_room_address_section_footer">"이 방이 공개 방 디렉토리에 표시되려면 방 주소가 필요합니다."</string>
<string name="screen_create_room_room_visibility_section_title">"방 표시 여부"</string> <string name="screen_create_room_room_visibility_section_title">"방 표시 여부"</string>
<string name="screen_create_room_topic_label">"주제 (선택)"</string> <string name="screen_create_room_topic_label">"주제 (선택)"</string>

View file

@ -7,17 +7,13 @@
<string name="screen_create_room_new_room_title">"Nytt rom"</string> <string name="screen_create_room_new_room_title">"Nytt rom"</string>
<string name="screen_create_room_new_space_title">"Nytt område"</string> <string name="screen_create_room_new_space_title">"Nytt område"</string>
<string name="screen_create_room_private_option_description">"Bare inviterte personer kan bli med."</string> <string name="screen_create_room_private_option_description">"Bare inviterte personer kan bli med."</string>
<string name="screen_create_room_private_option_title">"Privat"</string>
<string name="screen_create_room_public_option_description">"Alle kan finne dette rommet. <string name="screen_create_room_public_option_description">"Alle kan finne dette rommet.
Du kan endre dette når som helst i rominnstillingene."</string> Du kan endre dette når som helst i rominnstillingene."</string>
<string name="screen_create_room_public_option_short_description">"Alle kan bli med."</string> <string name="screen_create_room_public_option_short_description">"Alle kan bli med."</string>
<string name="screen_create_room_public_option_title">"Offentlig rom"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"Alle kan be om å få bli med, men en administrator eller moderator må godta forespørselen."</string> <string name="screen_create_room_room_access_section_knocking_option_description">"Alle kan be om å få bli med, men en administrator eller moderator må godta forespørselen."</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Be om å bli med"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"Be om å bli med"</string>
<string name="screen_create_room_room_access_section_private_option_description">"Bare inviterte personer kan bli med."</string> <string name="screen_create_room_room_access_section_private_option_description">"Bare inviterte personer kan bli med."</string>
<string name="screen_create_room_room_access_section_private_option_title">"Privat"</string>
<string name="screen_create_room_room_access_section_public_option_description">"Alle kan bli med."</string> <string name="screen_create_room_room_access_section_public_option_description">"Alle kan bli med."</string>
<string name="screen_create_room_room_access_section_public_option_title">"Alle"</string>
<string name="screen_create_room_room_access_section_title">"Hvem har tilgang"</string> <string name="screen_create_room_room_access_section_title">"Hvem har tilgang"</string>
<string name="screen_create_room_room_address_section_footer">"Du trenger en adresse for å gjøre den synlig i den offentlige katalogen."</string> <string name="screen_create_room_room_address_section_footer">"Du trenger en adresse for å gjøre den synlig i den offentlige katalogen."</string>
<string name="screen_create_room_room_address_section_title">"Adresse"</string> <string name="screen_create_room_room_address_section_title">"Adresse"</string>

View file

@ -6,10 +6,8 @@
<string name="screen_create_room_private_option_description">"Alleen uitgenodigde personen hebben toegang tot deze kamer. Alle berichten zijn end-to-end versleuteld."</string> <string name="screen_create_room_private_option_description">"Alleen uitgenodigde personen hebben toegang tot deze kamer. Alle berichten zijn end-to-end versleuteld."</string>
<string name="screen_create_room_public_option_description">"Iedereen kan deze kamer vinden. <string name="screen_create_room_public_option_description">"Iedereen kan deze kamer vinden.
Je kunt dit op elk gewenst moment wijzigen in de kamerinstellingen."</string> Je kunt dit op elk gewenst moment wijzigen in de kamerinstellingen."</string>
<string name="screen_create_room_public_option_title">"Openbare kamer"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"Iedereen kan vragen om toe te treden tot de kamer, maar een beheerder of moderator moet het verzoek accepteren"</string> <string name="screen_create_room_room_access_section_knocking_option_description">"Iedereen kan vragen om toe te treden tot de kamer, maar een beheerder of moderator moet het verzoek accepteren"</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Vraag om toe te treden"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"Vraag om toe te treden"</string>
<string name="screen_create_room_room_access_section_public_option_description">"Iedereen kan toetreden tot deze kamer"</string> <string name="screen_create_room_room_access_section_public_option_description">"Iedereen kan toetreden tot deze kamer"</string>
<string name="screen_create_room_room_access_section_public_option_title">"Iedereen"</string>
<string name="screen_create_room_topic_label">"Onderwerp (optioneel)"</string> <string name="screen_create_room_topic_label">"Onderwerp (optioneel)"</string>
</resources> </resources>

View file

@ -6,11 +6,9 @@
<string name="screen_create_room_private_option_description">"Tylko zaproszone osoby mogą dołączyć do tego pokoju. Wszystkie wiadomości są szyfrowane end-to-end."</string> <string name="screen_create_room_private_option_description">"Tylko zaproszone osoby mogą dołączyć do tego pokoju. Wszystkie wiadomości są szyfrowane end-to-end."</string>
<string name="screen_create_room_public_option_description">"Każdy może znaleźć ten pokój. <string name="screen_create_room_public_option_description">"Każdy może znaleźć ten pokój.
Możesz to zmienić w ustawieniach pokoju."</string> Możesz to zmienić w ustawieniach pokoju."</string>
<string name="screen_create_room_public_option_title">"Pokój publiczny"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"Każdy może poprosić o dołączenie do pokoju, ale administrator lub moderator będzie musiał zatwierdzić prośbę"</string> <string name="screen_create_room_room_access_section_knocking_option_description">"Każdy może poprosić o dołączenie do pokoju, ale administrator lub moderator będzie musiał zatwierdzić prośbę"</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Poproś o dołączenie"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"Poproś o dołączenie"</string>
<string name="screen_create_room_room_access_section_public_option_description">"Każdy może dołączyć do tego pokoju"</string> <string name="screen_create_room_room_access_section_public_option_description">"Każdy może dołączyć do tego pokoju"</string>
<string name="screen_create_room_room_access_section_public_option_title">"Wszyscy"</string>
<string name="screen_create_room_room_address_section_footer">"Aby ten pokój był widoczny w katalogu pomieszczeń publicznych, będziesz potrzebował adres pokoju."</string> <string name="screen_create_room_room_address_section_footer">"Aby ten pokój był widoczny w katalogu pomieszczeń publicznych, będziesz potrzebował adres pokoju."</string>
<string name="screen_create_room_room_address_section_title">"Adres pokoju"</string> <string name="screen_create_room_room_address_section_title">"Adres pokoju"</string>
<string name="screen_create_room_room_visibility_section_title">"Widoczność pomieszczenia"</string> <string name="screen_create_room_room_visibility_section_title">"Widoczność pomieszczenia"</string>

View file

@ -8,19 +8,15 @@
<string name="screen_create_room_new_room_title">"Nova sala"</string> <string name="screen_create_room_new_room_title">"Nova sala"</string>
<string name="screen_create_room_new_space_title">"Novo espaço"</string> <string name="screen_create_room_new_space_title">"Novo espaço"</string>
<string name="screen_create_room_private_option_description">"Apenas pessoas convidadas podem entrar."</string> <string name="screen_create_room_private_option_description">"Apenas pessoas convidadas podem entrar."</string>
<string name="screen_create_room_private_option_title">"Privada"</string>
<string name="screen_create_room_public_option_description">"Qualquer um pode encontrar esta sala. <string name="screen_create_room_public_option_description">"Qualquer um pode encontrar esta sala.
Você pode mudar isso a qualquer momento nas configurações da sala."</string> Você pode mudar isso a qualquer momento nas configurações da sala."</string>
<string name="screen_create_room_public_option_short_description">"Qualquer um pode entrar."</string> <string name="screen_create_room_public_option_short_description">"Qualquer um pode entrar."</string>
<string name="screen_create_room_public_option_title">"Publica"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"Qualquer um pode pedir para entrar, mas um administrador ou moderador deve aceitar a solicitação"</string> <string name="screen_create_room_room_access_section_knocking_option_description">"Qualquer um pode pedir para entrar, mas um administrador ou moderador deve aceitar a solicitação"</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Permitir pedir para entrar"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"Permitir pedir para entrar"</string>
<string name="screen_create_room_room_access_section_knocking_restricted_option_description">"Qualquer um em %1$s pode entrar, mas todos os outros devem pedir acesso."</string> <string name="screen_create_room_room_access_section_knocking_restricted_option_description">"Qualquer um em %1$s pode entrar, mas todos os outros devem pedir acesso."</string>
<string name="screen_create_room_room_access_section_knocking_restricted_option_title">"Pedir para entrar"</string> <string name="screen_create_room_room_access_section_knocking_restricted_option_title">"Pedir para entrar"</string>
<string name="screen_create_room_room_access_section_private_option_description">"Apenas pessoas convidadas podem entrar."</string> <string name="screen_create_room_room_access_section_private_option_description">"Apenas pessoas convidadas podem entrar."</string>
<string name="screen_create_room_room_access_section_private_option_title">"Privada"</string>
<string name="screen_create_room_room_access_section_public_option_description">"Qualquer um pode entrar."</string> <string name="screen_create_room_room_access_section_public_option_description">"Qualquer um pode entrar."</string>
<string name="screen_create_room_room_access_section_public_option_title">"Qualquer pessoa"</string>
<string name="screen_create_room_room_access_section_restricted_option_description">"Qualquer um em %1$s pode participar."</string> <string name="screen_create_room_room_access_section_restricted_option_description">"Qualquer um em %1$s pode participar."</string>
<string name="screen_create_room_room_access_section_restricted_option_title">"Normal"</string> <string name="screen_create_room_room_access_section_restricted_option_title">"Normal"</string>
<string name="screen_create_room_room_access_section_title">"Quem tem acesso"</string> <string name="screen_create_room_room_access_section_title">"Quem tem acesso"</string>

View file

@ -4,14 +4,11 @@
<string name="screen_create_room_add_people_title">"Convidar pessoas"</string> <string name="screen_create_room_add_people_title">"Convidar pessoas"</string>
<string name="screen_create_room_error_creating_room">"Ocorreu um erro ao criar a sala"</string> <string name="screen_create_room_error_creating_room">"Ocorreu um erro ao criar a sala"</string>
<string name="screen_create_room_private_option_description">"Apenas as pessoas convidadas podem entrar."</string> <string name="screen_create_room_private_option_description">"Apenas as pessoas convidadas podem entrar."</string>
<string name="screen_create_room_private_option_title">"Privada"</string>
<string name="screen_create_room_public_option_description">"Qualquer um pode encontrar esta sala. <string name="screen_create_room_public_option_description">"Qualquer um pode encontrar esta sala.
Pode alterar esta opção nas definições da sala."</string> Pode alterar esta opção nas definições da sala."</string>
<string name="screen_create_room_public_option_title">"Sala pública"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"Qualquer pessoa pode pedir para entrar, mas um administrador ou um moderador terá de aceitar o pedido."</string> <string name="screen_create_room_room_access_section_knocking_option_description">"Qualquer pessoa pode pedir para entrar, mas um administrador ou um moderador terá de aceitar o pedido."</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Pedir para participar"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"Pedir para participar"</string>
<string name="screen_create_room_room_access_section_public_option_description">"Qualquer pessoa pode entrar."</string> <string name="screen_create_room_room_access_section_public_option_description">"Qualquer pessoa pode entrar."</string>
<string name="screen_create_room_room_access_section_public_option_title">"Pública"</string>
<string name="screen_create_room_room_address_section_footer">"Para que esta sala seja visível no diretório público, precisa de ter um endereço."</string> <string name="screen_create_room_room_address_section_footer">"Para que esta sala seja visível no diretório público, precisa de ter um endereço."</string>
<string name="screen_create_room_room_address_section_title">"Endereço"</string> <string name="screen_create_room_room_address_section_title">"Endereço"</string>
<string name="screen_create_room_room_visibility_section_title">"Visibilidade da sala"</string> <string name="screen_create_room_room_visibility_section_title">"Visibilidade da sala"</string>

View file

@ -6,11 +6,9 @@
<string name="screen_create_room_private_option_description">"Doar persoanele invitate pot accesa această cameră. Toate mesajele sunt criptate end-to-end."</string> <string name="screen_create_room_private_option_description">"Doar persoanele invitate pot accesa această cameră. Toate mesajele sunt criptate end-to-end."</string>
<string name="screen_create_room_public_option_description">"Oricine poate găsi această cameră. <string name="screen_create_room_public_option_description">"Oricine poate găsi această cameră.
Puteți modifica acest lucru oricând în setări."</string> Puteți modifica acest lucru oricând în setări."</string>
<string name="screen_create_room_public_option_title">"Cameră publică"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"Oricine poate cere să se alăture camerei, dar un administrator sau un moderator va trebui să accepte cererea"</string> <string name="screen_create_room_room_access_section_knocking_option_description">"Oricine poate cere să se alăture camerei, dar un administrator sau un moderator va trebui să accepte cererea"</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Cereți să vă alăturați"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"Cereți să vă alăturați"</string>
<string name="screen_create_room_room_access_section_public_option_description">"Oricine se poate alătura acestei camere"</string> <string name="screen_create_room_room_access_section_public_option_description">"Oricine se poate alătura acestei camere"</string>
<string name="screen_create_room_room_access_section_public_option_title">"Oricine"</string>
<string name="screen_create_room_room_address_section_footer">"Pentru ca această cameră să fie vizibilă în directorul de camere publice, veți avea nevoie de o adresă de cameră."</string> <string name="screen_create_room_room_address_section_footer">"Pentru ca această cameră să fie vizibilă în directorul de camere publice, veți avea nevoie de o adresă de cameră."</string>
<string name="screen_create_room_room_address_section_title">"Adresa camerei"</string> <string name="screen_create_room_room_address_section_title">"Adresa camerei"</string>
<string name="screen_create_room_room_visibility_section_title">"Vizibilitatea camerei"</string> <string name="screen_create_room_room_visibility_section_title">"Vizibilitatea camerei"</string>

View file

@ -8,17 +8,13 @@
<string name="screen_create_room_new_room_title">"Новая комната"</string> <string name="screen_create_room_new_room_title">"Новая комната"</string>
<string name="screen_create_room_new_space_title">"Новое пространство"</string> <string name="screen_create_room_new_space_title">"Новое пространство"</string>
<string name="screen_create_room_private_option_description">"Присоединиться могут только приглашенные."</string> <string name="screen_create_room_private_option_description">"Присоединиться могут только приглашенные."</string>
<string name="screen_create_room_private_option_title">"Частный"</string>
<string name="screen_create_room_public_option_description">"Любой желающий может найти эту комнату. <string name="screen_create_room_public_option_description">"Любой желающий может найти эту комнату.
Вы можете изменить это в любое время в настройках комнаты."</string> Вы можете изменить это в любое время в настройках комнаты."</string>
<string name="screen_create_room_public_option_short_description">"Присоединиться может любой."</string> <string name="screen_create_room_public_option_short_description">"Присоединиться может любой."</string>
<string name="screen_create_room_public_option_title">"Общедоступная комната"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"Любой желающий может подать заявку на присоединение к комнате, но администратор или модератор должен будет принять запрос."</string> <string name="screen_create_room_room_access_section_knocking_option_description">"Любой желающий может подать заявку на присоединение к комнате, но администратор или модератор должен будет принять запрос."</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Разрешить запрос на присоединение"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"Разрешить запрос на присоединение"</string>
<string name="screen_create_room_room_access_section_private_option_description">"Присоединиться могут только приглашенные."</string> <string name="screen_create_room_room_access_section_private_option_description">"Присоединиться могут только приглашенные."</string>
<string name="screen_create_room_room_access_section_private_option_title">"Частный"</string>
<string name="screen_create_room_room_access_section_public_option_description">"Присоединиться может любой желающий."</string> <string name="screen_create_room_room_access_section_public_option_description">"Присоединиться может любой желающий."</string>
<string name="screen_create_room_room_access_section_public_option_title">"Публичный"</string>
<string name="screen_create_room_room_access_section_title">"Кто имеет доступ"</string> <string name="screen_create_room_room_access_section_title">"Кто имеет доступ"</string>
<string name="screen_create_room_room_address_section_footer">"Вам понадобится адрес комнаты, чтобы сделать ее видимой в каталоге."</string> <string name="screen_create_room_room_address_section_footer">"Вам понадобится адрес комнаты, чтобы сделать ее видимой в каталоге."</string>
<string name="screen_create_room_room_address_section_title">"Адрес"</string> <string name="screen_create_room_room_address_section_title">"Адрес"</string>

View file

@ -8,19 +8,15 @@
<string name="screen_create_room_new_room_title">"Nová miestnosť"</string> <string name="screen_create_room_new_room_title">"Nová miestnosť"</string>
<string name="screen_create_room_new_space_title">"Nový priestor"</string> <string name="screen_create_room_new_space_title">"Nový priestor"</string>
<string name="screen_create_room_private_option_description">"Pripojiť sa môžu iba pozvaní ľudia."</string> <string name="screen_create_room_private_option_description">"Pripojiť sa môžu iba pozvaní ľudia."</string>
<string name="screen_create_room_private_option_title">"Súkromná"</string>
<string name="screen_create_room_public_option_description">"Túto miestnosť môže nájsť ktokoľvek. <string name="screen_create_room_public_option_description">"Túto miestnosť môže nájsť ktokoľvek.
Môžete to kedykoľvek zmeniť v nastaveniach miestnosti."</string> Môžete to kedykoľvek zmeniť v nastaveniach miestnosti."</string>
<string name="screen_create_room_public_option_short_description">"Pripojiť sa môže ktokoľvek."</string> <string name="screen_create_room_public_option_short_description">"Pripojiť sa môže ktokoľvek."</string>
<string name="screen_create_room_public_option_title">"Verejná miestnosť"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"O pripojenie sa môže požiadať ktokoľvek, ale žiadosť musí schváliť správca alebo moderátor."</string> <string name="screen_create_room_room_access_section_knocking_option_description">"O pripojenie sa môže požiadať ktokoľvek, ale žiadosť musí schváliť správca alebo moderátor."</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Povoliť požiadať o vstup"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"Povoliť požiadať o vstup"</string>
<string name="screen_create_room_room_access_section_knocking_restricted_option_description">"Ktokoľvek v %1$s sa môžu pripojiť, ale všetci ostatní musia požiadať o prístup."</string> <string name="screen_create_room_room_access_section_knocking_restricted_option_description">"Ktokoľvek v %1$s sa môžu pripojiť, ale všetci ostatní musia požiadať o prístup."</string>
<string name="screen_create_room_room_access_section_knocking_restricted_option_title">"Požiadať o pripojenie"</string> <string name="screen_create_room_room_access_section_knocking_restricted_option_title">"Požiadať o pripojenie"</string>
<string name="screen_create_room_room_access_section_private_option_description">"Pripojiť sa môžu iba pozvaní ľudia."</string> <string name="screen_create_room_room_access_section_private_option_description">"Pripojiť sa môžu iba pozvaní ľudia."</string>
<string name="screen_create_room_room_access_section_private_option_title">"Súkromná"</string>
<string name="screen_create_room_room_access_section_public_option_description">"Pripojiť sa môže ktokoľvek."</string> <string name="screen_create_room_room_access_section_public_option_description">"Pripojiť sa môže ktokoľvek."</string>
<string name="screen_create_room_room_access_section_public_option_title">"Ktokoľvek"</string>
<string name="screen_create_room_room_access_section_restricted_option_description">"Ktokoľvek v %1$s sa môže pripojiť."</string> <string name="screen_create_room_room_access_section_restricted_option_description">"Ktokoľvek v %1$s sa môže pripojiť."</string>
<string name="screen_create_room_room_access_section_restricted_option_title">"Štandardná"</string> <string name="screen_create_room_room_access_section_restricted_option_title">"Štandardná"</string>
<string name="screen_create_room_room_access_section_title">"Kto má prístup"</string> <string name="screen_create_room_room_access_section_title">"Kto má prístup"</string>

View file

@ -6,11 +6,9 @@
<string name="screen_create_room_private_option_description">"Endast inbjudna personer kan gå med."</string> <string name="screen_create_room_private_option_description">"Endast inbjudna personer kan gå med."</string>
<string name="screen_create_room_public_option_description">"Vem som helst kan hitta det här rummet. <string name="screen_create_room_public_option_description">"Vem som helst kan hitta det här rummet.
Du kan ändra detta när som helst i rumsinställningarna."</string> Du kan ändra detta när som helst i rumsinställningarna."</string>
<string name="screen_create_room_public_option_title">"Offentligt rum"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"Vem som helst kan be om att gå med men en administratör eller en moderator måste acceptera begäran"</string> <string name="screen_create_room_room_access_section_knocking_option_description">"Vem som helst kan be om att gå med men en administratör eller en moderator måste acceptera begäran"</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Tillåt att be om att gå med"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"Tillåt att be om att gå med"</string>
<string name="screen_create_room_room_access_section_public_option_description">"Vem som helst kan gå med."</string> <string name="screen_create_room_room_access_section_public_option_description">"Vem som helst kan gå med."</string>
<string name="screen_create_room_room_access_section_public_option_title">"Offentligt"</string>
<string name="screen_create_room_room_address_section_footer">"Du behöver en adress för att den ska synas i den offentliga katalogen."</string> <string name="screen_create_room_room_address_section_footer">"Du behöver en adress för att den ska synas i den offentliga katalogen."</string>
<string name="screen_create_room_room_address_section_title">"Adress"</string> <string name="screen_create_room_room_address_section_title">"Adress"</string>
<string name="screen_create_room_room_visibility_section_title">"Rumssynlighet"</string> <string name="screen_create_room_room_visibility_section_title">"Rumssynlighet"</string>

View file

@ -6,11 +6,9 @@
<string name="screen_create_room_private_option_description">"Bu odaya yalnızca davet edilen kişiler erişebilir. Tüm mesajlar uçtan uca şifrelenir."</string> <string name="screen_create_room_private_option_description">"Bu odaya yalnızca davet edilen kişiler erişebilir. Tüm mesajlar uçtan uca şifrelenir."</string>
<string name="screen_create_room_public_option_description">"Bu odayı herkes bulabilir. <string name="screen_create_room_public_option_description">"Bu odayı herkes bulabilir.
Bunu istediğiniz zaman oda ayarlarından değiştirebilirsiniz."</string> Bunu istediğiniz zaman oda ayarlarından değiştirebilirsiniz."</string>
<string name="screen_create_room_public_option_title">"Herkese açık oda"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"Herkes odaya katılmayı isteyebilir ancak bir yönetici veya moderatörün isteği kabul etmesi gerekecektir"</string> <string name="screen_create_room_room_access_section_knocking_option_description">"Herkes odaya katılmayı isteyebilir ancak bir yönetici veya moderatörün isteği kabul etmesi gerekecektir"</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Katılmak için sor"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"Katılmak için sor"</string>
<string name="screen_create_room_room_access_section_public_option_description">"Bu odaya herkes katılabilir"</string> <string name="screen_create_room_room_access_section_public_option_description">"Bu odaya herkes katılabilir"</string>
<string name="screen_create_room_room_access_section_public_option_title">"Herkes"</string>
<string name="screen_create_room_room_address_section_footer">"Bu odanın genel oda dizininde görünür olması için bir oda adresine ihtiyacınız olacaktır."</string> <string name="screen_create_room_room_address_section_footer">"Bu odanın genel oda dizininde görünür olması için bir oda adresine ihtiyacınız olacaktır."</string>
<string name="screen_create_room_room_address_section_title">"Oda adresi"</string> <string name="screen_create_room_room_address_section_title">"Oda adresi"</string>
<string name="screen_create_room_room_visibility_section_title">"Oda görünürlüğü"</string> <string name="screen_create_room_room_visibility_section_title">"Oda görünürlüğü"</string>

View file

@ -6,11 +6,9 @@
<string name="screen_create_room_private_option_description">"Лише запрошені люди мають доступ до цієї кімнати. Усі повідомлення захищені наскрізним шифруванням."</string> <string name="screen_create_room_private_option_description">"Лише запрошені люди мають доступ до цієї кімнати. Усі повідомлення захищені наскрізним шифруванням."</string>
<string name="screen_create_room_public_option_description">"Будь-хто може знайти цю кімнату. <string name="screen_create_room_public_option_description">"Будь-хто може знайти цю кімнату.
Ви можете змінити це в будь-який час у налаштуваннях кімнати."</string> Ви можете змінити це в будь-який час у налаштуваннях кімнати."</string>
<string name="screen_create_room_public_option_title">"Публічна кімната"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"Будь-хто може попросити приєднатися до кімнати, але адміністратор або модератор повинен буде прийняти запит"</string> <string name="screen_create_room_room_access_section_knocking_option_description">"Будь-хто може попросити приєднатися до кімнати, але адміністратор або модератор повинен буде прийняти запит"</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Запросити приєднатися"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"Запросити приєднатися"</string>
<string name="screen_create_room_room_access_section_public_option_description">"Будь-хто може приєднатися до цієї кімнати"</string> <string name="screen_create_room_room_access_section_public_option_description">"Будь-хто може приєднатися до цієї кімнати"</string>
<string name="screen_create_room_room_access_section_public_option_title">"Кожний"</string>
<string name="screen_create_room_room_address_section_footer">"Щоб цю кімнату було видно в каталозі загальнодоступних кімнат, вам знадобиться її адреса."</string> <string name="screen_create_room_room_address_section_footer">"Щоб цю кімнату було видно в каталозі загальнодоступних кімнат, вам знадобиться її адреса."</string>
<string name="screen_create_room_room_address_section_title">"Адреса кімнати"</string> <string name="screen_create_room_room_address_section_title">"Адреса кімнати"</string>
<string name="screen_create_room_room_visibility_section_title">"Видимість кімнати"</string> <string name="screen_create_room_room_visibility_section_title">"Видимість кімнати"</string>

View file

@ -6,11 +6,9 @@
<string name="screen_create_room_private_option_description">"Faqat taklif etilgan shaxslargina bu xonaga kira oladi. Barcha xabarlar boshdan-oxirigacha shifrlanadi."</string> <string name="screen_create_room_private_option_description">"Faqat taklif etilgan shaxslargina bu xonaga kira oladi. Barcha xabarlar boshdan-oxirigacha shifrlanadi."</string>
<string name="screen_create_room_public_option_description">"Bu xonani har kim topishi mumkin. <string name="screen_create_room_public_option_description">"Bu xonani har kim topishi mumkin.
Buni xona sozlamalaridan istalgan vaqtda oʻzgartirishingiz mumkin."</string> Buni xona sozlamalaridan istalgan vaqtda oʻzgartirishingiz mumkin."</string>
<string name="screen_create_room_public_option_title">"Jamoat xonasi (har kim)"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"Xonaga qoshilishni istalgan kishi sorashi mumkin, lekin administrator yoki moderator sorovni qabul qilishi kerak"</string> <string name="screen_create_room_room_access_section_knocking_option_description">"Xonaga qoshilishni istalgan kishi sorashi mumkin, lekin administrator yoki moderator sorovni qabul qilishi kerak"</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Qoshilishni sorang"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"Qoshilishni sorang"</string>
<string name="screen_create_room_room_access_section_public_option_description">"Bu xonaga istalgan kishi qoshilishi mumkin"</string> <string name="screen_create_room_room_access_section_public_option_description">"Bu xonaga istalgan kishi qoshilishi mumkin"</string>
<string name="screen_create_room_room_access_section_public_option_title">"Har kim"</string>
<string name="screen_create_room_room_address_section_footer">"Ushbu xona ommaviy xonalar royxatida korinishi uchun sizga xona manzili kerak boladi."</string> <string name="screen_create_room_room_address_section_footer">"Ushbu xona ommaviy xonalar royxatida korinishi uchun sizga xona manzili kerak boladi."</string>
<string name="screen_create_room_room_visibility_section_title">"Xonaning korinishi"</string> <string name="screen_create_room_room_visibility_section_title">"Xonaning korinishi"</string>
<string name="screen_create_room_topic_label">"Mavzu (ixtiyoriy)"</string> <string name="screen_create_room_topic_label">"Mavzu (ixtiyoriy)"</string>

View file

@ -6,11 +6,9 @@
<string name="screen_create_room_private_option_description">"僅被邀請的人才能存取此聊天室。所有訊息均會端到端加密。"</string> <string name="screen_create_room_private_option_description">"僅被邀請的人才能存取此聊天室。所有訊息均會端到端加密。"</string>
<string name="screen_create_room_public_option_description">"任何人都可以找到此聊天室。 <string name="screen_create_room_public_option_description">"任何人都可以找到此聊天室。
您隨時都可以在聊天室設定中變更此設定。"</string> 您隨時都可以在聊天室設定中變更此設定。"</string>
<string name="screen_create_room_public_option_title">"公開聊天室"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"任何人都可以要求加入聊天室,但管理員或版主必須接受該請求"</string> <string name="screen_create_room_room_access_section_knocking_option_description">"任何人都可以要求加入聊天室,但管理員或版主必須接受該請求"</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"要求加入"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"要求加入"</string>
<string name="screen_create_room_room_access_section_public_option_description">"任何人都可以加入此聊天室"</string> <string name="screen_create_room_room_access_section_public_option_description">"任何人都可以加入此聊天室"</string>
<string name="screen_create_room_room_access_section_public_option_title">"任何人"</string>
<string name="screen_create_room_room_address_section_footer">"為了讓此聊天室在公開聊天室目錄中可見,您需要聊天室地址。"</string> <string name="screen_create_room_room_address_section_footer">"為了讓此聊天室在公開聊天室目錄中可見,您需要聊天室地址。"</string>
<string name="screen_create_room_room_address_section_title">"聊天室地址"</string> <string name="screen_create_room_room_address_section_title">"聊天室地址"</string>
<string name="screen_create_room_room_visibility_section_title">"聊天室能見度"</string> <string name="screen_create_room_room_visibility_section_title">"聊天室能見度"</string>

View file

@ -6,11 +6,9 @@
<string name="screen_create_room_private_option_description">"只有受邀用户才能访问此聊天室。所有消息均经过端到端加密。"</string> <string name="screen_create_room_private_option_description">"只有受邀用户才能访问此聊天室。所有消息均经过端到端加密。"</string>
<string name="screen_create_room_public_option_description">"任何人都能找到此聊天室。 <string name="screen_create_room_public_option_description">"任何人都能找到此聊天室。
你可以随时在聊天室设置中更改。"</string> 你可以随时在聊天室设置中更改。"</string>
<string name="screen_create_room_public_option_title">"公开聊天室"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"任何人都可以请求加入房间,但必须由管理员或审核人接受"</string> <string name="screen_create_room_room_access_section_knocking_option_description">"任何人都可以请求加入房间,但必须由管理员或审核人接受"</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"请求加入"</string> <string name="screen_create_room_room_access_section_knocking_option_title">"请求加入"</string>
<string name="screen_create_room_room_access_section_public_option_description">"任何人都可以加入此房间"</string> <string name="screen_create_room_room_access_section_public_option_description">"任何人都可以加入此房间"</string>
<string name="screen_create_room_room_access_section_public_option_title">"任何人"</string>
<string name="screen_create_room_room_address_section_footer">"要使该房间在公开房间目录中可见,您需要一个房间地址。"</string> <string name="screen_create_room_room_address_section_footer">"要使该房间在公开房间目录中可见,您需要一个房间地址。"</string>
<string name="screen_create_room_room_address_section_title">"房间地址"</string> <string name="screen_create_room_room_address_section_title">"房间地址"</string>
<string name="screen_create_room_room_visibility_section_title">"房间可见性"</string> <string name="screen_create_room_room_visibility_section_title">"房间可见性"</string>

View file

@ -35,6 +35,11 @@ interface EnterpriseService {
fun bugReportUrlFlow(sessionId: SessionId?): Flow<BugReportUrl> fun bugReportUrlFlow(sessionId: SessionId?): Flow<BugReportUrl>
/**
* Gets Notification Channel to use for the noisy notifications of the provided session.
*/
fun getNoisyNotificationChannelId(sessionId: SessionId): String?
companion object { companion object {
const val ANY_ACCOUNT_PROVIDER = "*" const val ANY_ACCOUNT_PROVIDER = "*"
} }

View file

@ -43,4 +43,6 @@ class DefaultEnterpriseService : EnterpriseService {
override fun bugReportUrlFlow(sessionId: SessionId?): Flow<BugReportUrl> { override fun bugReportUrlFlow(sessionId: SessionId?): Flow<BugReportUrl> {
return flowOf(BugReportUrl.UseDefault) return flowOf(BugReportUrl.UseDefault)
} }
override fun getNoisyNotificationChannelId(sessionId: SessionId): String? = null
} }

View file

@ -98,4 +98,10 @@ class DefaultEnterpriseServiceTest {
awaitComplete() awaitComplete()
} }
} }
@Test
fun `getNoisyNotificationChannelId returns null`() = runTest {
val defaultEnterpriseService = DefaultEnterpriseService()
assertThat(defaultEnterpriseService.getNoisyNotificationChannelId(A_SESSION_ID)).isNull()
}
} }

View file

@ -29,6 +29,7 @@ class FakeEnterpriseService(
private val overrideBrandColorResult: (SessionId?, String?) -> Unit = { _, _ -> lambdaError() }, private val overrideBrandColorResult: (SessionId?, String?) -> Unit = { _, _ -> lambdaError() },
private val firebasePushGatewayResult: () -> String? = { lambdaError() }, private val firebasePushGatewayResult: () -> String? = { lambdaError() },
private val unifiedPushDefaultPushGatewayResult: () -> String? = { lambdaError() }, private val unifiedPushDefaultPushGatewayResult: () -> String? = { lambdaError() },
private val getNoisyNotificationChannelIdResult: (SessionId?) -> String? = { lambdaError() },
) : EnterpriseService { ) : EnterpriseService {
private val brandColorState = MutableStateFlow(initialBrandColor) private val brandColorState = MutableStateFlow(initialBrandColor)
private val semanticColorsState = MutableStateFlow(initialSemanticColors) private val semanticColorsState = MutableStateFlow(initialSemanticColors)
@ -69,4 +70,8 @@ class FakeEnterpriseService(
override fun bugReportUrlFlow(sessionId: SessionId?): Flow<BugReportUrl> { override fun bugReportUrlFlow(sessionId: SessionId?): Flow<BugReportUrl> {
return bugReportUrlMutableFlow.asStateFlow() return bugReportUrlMutableFlow.asStateFlow()
} }
override fun getNoisyNotificationChannelId(sessionId: SessionId): String? {
return getNoisyNotificationChannelIdResult(sessionId)
}
} }

View file

@ -3,6 +3,8 @@
<string name="banner_battery_optimization_content_android">"Desativa as otimizações de bateria para esta aplicação, de modo a garantir que todas as notificações chegam."</string> <string name="banner_battery_optimization_content_android">"Desativa as otimizações de bateria para esta aplicação, de modo a garantir que todas as notificações chegam."</string>
<string name="banner_battery_optimization_submit_android">"Desativar otimizações"</string> <string name="banner_battery_optimization_submit_android">"Desativar otimizações"</string>
<string name="banner_battery_optimization_title_android">"As notificações não chegam?"</string> <string name="banner_battery_optimization_title_android">"As notificações não chegam?"</string>
<string name="banner_new_sound_message">"O toque de notificação foi atualizado — mais claro, mais rápido e menos perturbador."</string>
<string name="banner_new_sound_title">"Atualizámos os seus sons"</string>
<string name="banner_set_up_recovery_content">"Recupera a tua identidade criptográfica e o histórico de mensagens com uma chave de recuperação se tiveres perdido todos os teus dispositivos existentes."</string> <string name="banner_set_up_recovery_content">"Recupera a tua identidade criptográfica e o histórico de mensagens com uma chave de recuperação se tiveres perdido todos os teus dispositivos existentes."</string>
<string name="banner_set_up_recovery_submit">"Configurar recuperação"</string> <string name="banner_set_up_recovery_submit">"Configurar recuperação"</string>
<string name="banner_set_up_recovery_title">"Configurar a recuperação"</string> <string name="banner_set_up_recovery_title">"Configurar a recuperação"</string>

View file

@ -1,3 +1,4 @@
import extension.buildConfigFieldStr
import extension.setupDependencyInjection import extension.setupDependencyInjection
import extension.testCommonDependencies import extension.testCommonDependencies
@ -23,6 +24,30 @@ android {
isIncludeAndroidResources = true isIncludeAndroidResources = true
} }
} }
buildFeatures {
buildConfig = true
}
buildTypes {
val elementClassicPackageKey = "elementClassicPackage"
val elementClassicPackage = "im.vector.app"
val elementClassicPackageDebug = "$elementClassicPackage.debug"
val elementClassicPackageNightly = "$elementClassicPackage.nightly"
getByName("release") {
manifestPlaceholders[elementClassicPackageKey] = elementClassicPackage
buildConfigFieldStr(elementClassicPackageKey, elementClassicPackage)
}
getByName("debug") {
manifestPlaceholders[elementClassicPackageKey] = elementClassicPackageDebug
buildConfigFieldStr(elementClassicPackageKey, elementClassicPackageDebug)
}
register("nightly") {
matchingFallbacks += listOf("release")
manifestPlaceholders[elementClassicPackageKey] = elementClassicPackageNightly
buildConfigFieldStr(elementClassicPackageKey, elementClassicPackageNightly)
}
}
} }
setupDependencyInjection() setupDependencyInjection()

View file

@ -13,8 +13,9 @@
<intent> <intent>
<action android:name="android.support.customtabs.action.CustomTabsService" /> <action android:name="android.support.customtabs.action.CustomTabsService" />
</intent> </intent>
<!-- To be able to start the service exported by Element Classic -->
<package android:name="${elementClassicPackage}" />
</queries> </queries>
<!-- Permission to read data from Element classic -->
<uses-permission android:name="im.vector.app.READ_DATA" />
</manifest> </manifest>

View file

@ -20,9 +20,8 @@ import android.os.Messenger
import android.os.RemoteException import android.os.RemoteException
import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.ContributesBinding
import io.element.android.features.login.impl.BuildConfig
import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.core.meta.BuildType
import io.element.android.libraries.di.annotations.AppCoroutineScope import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.libraries.di.annotations.ApplicationContext import io.element.android.libraries.di.annotations.ApplicationContext
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
@ -44,7 +43,11 @@ sealed interface ElementClassicConnectionState {
object Idle : ElementClassicConnectionState object Idle : ElementClassicConnectionState
object ElementClassicNotFound : ElementClassicConnectionState object ElementClassicNotFound : ElementClassicConnectionState
object ElementClassicReadyNoSession : ElementClassicConnectionState object ElementClassicReadyNoSession : ElementClassicConnectionState
data class ElementClassicReady(val userId: UserId) : ElementClassicConnectionState data class ElementClassicReady(
val userId: UserId,
val secrets: String,
) : ElementClassicConnectionState
data class Error(val error: String) : ElementClassicConnectionState data class Error(val error: String) : ElementClassicConnectionState
} }
@ -56,7 +59,6 @@ class DefaultElementClassicConnection(
private val context: Context, private val context: Context,
@AppCoroutineScope @AppCoroutineScope
private val coroutineScope: CoroutineScope, private val coroutineScope: CoroutineScope,
private val buildMeta: BuildMeta,
) : ElementClassicConnection { ) : ElementClassicConnection {
// Messenger for communicating with the service. // Messenger for communicating with the service.
private var messenger: Messenger? = null private var messenger: Messenger? = null
@ -101,7 +103,7 @@ class DefaultElementClassicConnection(
// applications replace our component. // applications replace our component.
try { try {
val intentService = Intent() val intentService = Intent()
intentService.setComponent(getElementClassicComponent(buildMeta)) intentService.setComponent(getElementClassicComponent())
if (context.bindService(intentService, serviceConnection, BIND_AUTO_CREATE)) { if (context.bindService(intentService, serviceConnection, BIND_AUTO_CREATE)) {
Timber.tag(loggerTag.value).d("Binding returned true") Timber.tag(loggerTag.value).d("Binding returned true")
} else { } else {
@ -198,17 +200,8 @@ class DefaultElementClassicConnection(
} }
} }
private fun getElementClassicComponent(buildMeta: BuildMeta) = ComponentName( private fun getElementClassicComponent() = ComponentName(
buildString { BuildConfig.elementClassicPackage,
append(ELEMENT_CLASSIC_APP_ID)
append(
when (buildMeta.buildType) {
BuildType.DEBUG -> ELEMENT_CLASSIC_APP_ID_DEBUG_SUFFIX
BuildType.NIGHTLY -> ELEMENT_CLASSIC_APP_ID_NIGHTLY_SUFFIX
BuildType.RELEASE -> ELEMENT_CLASSIC_APP_ID_RELEASE_SUFFIX
}
)
},
ELEMENT_CLASSIC_SERVICE_FULL_CLASS_NAME, ELEMENT_CLASSIC_SERVICE_FULL_CLASS_NAME,
) )
@ -220,9 +213,14 @@ class DefaultElementClassicConnection(
if (error != null) { if (error != null) {
ElementClassicConnectionState.Error(error) ElementClassicConnectionState.Error(error)
} else { } else {
val userId = getString(KEY_USER_ID_STR)?.let(::UserId) val userId = getString(KEY_USER_ID_STR)?.takeIf { it.isNotEmpty() }?.let(::UserId)
if (userId != null) { if (userId != null) {
ElementClassicConnectionState.ElementClassicReady(userId) val secrets = getString(KEY_SECRETS_STR)?.takeIf { it.isNotEmpty() }
if (secrets == null) {
ElementClassicConnectionState.Error("No secrets received from Element Classic")
} else {
ElementClassicConnectionState.ElementClassicReady(userId, secrets)
}
} else { } else {
ElementClassicConnectionState.ElementClassicReadyNoSession ElementClassicConnectionState.ElementClassicReadyNoSession
} }
@ -232,18 +230,31 @@ class DefaultElementClassicConnection(
// Everything in this companion object must match what is defined in Element Classic // Everything in this companion object must match what is defined in Element Classic
private companion object { private companion object {
const val ELEMENT_CLASSIC_SERVICE_FULL_CLASS_NAME = "im.vector.app.features.importer.ImporterService"
// Command to the service to get the data. // Command to the service to get the data.
const val MSG_GET_DATA = 1 const val MSG_GET_DATA = 1
const val ELEMENT_CLASSIC_APP_ID = "im.vector.app"
const val ELEMENT_CLASSIC_APP_ID_DEBUG_SUFFIX = ".debug"
const val ELEMENT_CLASSIC_APP_ID_NIGHTLY_SUFFIX = ".nightly"
const val ELEMENT_CLASSIC_APP_ID_RELEASE_SUFFIX = ""
const val ELEMENT_CLASSIC_SERVICE_FULL_CLASS_NAME = "im.vector.app.features.importer.ImporterService"
// Keys for the bundle returned from the service // Keys for the bundle returned from the service
const val KEY_ERROR_STR = "error" const val KEY_ERROR_STR = "error"
const val KEY_USER_ID_STR = "userId" const val KEY_USER_ID_STR = "userId"
/**
* Key to extract the secrets from the bundle, as a Json string.
* Json will have this format:
* {
* "cross_signing" : {
* "master_key" : "z8RUxnaAGu___REDACTED___k+BQL9o",
* "user_signing_key" : "baJHzA___REDACTED___xMLbSUAXw9QUzqms",
* "self_signing_key" : "DU0CvLtR2G/___REDACTED___dV/MONNq4nsQhM"
* },
* "backup" : {
* "algorithm" : "m.megolm_backup.v1.curve25519-aes-sha2",
* "key" : "VzncmQ+UOV___REDACTED___patxDz7m0Nc",
* "backup_version" : "1"
* }
* }
*/
const val KEY_SECRETS_STR = "secrets"
} }
} }

View file

@ -13,6 +13,7 @@ import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
import io.element.android.libraries.matrix.test.A_SECRET
import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.libraries.sessionstorage.api.SessionStore
import io.element.android.libraries.sessionstorage.test.InMemorySessionStore import io.element.android.libraries.sessionstorage.test.InMemorySessionStore
@ -114,7 +115,7 @@ class LoginWithClassicPresenterTest {
presenter.test { presenter.test {
skipItems(2) skipItems(2)
elementClassicConnection.emitState( elementClassicConnection.emitState(
ElementClassicConnectionState.ElementClassicReady(userId = A_USER_ID) ElementClassicConnectionState.ElementClassicReady(userId = A_USER_ID, secrets = A_SECRET)
) )
val readyState = awaitItem() val readyState = awaitItem()
assertThat(readyState.canLoginWithClassic).isTrue() assertThat(readyState.canLoginWithClassic).isTrue()
@ -140,7 +141,7 @@ class LoginWithClassicPresenterTest {
presenter.test { presenter.test {
skipItems(2) skipItems(2)
elementClassicConnection.emitState( elementClassicConnection.emitState(
ElementClassicConnectionState.ElementClassicReady(userId = A_USER_ID) ElementClassicConnectionState.ElementClassicReady(userId = A_USER_ID, secrets = A_SECRET)
) )
val readyState = awaitItem() val readyState = awaitItem()
assertThat(readyState.canLoginWithClassic).isTrue() assertThat(readyState.canLoginWithClassic).isTrue()
@ -175,7 +176,7 @@ class LoginWithClassicPresenterTest {
presenter.test { presenter.test {
skipItems(2) skipItems(2)
elementClassicConnection.emitState( elementClassicConnection.emitState(
ElementClassicConnectionState.ElementClassicReady(userId = A_USER_ID) ElementClassicConnectionState.ElementClassicReady(userId = A_USER_ID, secrets = A_SECRET)
) )
// No new item, because canLoginWithClassic is still false // No new item, because canLoginWithClassic is still false
} }
@ -192,7 +193,7 @@ class LoginWithClassicPresenterTest {
skipItems(1) skipItems(1)
// Note: it should not happen IRL // Note: it should not happen IRL
elementClassicConnection.emitState( elementClassicConnection.emitState(
ElementClassicConnectionState.ElementClassicReady(userId = A_USER_ID) ElementClassicConnectionState.ElementClassicReady(userId = A_USER_ID, secrets = A_SECRET)
) )
// No new item, because canLoginWithClassic is still false // No new item, because canLoginWithClassic is still false
} }

View file

@ -53,6 +53,7 @@ dependencies {
implementation(projects.libraries.preferences.api) implementation(projects.libraries.preferences.api)
implementation(projects.libraries.recentemojis.api) implementation(projects.libraries.recentemojis.api)
implementation(projects.libraries.roomselect.api) implementation(projects.libraries.roomselect.api)
implementation(projects.libraries.audio.api)
implementation(projects.libraries.voiceplayer.api) implementation(projects.libraries.voiceplayer.api)
implementation(projects.libraries.voicerecorder.api) implementation(projects.libraries.voicerecorder.api)
implementation(projects.libraries.mediaplayer.api) implementation(projects.libraries.mediaplayer.api)
@ -95,6 +96,7 @@ dependencies {
testImplementation(projects.libraries.mediapickers.test) testImplementation(projects.libraries.mediapickers.test)
testImplementation(projects.libraries.permissions.test) testImplementation(projects.libraries.permissions.test)
testImplementation(projects.libraries.preferences.test) testImplementation(projects.libraries.preferences.test)
testImplementation(projects.libraries.audio.test)
testImplementation(projects.libraries.voicerecorder.test) testImplementation(projects.libraries.voicerecorder.test)
testImplementation(projects.libraries.mediaplayer.test) testImplementation(projects.libraries.mediaplayer.test)
testImplementation(projects.libraries.mediaviewer.test) testImplementation(projects.libraries.mediaviewer.test)

View file

@ -30,6 +30,8 @@ import io.element.android.features.messages.api.MessageComposerContext
import io.element.android.features.messages.api.timeline.voicemessages.composer.VoiceMessageComposerEvent import io.element.android.features.messages.api.timeline.voicemessages.composer.VoiceMessageComposerEvent
import io.element.android.features.messages.api.timeline.voicemessages.composer.VoiceMessageComposerPresenter import io.element.android.features.messages.api.timeline.voicemessages.composer.VoiceMessageComposerPresenter
import io.element.android.features.messages.api.timeline.voicemessages.composer.VoiceMessageComposerState import io.element.android.features.messages.api.timeline.voicemessages.composer.VoiceMessageComposerState
import io.element.android.libraries.audio.api.AudioFocus
import io.element.android.libraries.audio.api.AudioFocusRequester
import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.di.annotations.SessionCoroutineScope import io.element.android.libraries.di.annotations.SessionCoroutineScope
import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.Timeline
@ -58,6 +60,7 @@ class DefaultVoiceMessageComposerPresenter(
@Assisted private val timelineMode: Timeline.Mode, @Assisted private val timelineMode: Timeline.Mode,
private val voiceRecorder: VoiceRecorder, private val voiceRecorder: VoiceRecorder,
private val analyticsService: AnalyticsService, private val analyticsService: AnalyticsService,
private val audioFocus: AudioFocus,
mediaSenderFactory: MediaSenderFactory, mediaSenderFactory: MediaSenderFactory,
private val player: VoiceMessageComposerPlayer, private val player: VoiceMessageComposerPlayer,
private val messageComposerContext: MessageComposerContext, private val messageComposerContext: MessageComposerContext,
@ -246,8 +249,14 @@ class DefaultVoiceMessageComposerPresenter(
private fun CoroutineScope.startRecording() = launch { private fun CoroutineScope.startRecording() = launch {
try { try {
audioFocus.requestAudioFocus(AudioFocusRequester.RecordVoiceMessage) {
// something else grabbed focus (phone call, etc) - finish gracefully
// so the user keeps their partial recording
sessionCoroutineScope.finishRecording()
}
voiceRecorder.startRecord() voiceRecorder.startRecord()
} catch (e: SecurityException) { } catch (e: SecurityException) {
audioFocus.releaseAudioFocus()
Timber.e(e, "Voice message error") Timber.e(e, "Voice message error")
analyticsService.trackError(VoiceMessageException.PermissionMissing("Expected permission to record but none", e)) analyticsService.trackError(VoiceMessageException.PermissionMissing("Expected permission to record but none", e))
} }
@ -255,10 +264,12 @@ class DefaultVoiceMessageComposerPresenter(
private fun CoroutineScope.finishRecording() = launch { private fun CoroutineScope.finishRecording() = launch {
voiceRecorder.stopRecord() voiceRecorder.stopRecord()
audioFocus.releaseAudioFocus()
} }
private fun CoroutineScope.cancelRecording() = launch { private fun CoroutineScope.cancelRecording() = launch {
voiceRecorder.stopRecord(cancelled = true) voiceRecorder.stopRecord(cancelled = true)
audioFocus.releaseAudioFocus()
} }
private fun CoroutineScope.deleteRecording() = launch { private fun CoroutineScope.deleteRecording() = launch {

View file

@ -1238,7 +1238,7 @@ class MessagesPresenterTest {
} }
@Test @Test
fun `present - shows a "world_readable" icon if the room is encrypted and history is world_readable`() = runTest { fun `present - shows a 'world_readable' icon if the room is encrypted and history is world_readable`() = runTest {
val presenter = createMessagesPresenter( val presenter = createMessagesPresenter(
joinedRoom = FakeJoinedRoom( joinedRoom = FakeJoinedRoom(
baseRoom = FakeBaseRoom( baseRoom = FakeBaseRoom(

View file

@ -19,12 +19,15 @@ import io.element.android.features.messages.api.timeline.voicemessages.composer.
import io.element.android.features.messages.api.timeline.voicemessages.composer.VoiceMessageComposerState import io.element.android.features.messages.api.timeline.voicemessages.composer.VoiceMessageComposerState
import io.element.android.features.messages.impl.messagecomposer.aReplyMode import io.element.android.features.messages.impl.messagecomposer.aReplyMode
import io.element.android.features.messages.test.FakeMessageComposerContext import io.element.android.features.messages.test.FakeMessageComposerContext
import io.element.android.libraries.audio.api.AudioFocus
import io.element.android.libraries.audio.api.AudioFocusRequester
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.media.AudioInfo import io.element.android.libraries.matrix.api.media.AudioInfo
import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
import io.element.android.libraries.matrix.test.timeline.FakeTimeline import io.element.android.libraries.matrix.test.timeline.FakeTimeline
import io.element.android.libraries.mediaplayer.test.FakeAudioFocus
import io.element.android.libraries.mediaplayer.test.FakeMediaPlayer import io.element.android.libraries.mediaplayer.test.FakeMediaPlayer
import io.element.android.libraries.mediaupload.api.MediaOptimizationConfig import io.element.android.libraries.mediaupload.api.MediaOptimizationConfig
import io.element.android.libraries.mediaupload.impl.DefaultMediaSender import io.element.android.libraries.mediaupload.impl.DefaultMediaSender
@ -80,6 +83,12 @@ class DefaultVoiceMessageComposerPresenterTest {
timelineMode = Timeline.Mode.Live, timelineMode = Timeline.Mode.Live,
mediaOptimizationConfigProvider = { MediaOptimizationConfig(compressImages = true, videoCompressionPreset = VideoCompressionPreset.STANDARD) }, mediaOptimizationConfigProvider = { MediaOptimizationConfig(compressImages = true, videoCompressionPreset = VideoCompressionPreset.STANDARD) },
) )
private val requestAudioFocusResult = lambdaRecorder<AudioFocusRequester, () -> Unit, Unit> { _, _ -> }
private val releaseAudioFocusResult = lambdaRecorder<Unit> { }
private val audioFocus: AudioFocus = FakeAudioFocus(
requestAudioFocusResult = requestAudioFocusResult,
releaseAudioFocusResult = releaseAudioFocusResult,
)
private val messageComposerContext = FakeMessageComposerContext() private val messageComposerContext = FakeMessageComposerContext()
companion object { companion object {
@ -159,6 +168,61 @@ class DefaultVoiceMessageComposerPresenterTest {
} }
} }
@Test
fun `present - recording requests audio focus and releases on stop`() = runTest {
val presenter = createDefaultVoiceMessageComposerPresenter()
presenter.test {
awaitItem().eventSink(VoiceMessageComposerEvent.RecorderEvent(VoiceMessageRecorderEvent.Start))
val recordingState = awaitItem()
requestAudioFocusResult.assertions().isCalledOnce()
releaseAudioFocusResult.assertions().isNeverCalled()
recordingState.eventSink(VoiceMessageComposerEvent.RecorderEvent(VoiceMessageRecorderEvent.Stop))
awaitItem()
releaseAudioFocusResult.assertions().isCalledOnce()
cancelAndIgnoreRemainingEvents()
}
}
@Test
fun `present - cancelling recording releases audio focus`() = runTest {
val presenter = createDefaultVoiceMessageComposerPresenter()
presenter.test {
awaitItem().eventSink(VoiceMessageComposerEvent.RecorderEvent(VoiceMessageRecorderEvent.Start))
awaitItem().eventSink(VoiceMessageComposerEvent.RecorderEvent(VoiceMessageRecorderEvent.Cancel))
awaitItem()
requestAudioFocusResult.assertions().isCalledOnce()
releaseAudioFocusResult.assertions().isCalledOnce()
cancelAndIgnoreRemainingEvents()
}
}
@Test
fun `present - audio focus loss during recording finishes gracefully`() = runTest {
var onFocusLost: (() -> Unit)? = null
val testAudioFocus = FakeAudioFocus(
requestAudioFocusResult = { _, callback -> onFocusLost = callback },
releaseAudioFocusResult = { },
)
val presenter = createDefaultVoiceMessageComposerPresenter(audioFocus = testAudioFocus)
presenter.test {
awaitItem().eventSink(VoiceMessageComposerEvent.RecorderEvent(VoiceMessageRecorderEvent.Start))
awaitItem()
// simulate focus loss (phone call, etc)
onFocusLost?.invoke()
advanceUntilIdle()
val finalState = awaitItem()
assertThat(finalState.voiceMessageState).isEqualTo(aPreviewState())
voiceRecorder.assertCalls(started = 1, stopped = 1)
cancelAndIgnoreRemainingEvents()
}
}
@Test @Test
fun `present - abort recording`() = runTest { fun `present - abort recording`() = runTest {
val presenter = createDefaultVoiceMessageComposerPresenter() val presenter = createDefaultVoiceMessageComposerPresenter()
@ -647,12 +711,14 @@ class DefaultVoiceMessageComposerPresenterTest {
private fun TestScope.createDefaultVoiceMessageComposerPresenter( private fun TestScope.createDefaultVoiceMessageComposerPresenter(
permissionsPresenter: PermissionsPresenter = createFakePermissionsPresenter(), permissionsPresenter: PermissionsPresenter = createFakePermissionsPresenter(),
voiceRecorder: VoiceRecorder = this@DefaultVoiceMessageComposerPresenterTest.voiceRecorder, voiceRecorder: VoiceRecorder = this@DefaultVoiceMessageComposerPresenterTest.voiceRecorder,
audioFocus: AudioFocus = this@DefaultVoiceMessageComposerPresenterTest.audioFocus,
): DefaultVoiceMessageComposerPresenter { ): DefaultVoiceMessageComposerPresenter {
return DefaultVoiceMessageComposerPresenter( return DefaultVoiceMessageComposerPresenter(
sessionCoroutineScope = backgroundScope, sessionCoroutineScope = backgroundScope,
timelineMode = Timeline.Mode.Live, timelineMode = Timeline.Mode.Live,
voiceRecorder = voiceRecorder, voiceRecorder = voiceRecorder,
analyticsService = analyticsService, analyticsService = analyticsService,
audioFocus = audioFocus,
mediaSenderFactory = { mediaSender }, mediaSenderFactory = { mediaSender },
player = VoiceMessageComposerPlayer(FakeMediaPlayer(), this), player = VoiceMessageComposerPlayer(FakeMediaPlayer(), this),
messageComposerContext = messageComposerContext, messageComposerContext = messageComposerContext,

View file

@ -17,6 +17,7 @@ android {
dependencies { dependencies {
api(projects.features.messages.impl) api(projects.features.messages.impl)
implementation(projects.libraries.matrix.test) implementation(projects.libraries.matrix.test)
implementation(projects.libraries.audio.test)
implementation(projects.libraries.mediaplayer.test) implementation(projects.libraries.mediaplayer.test)
implementation(projects.libraries.mediaupload.test) implementation(projects.libraries.mediaupload.test)
implementation(projects.libraries.mediaviewer.api) implementation(projects.libraries.mediaviewer.api)

View file

@ -13,6 +13,7 @@ import io.element.android.features.messages.impl.voicemessages.composer.VoiceMes
import io.element.android.features.messages.test.FakeMessageComposerContext import io.element.android.features.messages.test.FakeMessageComposerContext
import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
import io.element.android.libraries.mediaplayer.test.FakeAudioFocus
import io.element.android.libraries.mediaplayer.test.FakeMediaPlayer import io.element.android.libraries.mediaplayer.test.FakeMediaPlayer
import io.element.android.libraries.mediaupload.api.MediaSender import io.element.android.libraries.mediaupload.api.MediaSender
import io.element.android.libraries.mediaupload.impl.DefaultMediaSender import io.element.android.libraries.mediaupload.impl.DefaultMediaSender
@ -38,6 +39,10 @@ class FakeDefaultVoiceMessageComposerPresenterFactory(
timelineMode = timelineMode, timelineMode = timelineMode,
voiceRecorder = FakeVoiceRecorder(), voiceRecorder = FakeVoiceRecorder(),
analyticsService = FakeAnalyticsService(), analyticsService = FakeAnalyticsService(),
audioFocus = FakeAudioFocus(
requestAudioFocusResult = { _, _ -> },
releaseAudioFocusResult = { },
),
mediaSenderFactory = { mediaSender }, mediaSenderFactory = { mediaSender },
player = VoiceMessageComposerPlayer( player = VoiceMessageComposerPlayer(
mediaPlayer = FakeMediaPlayer(), mediaPlayer = FakeMediaPlayer(),

View file

@ -10,6 +10,7 @@
<string name="screen_advanced_settings_element_call_base_url_validation_error">"URL inválido, certifica-te de que incluis o protocolo (http/https) e o endereço correto."</string> <string name="screen_advanced_settings_element_call_base_url_validation_error">"URL inválido, certifica-te de que incluis o protocolo (http/https) e o endereço correto."</string>
<string name="screen_advanced_settings_hide_invite_avatars_toggle_title">"Ocultar avatares nos pedidos de acesso a salas"</string> <string name="screen_advanced_settings_hide_invite_avatars_toggle_title">"Ocultar avatares nos pedidos de acesso a salas"</string>
<string name="screen_advanced_settings_hide_timeline_media_toggle_title">"Ocultar pré-visualizações de multimédia na cronologia"</string> <string name="screen_advanced_settings_hide_timeline_media_toggle_title">"Ocultar pré-visualizações de multimédia na cronologia"</string>
<string name="screen_advanced_settings_labs">"Experiências"</string>
<string name="screen_advanced_settings_media_compression_description">"Carrega fotos e vídeos mais rapidamente e reduz a utilização de dados"</string> <string name="screen_advanced_settings_media_compression_description">"Carrega fotos e vídeos mais rapidamente e reduz a utilização de dados"</string>
<string name="screen_advanced_settings_media_compression_title">"Otimiza a qualidade da mídia"</string> <string name="screen_advanced_settings_media_compression_title">"Otimiza a qualidade da mídia"</string>
<string name="screen_advanced_settings_moderation_and_safety_section_title">"Moderação e Segurança"</string> <string name="screen_advanced_settings_moderation_and_safety_section_title">"Moderação e Segurança"</string>
@ -43,6 +44,11 @@
<string name="screen_edit_profile_error_title">"Não foi possível atualizar o perfil"</string> <string name="screen_edit_profile_error_title">"Não foi possível atualizar o perfil"</string>
<string name="screen_edit_profile_title">"Editar perfil"</string> <string name="screen_edit_profile_title">"Editar perfil"</string>
<string name="screen_edit_profile_updating_details">"A atualizar o perfil…"</string> <string name="screen_edit_profile_updating_details">"A atualizar o perfil…"</string>
<string name="screen_labs_enable_threads">"Ativar respostas em tópicos"</string>
<string name="screen_labs_enable_threads_description">"A aplicação será reiniciada para aplicar esta alteração."</string>
<string name="screen_labs_header_description">"Experimenta as nossas mais recentes ideias ainda em desenvolvimento. Estas funcionalidades não estão finalizadas e por isso podem ser instáveis ou acabarem alteradas."</string>
<string name="screen_labs_header_title">"E que tal umas experiências?"</string>
<string name="screen_labs_title">"Experiências"</string>
<string name="screen_notification_settings_additional_settings_section_title">"Configurações adicionais"</string> <string name="screen_notification_settings_additional_settings_section_title">"Configurações adicionais"</string>
<string name="screen_notification_settings_calls_label">"Chamadas de áudio e vídeo"</string> <string name="screen_notification_settings_calls_label">"Chamadas de áudio e vídeo"</string>
<string name="screen_notification_settings_configuration_mismatch">"Incompatibilidade de configuração"</string> <string name="screen_notification_settings_configuration_mismatch">"Incompatibilidade de configuração"</string>

View file

@ -429,7 +429,7 @@ class DefaultBugReporterTest {
assertThat((param as WriteToFilesConfiguration.Enabled).directory).endsWith("/cache/logs/server.org") assertThat((param as WriteToFilesConfiguration.Enabled).directory).endsWith("/cache/logs/server.org")
assertThat((param as WriteToFilesConfiguration.Enabled).filenamePrefix).isEqualTo("logs") assertThat((param as WriteToFilesConfiguration.Enabled).filenamePrefix).isEqualTo("logs")
assertThat((param as WriteToFilesConfiguration.Enabled).numberOfFiles).isEqualTo(168) assertThat((param as WriteToFilesConfiguration.Enabled).numberOfFiles).isEqualTo(168)
assertThat((param as WriteToFilesConfiguration.Enabled).filenameSuffix).isEqualTo("log") assertThat((param as WriteToFilesConfiguration.Enabled).filenameSuffix).isEqualTo(".log")
} }
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
@ -491,7 +491,7 @@ class DefaultBugReporterTest {
assertThat((param as WriteToFilesConfiguration.Enabled).directory).endsWith("/cache/logs") assertThat((param as WriteToFilesConfiguration.Enabled).directory).endsWith("/cache/logs")
assertThat((param as WriteToFilesConfiguration.Enabled).filenamePrefix).isEqualTo("logs") assertThat((param as WriteToFilesConfiguration.Enabled).filenamePrefix).isEqualTo("logs")
assertThat((param as WriteToFilesConfiguration.Enabled).numberOfFiles).isEqualTo(168) assertThat((param as WriteToFilesConfiguration.Enabled).numberOfFiles).isEqualTo(168)
assertThat((param as WriteToFilesConfiguration.Enabled).filenameSuffix).isEqualTo("log") assertThat((param as WriteToFilesConfiguration.Enabled).filenameSuffix).isEqualTo(".log")
} }
@Test @Test

View file

@ -2,10 +2,13 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Administrador"</string> <string name="screen_room_change_permissions_administrators">"Administrador"</string>
<string name="screen_room_change_permissions_ban_people">"Banir pessoas"</string> <string name="screen_room_change_permissions_ban_people">"Banir pessoas"</string>
<string name="screen_room_change_permissions_change_settings">"Alterar configurações"</string>
<string name="screen_room_change_permissions_delete_messages">"Remover mensagens"</string> <string name="screen_room_change_permissions_delete_messages">"Remover mensagens"</string>
<string name="screen_room_change_permissions_everyone">"Membro"</string> <string name="screen_room_change_permissions_everyone">"Participante"</string>
<string name="screen_room_change_permissions_invite_people">"Convidar pessoas"</string> <string name="screen_room_change_permissions_invite_people">"Convidar pessoas"</string>
<string name="screen_room_change_permissions_member_moderation">"Gerir membros"</string> <string name="screen_room_change_permissions_manage_space">"Gerir espaço"</string>
<string name="screen_room_change_permissions_manage_space_rooms">"Gerir salas"</string>
<string name="screen_room_change_permissions_member_moderation">"Gerir participantes"</string>
<string name="screen_room_change_permissions_messages_and_content">"Mensagens e conteúdo"</string> <string name="screen_room_change_permissions_messages_and_content">"Mensagens e conteúdo"</string>
<string name="screen_room_change_permissions_moderators">"Moderador"</string> <string name="screen_room_change_permissions_moderators">"Moderador"</string>
<string name="screen_room_change_permissions_remove_people">"Remover pessoas"</string> <string name="screen_room_change_permissions_remove_people">"Remover pessoas"</string>
@ -14,6 +17,7 @@
<string name="screen_room_change_permissions_room_name">"Altera o nome da sala"</string> <string name="screen_room_change_permissions_room_name">"Altera o nome da sala"</string>
<string name="screen_room_change_permissions_room_topic">"Alterar a descrição da sala"</string> <string name="screen_room_change_permissions_room_topic">"Alterar a descrição da sala"</string>
<string name="screen_room_change_permissions_send_messages">"Enviar mensagens"</string> <string name="screen_room_change_permissions_send_messages">"Enviar mensagens"</string>
<string name="screen_room_change_permissions_title">"Permissões"</string>
<string name="screen_room_change_role_administrators_title">"Editar Administradores"</string> <string name="screen_room_change_role_administrators_title">"Editar Administradores"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Não poderás desfazer esta ação. Estás a promover o utilizador para ter o mesmo nível de poder que tu."</string> <string name="screen_room_change_role_confirm_add_admin_description">"Não poderás desfazer esta ação. Estás a promover o utilizador para ter o mesmo nível de poder que tu."</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Adicionar administrador?"</string> <string name="screen_room_change_role_confirm_add_admin_title">"Adicionar administrador?"</string>
@ -34,6 +38,12 @@
<string name="screen_room_change_role_unsaved_changes_description">"Tens alterações por guardar."</string> <string name="screen_room_change_role_unsaved_changes_description">"Tens alterações por guardar."</string>
<string name="screen_room_change_role_unsaved_changes_title">"Guardar alterações?"</string> <string name="screen_room_change_role_unsaved_changes_title">"Guardar alterações?"</string>
<string name="screen_room_member_list_banned_empty">"Não há nenhum utilizador banido."</string> <string name="screen_room_member_list_banned_empty">"Não há nenhum utilizador banido."</string>
<plurals name="screen_room_member_list_banned_header_title">
<item quantity="one">"%1$d banido"</item>
<item quantity="other">"%1$d banidos"</item>
</plurals>
<string name="screen_room_member_list_empty_search_subtitle">"Verifica a ortografia ou tenta uma nova pesquisa"</string>
<string name="screen_room_member_list_empty_search_title">"Sem resultados para “%1$s”"</string>
<plurals name="screen_room_member_list_header_title"> <plurals name="screen_room_member_list_header_title">
<item quantity="one">"%1$d pessoa"</item> <item quantity="one">"%1$d pessoa"</item>
<item quantity="other">"%1$d pessoas"</item> <item quantity="other">"%1$d pessoas"</item>
@ -41,10 +51,15 @@
<string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Remover e banir participante"</string> <string name="screen_room_member_list_manage_member_remove_confirmation_ban">"Remover e banir participante"</string>
<string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Remover apenas"</string> <string name="screen_room_member_list_manage_member_remove_confirmation_kick">"Remover apenas"</string>
<string name="screen_room_member_list_manage_member_unban_action">"Anular banimento"</string> <string name="screen_room_member_list_manage_member_unban_action">"Anular banimento"</string>
<string name="screen_room_member_list_manage_member_unban_message">"Poderão juntar-se novamente a esta sala se forem convidados."</string> <string name="screen_room_member_list_manage_member_unban_message">"Poderão entrar novamente nesta sala se forem convidados."</string>
<string name="screen_room_member_list_manage_member_unban_title">"Desbanir da sala"</string> <string name="screen_room_member_list_manage_member_unban_title">"Desbanir da sala"</string>
<string name="screen_room_member_list_mode_banned">"Banidos"</string> <string name="screen_room_member_list_mode_banned">"Banidos"</string>
<string name="screen_room_member_list_mode_members">"Participantes"</string> <string name="screen_room_member_list_mode_members">"Participantes"</string>
<plurals name="screen_room_member_list_pending_header_title">
<item quantity="one">"%1$d convidado"</item>
<item quantity="other">"%1$d convidados"</item>
</plurals>
<string name="screen_room_member_list_pending_status">"Pendente"</string>
<string name="screen_room_member_list_role_administrator">"Administrador"</string> <string name="screen_room_member_list_role_administrator">"Administrador"</string>
<string name="screen_room_member_list_role_moderator">"Moderador"</string> <string name="screen_room_member_list_role_moderator">"Moderador"</string>
<string name="screen_room_member_list_role_owner">"Dono / Dona"</string> <string name="screen_room_member_list_role_owner">"Dono / Dona"</string>
@ -59,10 +74,12 @@
<string name="screen_room_roles_and_permissions_messages_and_content">"Mensagens e conteúdo"</string> <string name="screen_room_roles_and_permissions_messages_and_content">"Mensagens e conteúdo"</string>
<string name="screen_room_roles_and_permissions_moderators">"Moderadores"</string> <string name="screen_room_roles_and_permissions_moderators">"Moderadores"</string>
<string name="screen_room_roles_and_permissions_owners">"Donos"</string> <string name="screen_room_roles_and_permissions_owners">"Donos"</string>
<string name="screen_room_roles_and_permissions_permissions_header">"Permissões"</string>
<string name="screen_room_roles_and_permissions_reset">"Repor permissões"</string> <string name="screen_room_roles_and_permissions_reset">"Repor permissões"</string>
<string name="screen_room_roles_and_permissions_reset_confirm_description">"Ao repores as permissões, perderás as configurações atuais."</string> <string name="screen_room_roles_and_permissions_reset_confirm_description">"Ao repores as permissões, perderás as configurações atuais."</string>
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Repor as permissões?"</string> <string name="screen_room_roles_and_permissions_reset_confirm_title">"Repor as permissões?"</string>
<string name="screen_room_roles_and_permissions_roles_header">"Cargos"</string> <string name="screen_room_roles_and_permissions_roles_header">"Cargos"</string>
<string name="screen_room_roles_and_permissions_room_details">"Detalhes da sala"</string> <string name="screen_room_roles_and_permissions_room_details">"Detalhes da sala"</string>
<string name="screen_room_roles_and_permissions_space_details">"Detalhes do espaço"</string>
<string name="screen_room_roles_and_permissions_title">"Cargos e permissões"</string> <string name="screen_room_roles_and_permissions_title">"Cargos e permissões"</string>
</resources> </resources>

View file

@ -88,7 +88,6 @@
<string name="screen_security_and_privacy_room_access_invite_only_option_description">"Хората могат да се присъединят само ако са поканени"</string> <string name="screen_security_and_privacy_room_access_invite_only_option_description">"Хората могат да се присъединят само ако са поканени"</string>
<string name="screen_security_and_privacy_room_access_invite_only_option_title">"Само с покана"</string> <string name="screen_security_and_privacy_room_access_invite_only_option_title">"Само с покана"</string>
<string name="screen_security_and_privacy_room_access_section_header">"Достъп до стаята"</string> <string name="screen_security_and_privacy_room_access_section_header">"Достъп до стаята"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_title">"Членове на пространството"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"Пространствата в момента не се поддържат"</string> <string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"Пространствата в момента не се поддържат"</string>
<string name="screen_security_and_privacy_room_directory_visibility_toggle_title">"Видима в директорията на обществените стаи"</string> <string name="screen_security_and_privacy_room_directory_visibility_toggle_title">"Видима в директорията на обществените стаи"</string>
<string name="screen_security_and_privacy_room_history_section_header">"Кой може да чете историята"</string> <string name="screen_security_and_privacy_room_history_section_header">"Кой може да чете историята"</string>

View file

@ -1,5 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="crypto_history_sharing_room_info_hidden_badge_content">"Noví členové nevidí historii"</string>
<string name="crypto_history_sharing_room_info_shared_badge_content">"Noví členové vidí historii"</string>
<string name="crypto_history_sharing_room_info_world_readable_badge_content">"Každý může vidět historii"</string>
<string name="screen_edit_room_address_room_address_section_footer">"Budete potřebovat adresu místnosti, aby byla viditelná v adresáři místností."</string> <string name="screen_edit_room_address_room_address_section_footer">"Budete potřebovat adresu místnosti, aby byla viditelná v adresáři místností."</string>
<string name="screen_edit_room_address_title">"Upravit adresu"</string> <string name="screen_edit_room_address_title">"Upravit adresu"</string>
<string name="screen_notification_settings_edit_failed_updating_default_mode">"Při aktualizaci nastavení oznámení došlo k chybě."</string> <string name="screen_notification_settings_edit_failed_updating_default_mode">"Při aktualizaci nastavení oznámení došlo k chybě."</string>

View file

@ -135,7 +135,6 @@ Nid ydym yn argymell galluogi amgryptio ar gyfer ystafelloedd y gall unrhyw un d
<string name="screen_security_and_privacy_room_access_invite_only_option_description">"Dim ond os cawn nhw wahoddiad gall pobl ymuno"</string> <string name="screen_security_and_privacy_room_access_invite_only_option_description">"Dim ond os cawn nhw wahoddiad gall pobl ymuno"</string>
<string name="screen_security_and_privacy_room_access_invite_only_option_title">"Gwahoddiad yn unig"</string> <string name="screen_security_and_privacy_room_access_invite_only_option_title">"Gwahoddiad yn unig"</string>
<string name="screen_security_and_privacy_room_access_section_header">"Mynediad ystafell"</string> <string name="screen_security_and_privacy_room_access_section_header">"Mynediad ystafell"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_title">"Aelodau gofod"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"Nid yw gofodau\'n cael eu cefnogi ar hyn o bryd"</string> <string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"Nid yw gofodau\'n cael eu cefnogi ar hyn o bryd"</string>
<string name="screen_security_and_privacy_room_address_section_footer">"Bydd angen cyfeiriad ystafell arnoch i\'w wneud yn weladwy yn y cyfeiriadur."</string> <string name="screen_security_and_privacy_room_address_section_footer">"Bydd angen cyfeiriad ystafell arnoch i\'w wneud yn weladwy yn y cyfeiriadur."</string>
<string name="screen_security_and_privacy_room_address_section_header">"Cyfeiriad yr ystafell"</string> <string name="screen_security_and_privacy_room_address_section_header">"Cyfeiriad yr ystafell"</string>

View file

@ -150,7 +150,6 @@ Vi anbefaler ikke at aktivere kryptering for rum, som alle kan finde og deltage
<string name="screen_security_and_privacy_room_access_section_header">"Adgang"</string> <string name="screen_security_and_privacy_room_access_section_header">"Adgang"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_multiple_parents_description">"Alle i autoriserede grupper kan deltage."</string> <string name="screen_security_and_privacy_room_access_space_members_option_multiple_parents_description">"Alle i autoriserede grupper kan deltage."</string>
<string name="screen_security_and_privacy_room_access_space_members_option_single_parent_description">"Alle i %1$s kan deltage."</string> <string name="screen_security_and_privacy_room_access_space_members_option_single_parent_description">"Alle i %1$s kan deltage."</string>
<string name="screen_security_and_privacy_room_access_space_members_option_title">"Medlemmer af gruppen"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"Grupper understøttes ikke i øjeblikket"</string> <string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"Grupper understøttes ikke i øjeblikket"</string>
<string name="screen_security_and_privacy_room_address_section_footer">"Du skal bruge en adresse for at gøre det synligt i det offentlige register."</string> <string name="screen_security_and_privacy_room_address_section_footer">"Du skal bruge en adresse for at gøre det synligt i det offentlige register."</string>
<string name="screen_security_and_privacy_room_address_section_header">"Adresse"</string> <string name="screen_security_and_privacy_room_address_section_header">"Adresse"</string>

View file

@ -150,7 +150,6 @@ Wir empfehlen keine Verschlüsselung für Chats zu aktivieren, die jeder finden
<string name="screen_security_and_privacy_room_access_section_header">"Zugang"</string> <string name="screen_security_and_privacy_room_access_section_header">"Zugang"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_multiple_parents_description">"Jeder in autorisierten Spaces kann beitreten."</string> <string name="screen_security_and_privacy_room_access_space_members_option_multiple_parents_description">"Jeder in autorisierten Spaces kann beitreten."</string>
<string name="screen_security_and_privacy_room_access_space_members_option_single_parent_description">"Jeder in %1$s kann beitreten."</string> <string name="screen_security_and_privacy_room_access_space_members_option_single_parent_description">"Jeder in %1$s kann beitreten."</string>
<string name="screen_security_and_privacy_room_access_space_members_option_title">"Spacemitglieder"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"Spaces werden zur Zeit nicht unterstützt."</string> <string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"Spaces werden zur Zeit nicht unterstützt."</string>
<string name="screen_security_and_privacy_room_address_section_footer">"Du benötigst eine Chat-Adresse, um den Chat im öffentlichen Verzeichnis sichtbar zu machen."</string> <string name="screen_security_and_privacy_room_address_section_footer">"Du benötigst eine Chat-Adresse, um den Chat im öffentlichen Verzeichnis sichtbar zu machen."</string>
<string name="screen_security_and_privacy_room_address_section_header">"Adresse"</string> <string name="screen_security_and_privacy_room_address_section_header">"Adresse"</string>

View file

@ -120,7 +120,6 @@
<string name="screen_security_and_privacy_room_access_invite_only_option_description">"Τα άτομα μπορούν να συμμετάσχουν μόνο εάν έχουν προσκληθεί"</string> <string name="screen_security_and_privacy_room_access_invite_only_option_description">"Τα άτομα μπορούν να συμμετάσχουν μόνο εάν έχουν προσκληθεί"</string>
<string name="screen_security_and_privacy_room_access_invite_only_option_title">"Μόνο πρόσκληση"</string> <string name="screen_security_and_privacy_room_access_invite_only_option_title">"Μόνο πρόσκληση"</string>
<string name="screen_security_and_privacy_room_access_section_header">"Πρόσβαση στην αίθουσα"</string> <string name="screen_security_and_privacy_room_access_section_header">"Πρόσβαση στην αίθουσα"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_title">"Μέλη χώρου"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"Οι χώροι δεν υποστηρίζονται προς το παρόν"</string> <string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"Οι χώροι δεν υποστηρίζονται προς το παρόν"</string>
<string name="screen_security_and_privacy_room_address_section_footer">"Θα χρειαστείτε μια διεύθυνση αίθουσας για να την κάνετε ορατή στον κατάλογο."</string> <string name="screen_security_and_privacy_room_address_section_footer">"Θα χρειαστείτε μια διεύθυνση αίθουσας για να την κάνετε ορατή στον κατάλογο."</string>
<string name="screen_security_and_privacy_room_directory_visibility_section_footer">"Επιστρέψτε την εύρεση αυτής της αίθουσας με αναζήτηση στον κατάλογο %1$s δημοσίων αιθουσών"</string> <string name="screen_security_and_privacy_room_directory_visibility_section_footer">"Επιστρέψτε την εύρεση αυτής της αίθουσας με αναζήτηση στον κατάλογο %1$s δημοσίων αιθουσών"</string>

View file

@ -118,7 +118,6 @@ No recomendamos habilitar el cifrado para las salas que cualquiera pueda encontr
<string name="screen_security_and_privacy_room_access_invite_only_option_description">"Las personas solo pueden unirse si están invitadas"</string> <string name="screen_security_and_privacy_room_access_invite_only_option_description">"Las personas solo pueden unirse si están invitadas"</string>
<string name="screen_security_and_privacy_room_access_invite_only_option_title">"Solo por invitación"</string> <string name="screen_security_and_privacy_room_access_invite_only_option_title">"Solo por invitación"</string>
<string name="screen_security_and_privacy_room_access_section_header">"Acceso a la sala"</string> <string name="screen_security_and_privacy_room_access_section_header">"Acceso a la sala"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_title">"Miembros del espacio"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"No se admiten los espacios por el momento."</string> <string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"No se admiten los espacios por el momento."</string>
<string name="screen_security_and_privacy_room_address_section_footer">"Necesitarás una dirección de sala para que sea visible en el directorio."</string> <string name="screen_security_and_privacy_room_address_section_footer">"Necesitarás una dirección de sala para que sea visible en el directorio."</string>
<string name="screen_security_and_privacy_room_directory_visibility_section_footer">"Permite encontrar esta sala buscando en el directorio de salas públicas de %1$s"</string> <string name="screen_security_and_privacy_room_directory_visibility_section_footer">"Permite encontrar esta sala buscando en el directorio de salas públicas de %1$s"</string>

View file

@ -106,7 +106,6 @@
<string name="screen_security_and_privacy_room_access_invite_only_option_description">"Gonbidatutako pertsonak bakarrik sartu ahal izango dira"</string> <string name="screen_security_and_privacy_room_access_invite_only_option_description">"Gonbidatutako pertsonak bakarrik sartu ahal izango dira"</string>
<string name="screen_security_and_privacy_room_access_invite_only_option_title">"Gonbidapen bidez"</string> <string name="screen_security_and_privacy_room_access_invite_only_option_title">"Gonbidapen bidez"</string>
<string name="screen_security_and_privacy_room_access_section_header">"Gelarako sarbidea"</string> <string name="screen_security_and_privacy_room_access_section_header">"Gelarako sarbidea"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_title">"Guneko kideak"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"Gaur-gaurkoz ez da guneekin bateragarria"</string> <string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"Gaur-gaurkoz ez da guneekin bateragarria"</string>
<string name="screen_security_and_privacy_room_address_section_header">"Gelaren helbidea"</string> <string name="screen_security_and_privacy_room_address_section_header">"Gelaren helbidea"</string>
<string name="screen_security_and_privacy_room_directory_visibility_toggle_title">"Gela publikoen direktorioan ikusgai"</string> <string name="screen_security_and_privacy_room_directory_visibility_toggle_title">"Gela publikoen direktorioan ikusgai"</string>

View file

@ -123,7 +123,6 @@
<string name="screen_security_and_privacy_room_access_invite_only_option_description">"افراد فقط در صورت دعوت می‌توانند بپیوندند"</string> <string name="screen_security_and_privacy_room_access_invite_only_option_description">"افراد فقط در صورت دعوت می‌توانند بپیوندند"</string>
<string name="screen_security_and_privacy_room_access_invite_only_option_title">"فقط دعوتی"</string> <string name="screen_security_and_privacy_room_access_invite_only_option_title">"فقط دعوتی"</string>
<string name="screen_security_and_privacy_room_access_section_header">"دسترسی اتاق"</string> <string name="screen_security_and_privacy_room_access_section_header">"دسترسی اتاق"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_title">"اعضای فضا"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"در حال حاضر فضاها پشتیبانی نمی‌شوند"</string> <string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"در حال حاضر فضاها پشتیبانی نمی‌شوند"</string>
<string name="screen_security_and_privacy_room_address_section_header">"نشانی اتاق"</string> <string name="screen_security_and_privacy_room_address_section_header">"نشانی اتاق"</string>
<string name="screen_security_and_privacy_room_history_anyone_option_title">"هرکسی"</string> <string name="screen_security_and_privacy_room_history_anyone_option_title">"هرکسی"</string>

View file

@ -132,7 +132,6 @@ Emme suosittele salauksen ottamista käyttöön huoneissa, jotka kuka tahansa vo
<string name="screen_security_and_privacy_room_access_invite_only_option_description">"Vain kutsutut henkilöt voivat liittyä."</string> <string name="screen_security_and_privacy_room_access_invite_only_option_description">"Vain kutsutut henkilöt voivat liittyä."</string>
<string name="screen_security_and_privacy_room_access_invite_only_option_title">"Vain kutsutut"</string> <string name="screen_security_and_privacy_room_access_invite_only_option_title">"Vain kutsutut"</string>
<string name="screen_security_and_privacy_room_access_section_header">"Pääsy"</string> <string name="screen_security_and_privacy_room_access_section_header">"Pääsy"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_title">"Tilan jäsenet"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"Tiloja ei tällä hetkellä tueta"</string> <string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"Tiloja ei tällä hetkellä tueta"</string>
<string name="screen_security_and_privacy_room_address_section_footer">"Tarvitset osoitteen, jotta se näkyy julkisessa hakemistossa."</string> <string name="screen_security_and_privacy_room_address_section_footer">"Tarvitset osoitteen, jotta se näkyy julkisessa hakemistossa."</string>
<string name="screen_security_and_privacy_room_address_section_header">"Osoite"</string> <string name="screen_security_and_privacy_room_address_section_header">"Osoite"</string>

View file

@ -151,7 +151,6 @@ Ne preporučujemo omogućavanje šifriranja za sobe koje svatko može pronaći i
<string name="screen_security_and_privacy_room_access_section_header">"Pristup"</string> <string name="screen_security_and_privacy_room_access_section_header">"Pristup"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_multiple_parents_description">"Svatko tko se nalazi u ovlaštenim prostorima može se pridružiti."</string> <string name="screen_security_and_privacy_room_access_space_members_option_multiple_parents_description">"Svatko tko se nalazi u ovlaštenim prostorima može se pridružiti."</string>
<string name="screen_security_and_privacy_room_access_space_members_option_single_parent_description">"Svatko u %1$s može se pridružiti."</string> <string name="screen_security_and_privacy_room_access_space_members_option_single_parent_description">"Svatko u %1$s može se pridružiti."</string>
<string name="screen_security_and_privacy_room_access_space_members_option_title">"Članovi prostora"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"Prostori trenutačno nisu podržani"</string> <string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"Prostori trenutačno nisu podržani"</string>
<string name="screen_security_and_privacy_room_address_section_footer">"Trebat će vam adresa kako bi bila vidljiva u javnom direktoriju."</string> <string name="screen_security_and_privacy_room_address_section_footer">"Trebat će vam adresa kako bi bila vidljiva u javnom direktoriju."</string>
<string name="screen_security_and_privacy_room_address_section_header">"Adresa"</string> <string name="screen_security_and_privacy_room_address_section_header">"Adresa"</string>

View file

@ -150,7 +150,6 @@ Nem javasoljuk a titkosítás engedélyezését az olyan szobákban, amelyeket b
<string name="screen_security_and_privacy_room_access_section_header">"Hozzáférés"</string> <string name="screen_security_and_privacy_room_access_section_header">"Hozzáférés"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_multiple_parents_description">"Bárki csatlakozhat, az engedélyezett terekből."</string> <string name="screen_security_and_privacy_room_access_space_members_option_multiple_parents_description">"Bárki csatlakozhat, az engedélyezett terekből."</string>
<string name="screen_security_and_privacy_room_access_space_members_option_single_parent_description">"Bárki csatlakozhatnak innen: %1$s."</string> <string name="screen_security_and_privacy_room_access_space_members_option_single_parent_description">"Bárki csatlakozhatnak innen: %1$s."</string>
<string name="screen_security_and_privacy_room_access_space_members_option_title">"A tér tagjai"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"A terek jelenleg nem támogatottak"</string> <string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"A terek jelenleg nem támogatottak"</string>
<string name="screen_security_and_privacy_room_address_section_footer">"Szüksége lesz egy szobacímre, hogy láthatóvá tegye a szobakatalógusban."</string> <string name="screen_security_and_privacy_room_address_section_footer">"Szüksége lesz egy szobacímre, hogy láthatóvá tegye a szobakatalógusban."</string>
<string name="screen_security_and_privacy_room_address_section_header">"Cím"</string> <string name="screen_security_and_privacy_room_address_section_header">"Cím"</string>

View file

@ -117,7 +117,6 @@ Kami tidak menyarankan untuk mengaktifkan enkripsi untuk ruangan yang dapat dite
<string name="screen_security_and_privacy_room_access_invite_only_option_description">"Orang hanya dapat bergabung jika mereka diundang"</string> <string name="screen_security_and_privacy_room_access_invite_only_option_description">"Orang hanya dapat bergabung jika mereka diundang"</string>
<string name="screen_security_and_privacy_room_access_invite_only_option_title">"Hanya undangan"</string> <string name="screen_security_and_privacy_room_access_invite_only_option_title">"Hanya undangan"</string>
<string name="screen_security_and_privacy_room_access_section_header">"Akses ruangan"</string> <string name="screen_security_and_privacy_room_access_section_header">"Akses ruangan"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_title">"Anggota space"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"Space saat ini tidak didukung"</string> <string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"Space saat ini tidak didukung"</string>
<string name="screen_security_and_privacy_room_address_section_footer">"Anda akan memerlukan alamat ruangan untuk membuatnya terlihat dalam direktori."</string> <string name="screen_security_and_privacy_room_address_section_footer">"Anda akan memerlukan alamat ruangan untuk membuatnya terlihat dalam direktori."</string>
<string name="screen_security_and_privacy_room_directory_visibility_section_footer">"Izinkan ruangan ini ditemukan dengan mencari direktori ruangan %1$s publik"</string> <string name="screen_security_and_privacy_room_directory_visibility_section_footer">"Izinkan ruangan ini ditemukan dengan mencari direktori ruangan %1$s publik"</string>

View file

@ -143,7 +143,6 @@ Non consigliamo di attivare la crittografia per le stanze che chiunque può trov
<string name="screen_security_and_privacy_room_access_invite_only_option_description">"Solo le persone invitate possono entrare."</string> <string name="screen_security_and_privacy_room_access_invite_only_option_description">"Solo le persone invitate possono entrare."</string>
<string name="screen_security_and_privacy_room_access_invite_only_option_title">"Solo su invito"</string> <string name="screen_security_and_privacy_room_access_invite_only_option_title">"Solo su invito"</string>
<string name="screen_security_and_privacy_room_access_section_header">"Accesso"</string> <string name="screen_security_and_privacy_room_access_section_header">"Accesso"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_title">"Membri dello spazio"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"Gli spazi non sono attualmente supportati"</string> <string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"Gli spazi non sono attualmente supportati"</string>
<string name="screen_security_and_privacy_room_address_section_footer">"Per renderlo visibile nell\'elenco pubblico, avrai bisogno di un indirizzo."</string> <string name="screen_security_and_privacy_room_address_section_footer">"Per renderlo visibile nell\'elenco pubblico, avrai bisogno di un indirizzo."</string>
<string name="screen_security_and_privacy_room_address_section_header">"Indirizzo"</string> <string name="screen_security_and_privacy_room_address_section_header">"Indirizzo"</string>

View file

@ -124,7 +124,6 @@
<string name="screen_security_and_privacy_room_access_invite_only_option_description">"초대받은 사용자만 가입할 수 있습니다."</string> <string name="screen_security_and_privacy_room_access_invite_only_option_description">"초대받은 사용자만 가입할 수 있습니다."</string>
<string name="screen_security_and_privacy_room_access_invite_only_option_title">"초대 전용"</string> <string name="screen_security_and_privacy_room_access_invite_only_option_title">"초대 전용"</string>
<string name="screen_security_and_privacy_room_access_section_header">"방 액세스"</string> <string name="screen_security_and_privacy_room_access_section_header">"방 액세스"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_title">"스페이스 멤버들"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"스페이스는 현재 지원되지 않습니다"</string> <string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"스페이스는 현재 지원되지 않습니다"</string>
<string name="screen_security_and_privacy_room_address_section_footer">"디렉토리에 표시하려면 방 주소가 필요합니다."</string> <string name="screen_security_and_privacy_room_address_section_footer">"디렉토리에 표시하려면 방 주소가 필요합니다."</string>
<string name="screen_security_and_privacy_room_directory_visibility_section_footer">"%1$s 공개 방 디렉토리에서 이 방을 검색할 수 있도록 허용합니다"</string> <string name="screen_security_and_privacy_room_directory_visibility_section_footer">"%1$s 공개 방 디렉토리에서 이 방을 검색할 수 있도록 허용합니다"</string>

View file

@ -149,7 +149,6 @@ Vi anbefaler ikke å aktivere kryptering for rom som hvem som helst kan finne og
<string name="screen_security_and_privacy_room_access_section_header">"Tilgang"</string> <string name="screen_security_and_privacy_room_access_section_header">"Tilgang"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_multiple_parents_description">"Alle i autoriserte områder kan bli med."</string> <string name="screen_security_and_privacy_room_access_space_members_option_multiple_parents_description">"Alle i autoriserte områder kan bli med."</string>
<string name="screen_security_and_privacy_room_access_space_members_option_single_parent_description">"Alle i %1$s kan bli med."</string> <string name="screen_security_and_privacy_room_access_space_members_option_single_parent_description">"Alle i %1$s kan bli med."</string>
<string name="screen_security_and_privacy_room_access_space_members_option_title">"Medlemmer av område"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"Områder støttes ikke for øyeblikket"</string> <string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"Områder støttes ikke for øyeblikket"</string>
<string name="screen_security_and_privacy_room_address_section_footer">"Du trenger en adresse for å gjøre den synlig i den offentlige katalogen."</string> <string name="screen_security_and_privacy_room_address_section_footer">"Du trenger en adresse for å gjøre den synlig i den offentlige katalogen."</string>
<string name="screen_security_and_privacy_room_address_section_header">"Adresse"</string> <string name="screen_security_and_privacy_room_address_section_header">"Adresse"</string>

View file

@ -130,7 +130,6 @@ Odradzamy włączanie szyfrowania dla pokoi, które każdy może znaleźć i do
<string name="screen_security_and_privacy_room_access_invite_only_option_description">"Tylko osoby z zaproszeniem mogą dołączyć"</string> <string name="screen_security_and_privacy_room_access_invite_only_option_description">"Tylko osoby z zaproszeniem mogą dołączyć"</string>
<string name="screen_security_and_privacy_room_access_invite_only_option_title">"Tylko zaproszenie"</string> <string name="screen_security_and_privacy_room_access_invite_only_option_title">"Tylko zaproszenie"</string>
<string name="screen_security_and_privacy_room_access_section_header">"Dostęp do pokoju"</string> <string name="screen_security_and_privacy_room_access_section_header">"Dostęp do pokoju"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_title">"Członkowie przestrzeni"</string>
<string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"Przestrzenie nie są obecnie wspierane"</string> <string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"Przestrzenie nie są obecnie wspierane"</string>
<string name="screen_security_and_privacy_room_address_section_footer">"Aby pokój był widoczny w katalogu, potrzebny jest adres pokoju."</string> <string name="screen_security_and_privacy_room_address_section_footer">"Aby pokój był widoczny w katalogu, potrzebny jest adres pokoju."</string>
<string name="screen_security_and_privacy_room_address_section_header">"Adres pokoju"</string> <string name="screen_security_and_privacy_room_address_section_header">"Adres pokoju"</string>

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