diff --git a/.github/workflows/update-gradle-wrapper.yml b/.github/workflows/update-gradle-wrapper.yml new file mode 100644 index 0000000000..4a786a9339 --- /dev/null +++ b/.github/workflows/update-gradle-wrapper.yml @@ -0,0 +1,18 @@ +name: Update Gradle Wrapper + +on: + schedule: + - cron: "0 0 * * *" + +jobs: + update-gradle-wrapper: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Update Gradle Wrapper + uses: gradle-update/update-gradle-wrapper-action@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + target-branch: develop diff --git a/.idea/dictionaries/bmarty.xml b/.idea/dictionaries/bmarty.xml index 5c27da044e..d13e40248f 100644 --- a/.idea/dictionaries/bmarty.xml +++ b/.idea/dictionaries/bmarty.xml @@ -29,6 +29,7 @@ signout signup ssss + sygnal threepid unwedging diff --git a/CHANGES.md b/CHANGES.md index 6837fa8313..99058117b6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,62 @@ +Changes in Element 1.0.9 (2020-10-16) +=================================================== + +Features ✨: + - Search messages in a room - phase 1 (#2110) + - Hide encrypted history (before user is invited). Can be shown if wanted in developer settings + - Changed rainbow algorithm + +Improvements 🙌: + - Wording differentiation for direct rooms (#2176) + - PIN code: request PIN code if phone has been locked + - Small optimisation of scrolling experience in timeline (#2114) + - Allow user to reset cross signing if he has no way to recover (#2052) + - Ability to share text + - Create home shortcut for any room (#1525) + - Can't confirm email due to killing by Android (#2021) + - Add a menu item to open the setting in room list and in room (#2171) + - Add a menu item in the timeline as a shortcut to invite user (#2171) + - Drawer: move settings access and add sign out action (#2171) + - Filter room member (and banned users) by name (#2184) + - Implement "Jump to read receipt" and "Mention" actions on the room member profile screen + - Direct share (#2029) + - Add FAB to room members list (#2226) + - Add Sygnal API implementation to test is Push are correctly received + - Add PushGateway API implementation to test if Push are correctly received + - Cross signing: shouldn't offer to verify with other session when there is not. (#2227) + +Bugfix 🐛: + - Improve support for image/audio/video/file selection with intent changes (#1376) + - Fix Splash layout on small screens + - Invalid popup when pressing back (#1635) + - Simplifies draft management and should fix bunch of draft issues (#952, #683) + - Very long topic cannot be fully visible (#1957) + - Properly detect cross signing keys reset + - Don't set presence when handling a push notification or polling (#2156) + - Be robust against `StrandHogg` task injection + - Clear alerts if user sign out + - Fix rows are hidden in Textinput (#2234) + - Uploading a file to a room caused it to have a info.size of -1 (#2141) + +Translations 🗣: + - Move store data to `/fastlane/metadata/android` (#812) + - Weblate is now hosted at https://translate.element.io + +SDK API changes ⚠️: + - Search messages in a room by using Session.searchService() or Room.search() + +Build 🧱: + - Use Update Gradle Wrapper Action + - Updates Gradle Wrapper from 5.6.4 to 6.6.1. (#2193) + - Upgrade kotlin version from `1.3.72` to `1.4.10` and kotlin coroutines version from `1.3.8` to `1.3.9` + - Upgrade build tools from `3.5.3` to `4.0.1`, then to `4.1.0` + - Upgrade com.google.gms:google-services from `4.3.2` to `4.3.4` + - Upgrade Moshi to `1.11.0`, Dagger to `2.29.1`, Epoxy to `4.1.0` + +Other changes: + - Added registration/verification automated UI tests + - Create a script to help getting public information form any homeserver + Changes in Element 1.0.8 (2020-09-25) =================================================== @@ -83,7 +142,7 @@ Bugfix 🐛: - Replies to poll appears in timeline as unsupported events during sending (#1004) Translations 🗣: - - The SDK is now using SAS string translations from [Weblate Matrix-doc project](https://translate.riot.im/projects/matrix-doc/) (#1909) + - The SDK is now using SAS string translations from [Weblate Matrix-doc project](https://translate.element.io/projects/matrix-doc/) (#1909) - New translation to kabyle Build 🧱: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f10c87cdbe..3464fd9e76 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -40,7 +40,7 @@ For now, the Matrix SDK and the Element application are in the same project. So ## I want to help translating Element If you want to fix an issue with an English string, please submit a PR. -If you want to fix an issue in other languages, or add a missing translation, or even add a new language, please use [Weblate](https://translate.riot.im/projects/element-android/). +If you want to fix an issue in other languages, or add a missing translation, or even add a new language, please use [Weblate](https://translate.element.io/projects/element-android/). ## I want to submit a PR to fix an issue diff --git a/README.md b/README.md index 64c6c9d04d..e89fb15010 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ [![Buildkite](https://badge.buildkite.com/ad0065c1b70f557cd3b1d3d68f9c2154010f83c4d6f71706a9.svg?branch=develop)](https://buildkite.com/matrix-dot-org/element-android/builds?branch=develop) -[![Weblate](https://translate.riot.im/widgets/element-android/-/svg-badge.svg)](https://translate.riot.im/engage/element-android/?utm_source=widget) +[![Weblate](https://translate.element.io/widgets/element-android/-/svg-badge.svg)](https://translate.element.io/engage/element-android/?utm_source=widget) [![Element Android Matrix room #element-android:matrix.org](https://img.shields.io/matrix/element-android:matrix.org.svg?label=%23element-android:matrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#element-android:matrix.org) [![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=im.vector.app.android&metric=alert_status)](https://sonarcloud.io/dashboard?id=im.vector.app.android) [![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=im.vector.app.android&metric=vulnerabilities)](https://sonarcloud.io/dashboard?id=im.vector.app.android) diff --git a/attachment-viewer/build.gradle b/attachment-viewer/build.gradle index 3a5c3298d4..91ddd519df 100644 --- a/attachment-viewer/build.gradle +++ b/attachment-viewer/build.gradle @@ -58,21 +58,16 @@ android { } dependencies { - implementation 'com.github.chrisbanes:PhotoView:2.0.0' + implementation 'com.github.chrisbanes:PhotoView:2.1.4' implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0' implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' - implementation fileTree(dir: "libs", include: ["*.jar"]) implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation 'androidx.core:core-ktx:1.3.0' - implementation 'androidx.appcompat:appcompat:1.1.0' - implementation 'com.google.android.material:material:1.1.0' - implementation 'androidx.constraintlayout:constraintlayout:1.1.3' - implementation 'androidx.navigation:navigation-fragment-ktx:2.1.0' - implementation 'androidx.navigation:navigation-ui-ktx:2.1.0' - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test.ext:junit:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + implementation 'androidx.core:core-ktx:1.3.2' + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation "androidx.fragment:fragment:1.3.0-beta01" + implementation "androidx.recyclerview:recyclerview:1.1.0" + implementation 'com.google.android.material:material:1.2.1' } \ No newline at end of file diff --git a/build.gradle b/build.gradle index f06d1859b5..05dcaa43ed 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,9 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.3.72' + // Ref: https://kotlinlang.org/releases.html + ext.kotlin_version = '1.4.10' + ext.kotlin_coroutines_version = "1.3.9" repositories { google() jcenter() @@ -10,10 +12,8 @@ buildscript { } } dependencies { - // Warning: 3.6.3 leads to infinite gradle builds. Stick to 3.5.3 for the moment - classpath 'com.android.tools.build:gradle:3.5.3' - classpath 'com.google.gms:google-services:4.3.2' - classpath "com.airbnb.okreplay:gradle-plugin:1.5.0" + classpath 'com.android.tools.build:gradle:4.1.0' + classpath 'com.google.gms:google-services:4.3.4' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.7.1' classpath 'com.google.android.gms:oss-licenses-plugin:0.10.2' @@ -64,7 +64,8 @@ allprojects { tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { // Warnings are potential errors, so stop ignoring them - kotlinOptions.allWarningsAsErrors = true + // You can override by passing `-PallWarningsAsErrors=false` in the command line + kotlinOptions.allWarningsAsErrors = project.properties['allWarningsAsErrors']?.toBoolean() ?: true } } diff --git a/docs/ui-tests.md b/docs/ui-tests.md new file mode 100644 index 0000000000..ff01da0b31 --- /dev/null +++ b/docs/ui-tests.md @@ -0,0 +1,107 @@ +# Automate user interface tests + +Element Android ensures that some fundamental flows are properly working by running automated user interface tests. +Ui tests are using the android [Espresso](https://developer.android.com/training/testing/espresso) library. + +Tests can be run on a real device, or on a virtual device (such as the emulator in Android Studio). + +Currently the test are covering a small set of application flows: + - Registration + - Self verification via emoji + - Self verification via passphrase + +## Prerequisites: + +Out of the box, the tests use one of the homeservers (located at http://localhost:8080) of the "Demo Federation of Homeservers" (https://github.com/matrix-org/synapse#running-a-demo-federation-of-synapses). + +You first need to follow instructions to set up Synapse in development mode at https://github.com/matrix-org/synapse#synapse-development. If you have already installed all dependencies, the steps are: + +```shell script +$ git clone https://github.com/matrix-org/synapse.git +$ cd synapse +$ virtualenv -p python3 env +$ source env/bin/activate +(env) $ python -m pip install --no-use-pep517 -e . +``` + +Every time you want to launch these test homeservers, type: + +```shell script +$ virtualenv -p python3 env +$ source env/bin/activate +(env) $ demo/start.sh --no-rate-limit +``` + +**Emulator/Device set up** + +When running the test via android studio on a device, you have to disable system animations in order for the test to work properly. + +First, ensure developer mode is enabled: + +- To enable developer options, tap the **Build Number** option 7 times. You can find this option in one of the following locations, depending on your Android version: + + - Android 9 (API level 28) and higher: **Settings > About Phone > Build Number** + - Android 8.0.0 (API level 26) and Android 8.1.0 (API level 26): **Settings > System > About Phone > Build Number** + - Android 7.1 (API level 25) and lower: **Settings > About Phone > Build Number** + +On your device, under **Settings > Developer options**, disable the following 3 settings: + +- Window animation scale +- Transition animation scale +- Animator duration scale + +## Run the tests + +Once Synapse is running, and an emulator is running, you can run the UI tests. + +### From the source code + +Click on the green arrow in front of each test. Clicking on the arrow in front of the test class, or from the package directory does not always work (Tests not found issue). + +### From command line + +````shell script +./gradlew vector:connectedGplayDebugAndroidTest +```` + +To run all the tests from the `vector` module. + +In case of trouble, you can try to uninstall the previous installed test APK first with this command: + +```shell script +adb uninstall im.vector.app.debug.test +``` +## Recipes + +We added some specific Espresso IdlingResources, and other utilities for matrix related tests + +### Wait for initial sync + +```kotlin +// Wait for initial sync and check room list is there +withIdlingResource(initialSyncIdlingResource(uiSession)) { + onView(withId(R.id.roomListContainer)) + .check(matches(isDisplayed())) +} +``` + +### Accessing current activity + +```kotlin + val activity = EspressoHelper.getCurrentActivity()!! + val uiSession = (activity as HomeActivity).activeSessionHolder.getActiveSession() +``` + +### Interact with other session + +It's possible to create a session via the SDK, and then use this session to interact with the one that the emulator is using (to check verifications for example) + +```kotlin +@Before +fun initAccount() { + val context = InstrumentationRegistry.getInstrumentation().targetContext + val matrix = Matrix.getInstance(context) + val userName = "foobar_${System.currentTimeMillis()}" + existingSession = createAccountAndSync(matrix, userName, password, true) +} +``` diff --git a/vector/src/main/play/listings/de/full_description.txt b/fastlane/metadata/android/de/full_description.txt similarity index 100% rename from vector/src/main/play/listings/de/full_description.txt rename to fastlane/metadata/android/de/full_description.txt diff --git a/vector/src/main/play/listings/de/short_description.txt b/fastlane/metadata/android/de/short_description.txt similarity index 100% rename from vector/src/main/play/listings/de/short_description.txt rename to fastlane/metadata/android/de/short_description.txt diff --git a/vector/src/main/play/listings/de/title.txt b/fastlane/metadata/android/de/title.txt similarity index 100% rename from vector/src/main/play/listings/de/title.txt rename to fastlane/metadata/android/de/title.txt diff --git a/vector/src/main/play/listings/en-US/full_description.txt b/fastlane/metadata/android/en-US/full_description.txt similarity index 100% rename from vector/src/main/play/listings/en-US/full_description.txt rename to fastlane/metadata/android/en-US/full_description.txt diff --git a/vector/src/main/play/listings/en-US/short_description.txt b/fastlane/metadata/android/en-US/short_description.txt similarity index 100% rename from vector/src/main/play/listings/en-US/short_description.txt rename to fastlane/metadata/android/en-US/short_description.txt diff --git a/vector/src/main/play/listings/en-US/title.txt b/fastlane/metadata/android/en-US/title.txt similarity index 100% rename from vector/src/main/play/listings/en-US/title.txt rename to fastlane/metadata/android/en-US/title.txt diff --git a/vector/src/main/play/listings/es/full_description.txt b/fastlane/metadata/android/es/full_description.txt similarity index 100% rename from vector/src/main/play/listings/es/full_description.txt rename to fastlane/metadata/android/es/full_description.txt diff --git a/vector/src/main/play/listings/es/short_description.txt b/fastlane/metadata/android/es/short_description.txt similarity index 100% rename from vector/src/main/play/listings/es/short_description.txt rename to fastlane/metadata/android/es/short_description.txt diff --git a/vector/src/main/play/listings/es/title.txt b/fastlane/metadata/android/es/title.txt similarity index 100% rename from vector/src/main/play/listings/es/title.txt rename to fastlane/metadata/android/es/title.txt diff --git a/fastlane/metadata/android/et/short_description.txt b/fastlane/metadata/android/et/short_description.txt new file mode 100644 index 0000000000..4075c1f7cf --- /dev/null +++ b/fastlane/metadata/android/et/short_description.txt @@ -0,0 +1 @@ +Turvalised ning hajutatud vestlused ja VoIP-kõned. Sinu suhtlus on üliturvaline. diff --git a/fastlane/metadata/android/et/title.txt b/fastlane/metadata/android/et/title.txt new file mode 100644 index 0000000000..f74f9ff18f --- /dev/null +++ b/fastlane/metadata/android/et/title.txt @@ -0,0 +1 @@ +Element (varem Riot.im) diff --git a/vector/src/main/play/listings/fi/short_description.txt b/fastlane/metadata/android/fi/short_description.txt similarity index 100% rename from vector/src/main/play/listings/fi/short_description.txt rename to fastlane/metadata/android/fi/short_description.txt diff --git a/vector/src/main/play/listings/fi/title.txt b/fastlane/metadata/android/fi/title.txt similarity index 100% rename from vector/src/main/play/listings/fi/title.txt rename to fastlane/metadata/android/fi/title.txt diff --git a/vector/src/main/play/listings/fr/title.txt b/fastlane/metadata/android/fr/title.txt similarity index 100% rename from vector/src/main/play/listings/fr/title.txt rename to fastlane/metadata/android/fr/title.txt diff --git a/vector/src/main/play/listings/hu/full_description.txt b/fastlane/metadata/android/hu/full_description.txt similarity index 100% rename from vector/src/main/play/listings/hu/full_description.txt rename to fastlane/metadata/android/hu/full_description.txt diff --git a/vector/src/main/play/listings/hu/short_description.txt b/fastlane/metadata/android/hu/short_description.txt similarity index 100% rename from vector/src/main/play/listings/hu/short_description.txt rename to fastlane/metadata/android/hu/short_description.txt diff --git a/vector/src/main/play/listings/hu/title.txt b/fastlane/metadata/android/hu/title.txt similarity index 100% rename from vector/src/main/play/listings/hu/title.txt rename to fastlane/metadata/android/hu/title.txt diff --git a/fastlane/metadata/android/it/full_description.txt b/fastlane/metadata/android/it/full_description.txt new file mode 100644 index 0000000000..b6f7cf449c --- /dev/null +++ b/fastlane/metadata/android/it/full_description.txt @@ -0,0 +1,30 @@ +Element è un nuovo tipo di app di messaggistica e collaborazione che: + +1. Ti mette al controllo per preservare la tua privacy +2. Ti lascia comunicare con chiunque nella rete Matrix e oltre, integrandosi con app come Slack +3. Ti protegge da pubblicità, raccolta di dati e piattaforme chiuse +4. Ti protegge con la crittografia end-to-end, con la firma incrociata per verificare gli altri + +Element è completamente diverso dalle altre app di messaggistica e collaborazione perchè è decentralizzato e open source. + +Element può essere gestito in locale - o puoi scegliere un host - in modo che tu abbia privacy, possesso e controllo dei tuoi dati e conversazioni. Ti dà accesso ad una rete aperta, quindi non sei limitato a parlare solo con altri utenti Element. Ed è molto sicuro. + +Element può fare tutto ciò perchè funziona su Matrix - lo standard per comunicazioni aperte e decentralizzate. + +Element ti mette al controllo lasciandoti scegliere chi gestisce il server delle tue conversazioni. Dall'app Element, hai diverse opzioni: + +1. Crea un account gratuito sul server pubblico matrix.org gestito dagli sviluppatori di Matrix, o scegli tra migliaia di server pubblici gestiti da volontari +2. Gestisci autonomamente un account installando un server sul tuo hardware +3. Registra un account su un server personalizzato iscrivendoti alla piattaforma Element Matrix Services + +Perchè scegliere Element? + +POSSIEDI I TUOI DATI: decidi dove tenere i tuoi dati e messaggi. Sono tuoi e li controlli tu, non qualche MEGADITTA che raccoglie i tuoi dati o ne dà l'accesso a terze parti. + +MESSAGGISTICA E COLLABORAZIONE APERTE: puoi chattare con chiunque nella rete Matrix, usando Element o un'altra app Matrix, o anche se si sta usando un sistema di messaggistica diverso come Slack, IRC o XMPP. + +SUPER SICURO: vera crittografia end-to-end (solo chi è nella conversazione può decifrare i messaggi) e firma incrociata per verificare i dispositivi dei partecipanti. + +COMUNICAZIONE COMPLETA: messaggi, chiamate audio e video, condivisione file e schermo, un vasto numero di integrazioni, bot e widget. Crea stanze, comunità, resta in contatto e porta a termine gli impegni. + +OVUNQUE TU SIA: resta in contatto ovunque tu sia con la cronologia dei messaggi sincronizzata tra tutti i tuoi dispositivi e in rete su https://app.element.io. diff --git a/fastlane/metadata/android/it/short_description.txt b/fastlane/metadata/android/it/short_description.txt new file mode 100644 index 0000000000..8c0c8fbee0 --- /dev/null +++ b/fastlane/metadata/android/it/short_description.txt @@ -0,0 +1 @@ +Chat e VoIP decentralizzati sicuri. Tieni lontani i tuoi dati dalle terze parti. diff --git a/fastlane/metadata/android/it/title.txt b/fastlane/metadata/android/it/title.txt new file mode 100644 index 0000000000..54e3b456c7 --- /dev/null +++ b/fastlane/metadata/android/it/title.txt @@ -0,0 +1 @@ +Element (ex Riot.im) diff --git a/vector/src/main/play/listings/kab/full_description.txt b/fastlane/metadata/android/kab/full_description.txt similarity index 100% rename from vector/src/main/play/listings/kab/full_description.txt rename to fastlane/metadata/android/kab/full_description.txt diff --git a/vector/src/main/play/listings/kab/short_description.txt b/fastlane/metadata/android/kab/short_description.txt similarity index 100% rename from vector/src/main/play/listings/kab/short_description.txt rename to fastlane/metadata/android/kab/short_description.txt diff --git a/vector/src/main/play/listings/kab/title.txt b/fastlane/metadata/android/kab/title.txt similarity index 100% rename from vector/src/main/play/listings/kab/title.txt rename to fastlane/metadata/android/kab/title.txt diff --git a/vector/src/main/play/listings/pt_BR/full_description.txt b/fastlane/metadata/android/pt_BR/full_description.txt similarity index 100% rename from vector/src/main/play/listings/pt_BR/full_description.txt rename to fastlane/metadata/android/pt_BR/full_description.txt diff --git a/vector/src/main/play/listings/pt_BR/short_description.txt b/fastlane/metadata/android/pt_BR/short_description.txt similarity index 100% rename from vector/src/main/play/listings/pt_BR/short_description.txt rename to fastlane/metadata/android/pt_BR/short_description.txt diff --git a/fastlane/metadata/android/pt_BR/title.txt b/fastlane/metadata/android/pt_BR/title.txt new file mode 100644 index 0000000000..5d2ae0c353 --- /dev/null +++ b/fastlane/metadata/android/pt_BR/title.txt @@ -0,0 +1 @@ +Element (o novo Riot.im) diff --git a/vector/src/main/play/listings/ru/full_description.txt b/fastlane/metadata/android/ru/full_description.txt similarity index 100% rename from vector/src/main/play/listings/ru/full_description.txt rename to fastlane/metadata/android/ru/full_description.txt diff --git a/vector/src/main/play/listings/ru/short_description.txt b/fastlane/metadata/android/ru/short_description.txt similarity index 100% rename from vector/src/main/play/listings/ru/short_description.txt rename to fastlane/metadata/android/ru/short_description.txt diff --git a/vector/src/main/play/listings/ru/title.txt b/fastlane/metadata/android/ru/title.txt similarity index 100% rename from vector/src/main/play/listings/ru/title.txt rename to fastlane/metadata/android/ru/title.txt diff --git a/vector/src/main/play/listings/sk/full_description.txt b/fastlane/metadata/android/sk/full_description.txt similarity index 100% rename from vector/src/main/play/listings/sk/full_description.txt rename to fastlane/metadata/android/sk/full_description.txt diff --git a/vector/src/main/play/listings/sk/short_description.txt b/fastlane/metadata/android/sk/short_description.txt similarity index 100% rename from vector/src/main/play/listings/sk/short_description.txt rename to fastlane/metadata/android/sk/short_description.txt diff --git a/vector/src/main/play/listings/sk/title.txt b/fastlane/metadata/android/sk/title.txt similarity index 100% rename from vector/src/main/play/listings/sk/title.txt rename to fastlane/metadata/android/sk/title.txt diff --git a/vector/src/main/play/listings/sv/full_description.txt b/fastlane/metadata/android/sv/full_description.txt similarity index 100% rename from vector/src/main/play/listings/sv/full_description.txt rename to fastlane/metadata/android/sv/full_description.txt diff --git a/vector/src/main/play/listings/sv/short_description.txt b/fastlane/metadata/android/sv/short_description.txt similarity index 100% rename from vector/src/main/play/listings/sv/short_description.txt rename to fastlane/metadata/android/sv/short_description.txt diff --git a/vector/src/main/play/listings/sv/title.txt b/fastlane/metadata/android/sv/title.txt similarity index 100% rename from vector/src/main/play/listings/sv/title.txt rename to fastlane/metadata/android/sv/title.txt diff --git a/vector/src/main/play/listings/th/short_description.txt b/fastlane/metadata/android/th/short_description.txt similarity index 100% rename from vector/src/main/play/listings/th/short_description.txt rename to fastlane/metadata/android/th/short_description.txt diff --git a/vector/src/main/play/listings/th/title.txt b/fastlane/metadata/android/th/title.txt similarity index 100% rename from vector/src/main/play/listings/th/title.txt rename to fastlane/metadata/android/th/title.txt diff --git a/vector/src/main/play/listings/tr/short_description.txt b/fastlane/metadata/android/tr/short_description.txt similarity index 100% rename from vector/src/main/play/listings/tr/short_description.txt rename to fastlane/metadata/android/tr/short_description.txt diff --git a/vector/src/main/play/listings/tr/title.txt b/fastlane/metadata/android/tr/title.txt similarity index 100% rename from vector/src/main/play/listings/tr/title.txt rename to fastlane/metadata/android/tr/title.txt diff --git a/vector/src/main/play/listings/uk/full_description.txt b/fastlane/metadata/android/uk/full_description.txt similarity index 95% rename from vector/src/main/play/listings/uk/full_description.txt rename to fastlane/metadata/android/uk/full_description.txt index ca6b4c2ae1..64247581d2 100644 --- a/vector/src/main/play/listings/uk/full_description.txt +++ b/fastlane/metadata/android/uk/full_description.txt @@ -13,7 +13,7 @@ Element здатен забезпечити усе це завдяки тому, Element надає вам повний контроль, дозволяючи обирати з-поміж надавачів послуг, що обслуговують сервери з вашими бесідами. Ви вільні обрати будь-який спосіб розміщення прямо з застосунку Element: -1. Отримати безкоштовний обліковий запис на загальнодоступному сервері matrix.org +1. Отримати безкоштовний обліковий запис на загальнодоступному сервері matrix.org, який обслуговують розробники Matrix, чи на одному з тисяч публічних серверів, які обслуговують волонтери 2. Розмістити свій обліковий запис на власному сервері 3. Зареєструватись на індивідуальному сервері, просто підписавшись на послуги платформи Element Matrix Services diff --git a/vector/src/main/play/listings/uk/short_description.txt b/fastlane/metadata/android/uk/short_description.txt similarity index 100% rename from vector/src/main/play/listings/uk/short_description.txt rename to fastlane/metadata/android/uk/short_description.txt diff --git a/vector/src/main/play/listings/uk/title.txt b/fastlane/metadata/android/uk/title.txt similarity index 100% rename from vector/src/main/play/listings/uk/title.txt rename to fastlane/metadata/android/uk/title.txt diff --git a/vector/src/main/play/listings/zh_Hans/full_description.txt b/fastlane/metadata/android/zh_Hans/full_description.txt similarity index 100% rename from vector/src/main/play/listings/zh_Hans/full_description.txt rename to fastlane/metadata/android/zh_Hans/full_description.txt diff --git a/vector/src/main/play/listings/zh_Hans/short_description.txt b/fastlane/metadata/android/zh_Hans/short_description.txt similarity index 100% rename from vector/src/main/play/listings/zh_Hans/short_description.txt rename to fastlane/metadata/android/zh_Hans/short_description.txt diff --git a/vector/src/main/play/listings/zh_Hans/title.txt b/fastlane/metadata/android/zh_Hans/title.txt similarity index 100% rename from vector/src/main/play/listings/zh_Hans/title.txt rename to fastlane/metadata/android/zh_Hans/title.txt diff --git a/gradle.properties b/gradle.properties index 99fd9d64fd..b3f11e08a3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,6 +14,8 @@ org.gradle.jvmargs=-Xmx2048m # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true +# Enable file system watch (https://docs.gradle.org/6.7/release-notes.html) +org.gradle.vfs.watch=true vector.debugPrivateData=false vector.httpLogLevel=NONE diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 87b738cbd0..e708b1c023 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 4da2435f42..99d667ccdc 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Thu Jul 02 12:33:07 CEST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionSha256Sum=0080de8491f0918e4f529a6db6820fa0b9e818ee2386117f4394f95feb1d5583 +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip diff --git a/gradlew b/gradlew index af6708ff22..4f906e0c81 100755 --- a/gradlew +++ b/gradlew @@ -1,5 +1,21 @@ #!/usr/bin/env sh +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + ############################################################################## ## ## Gradle start up script for UN*X @@ -28,7 +44,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m"' +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -66,6 +82,7 @@ esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -109,10 +126,11 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath @@ -138,19 +156,19 @@ if $cygwin ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -159,14 +177,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 0f8d5937c4..ac1b06f938 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -35,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -45,28 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell diff --git a/matrix-sdk-android-rx/build.gradle b/matrix-sdk-android-rx/build.gradle index 70a05114c2..3d62758065 100644 --- a/matrix-sdk-android-rx/build.gradle +++ b/matrix-sdk-android-rx/build.gradle @@ -35,7 +35,8 @@ android { dependencies { implementation project(":matrix-sdk-android") - implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation "androidx.fragment:fragment:1.3.0-beta01" implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0' implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' // Paging @@ -43,8 +44,4 @@ dependencies { // Logging implementation 'com.jakewharton.timber:timber:4.7.1' - - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test:runner:1.2.0' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' } diff --git a/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/LiveDataObservable.kt b/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/LiveDataObservable.kt index ec0d0cd288..2174c6f118 100644 --- a/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/LiveDataObservable.kt +++ b/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/LiveDataObservable.kt @@ -1,5 +1,4 @@ /* - * Copyright 2019 New Vector Ltd * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/OptionalRx.kt b/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/OptionalRx.kt index 551e277353..ff4b0d755c 100644 --- a/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/OptionalRx.kt +++ b/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/OptionalRx.kt @@ -1,5 +1,4 @@ /* - * Copyright 2019 New Vector Ltd * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxCallbackBuilders.kt b/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxCallbackBuilders.kt index 793d4694ce..f6dbe3d160 100644 --- a/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxCallbackBuilders.kt +++ b/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxCallbackBuilders.kt @@ -1,5 +1,4 @@ /* - * Copyright 2019 New Vector Ltd * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxRoom.kt b/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxRoom.kt index 0717a10d03..228e83faff 100644 --- a/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxRoom.kt +++ b/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxRoom.kt @@ -1,5 +1,4 @@ /* - * Copyright (c) 2020 New Vector Ltd * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -101,8 +100,11 @@ class RxRoom(private val room: Room) { return room.getEventReadReceiptsLive(eventId).asObservable() } - fun liveDrafts(): Observable> { - return room.getDraftsLive().asObservable() + fun liveDraft(): Observable> { + return room.getDraftLive().asObservable() + .startWithCallable { + room.getDraft().toOptional() + } } fun liveNotificationState(): Observable { diff --git a/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxSession.kt b/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxSession.kt index 55ede52c0c..03df708c0c 100644 --- a/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxSession.kt +++ b/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxSession.kt @@ -1,5 +1,4 @@ /* - * Copyright (c) 2020 New Vector Ltd * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/SecretsSynchronisationInfo.kt b/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/SecretsSynchronisationInfo.kt index e80bca1d7d..6da3217070 100644 --- a/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/SecretsSynchronisationInfo.kt +++ b/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/SecretsSynchronisationInfo.kt @@ -1,5 +1,4 @@ /* - * Copyright (c) 2020 New Vector Ltd * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index 2c20137647..e4a8b8884a 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -3,7 +3,6 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-android-extensions' apply plugin: 'realm-android' -apply plugin: 'okreplay' buildscript { repositories { @@ -36,6 +35,10 @@ android { // that the app's state is completely cleared between tests. testInstrumentationRunnerArguments clearPackageData: 'true' + // Seems that the build tools 4.1.0 does not generate BuildConfig.VERSION_NAME anymore. + // Add it manually here. We may remove this trick in the future + buildConfigField "String", "VERSION_NAME", "\"0.0.1\"" + buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\"" resValue "string", "git_sdk_revision", "\"${gitRevision()}\"" resValue "string", "git_sdk_revision_unix_date", "\"${gitRevisionUnixDate()}\"" @@ -109,21 +112,21 @@ static def gitRevisionDate() { dependencies { def arrow_version = "0.8.2" - def moshi_version = '1.8.0' + def moshi_version = '1.11.0' def lifecycle_version = '2.2.0' def arch_version = '2.1.0' - def coroutines_version = "1.3.8" def markwon_version = '3.1.0' - def daggerVersion = '2.25.4' + def daggerVersion = '2.29.1' def work_version = '2.4.0' def retrofit_version = '2.6.2' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" implementation "androidx.appcompat:appcompat:1.2.0" - implementation "androidx.core:core-ktx:1.3.1" + implementation "androidx.fragment:fragment:1.3.0-beta01" + implementation "androidx.core:core-ktx:1.3.2" implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" @@ -143,7 +146,7 @@ dependencies { implementation "ru.noties.markwon:core:$markwon_version" // Image - implementation 'androidx.exifinterface:exifinterface:1.3.0-alpha01' + implementation 'androidx.exifinterface:exifinterface:1.3.0' // Database implementation 'com.github.Zhuinden:realm-monarchy:0.5.1' @@ -181,33 +184,29 @@ dependencies { // Use the same WebRTC library than the one used by Jitsi library implementation('com.facebook.react:react-native-webrtc:1.84.0-jitsi-5112273@aar') - debugImplementation 'com.airbnb.okreplay:okreplay:1.5.0' - releaseImplementation 'com.airbnb.okreplay:noop:1.5.0' - androidTestImplementation 'com.airbnb.okreplay:espresso:1.5.0' - - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13' testImplementation 'org.robolectric:robolectric:4.3' //testImplementation 'org.robolectric:shadows-support-v4:3.0' // Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281 testImplementation 'io.mockk:mockk:1.9.2.kotlin12' - testImplementation 'org.amshove.kluent:kluent-android:1.44' - testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" + testImplementation 'org.amshove.kluent:kluent-android:1.61' + testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" // Plant Timber tree for test testImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1' kaptAndroidTest "com.google.dagger:dagger-compiler:$daggerVersion" - androidTestImplementation 'androidx.test:core:1.2.0' - androidTestImplementation 'androidx.test:runner:1.2.0' - androidTestImplementation 'androidx.test:rules:1.2.0' - androidTestImplementation 'androidx.test.ext:junit:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' - androidTestImplementation 'org.amshove.kluent:kluent-android:1.44' + androidTestImplementation 'androidx.test:core:1.3.0' + androidTestImplementation 'androidx.test:runner:1.3.0' + androidTestImplementation 'androidx.test:rules:1.3.0' + androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' + androidTestImplementation 'org.amshove.kluent:kluent-android:1.61' // Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281 androidTestImplementation 'io.mockk:mockk-android:1.9.2.kotlin12' androidTestImplementation "androidx.arch.core:core-testing:$arch_version" - androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" + androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" // Plant Timber tree for test androidTestImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1' - androidTestUtil 'androidx.test:orchestrator:1.2.0' + androidTestUtil 'androidx.test:orchestrator:1.3.0' } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/InstrumentedTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/InstrumentedTest.kt index cb6e624bce..b784884363 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/InstrumentedTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/InstrumentedTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/LiveDataTestObserver.java b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/LiveDataTestObserver.java index a09a655008..26920fbb35 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/LiveDataTestObserver.java +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/LiveDataTestObserver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/MainThreadExecutor.java b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/MainThreadExecutor.java index d26782d538..7ef2534037 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/MainThreadExecutor.java +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/MainThreadExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/SingleThreadCoroutineDispatcher.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/SingleThreadCoroutineDispatcher.kt index 4316b09b89..9942ea9db3 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/SingleThreadCoroutineDispatcher.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/SingleThreadCoroutineDispatcher.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/AccountCreationTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/AccountCreationTest.kt index cbb5af5911..5dede9dcfd 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/AccountCreationTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/AccountCreationTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/ChangePasswordTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/ChangePasswordTest.kt index e2140328e6..ec5477f976 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/ChangePasswordTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/ChangePasswordTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/DeactivateAccountTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/DeactivateAccountTest.kt index 36d09fb497..a6fbfd9b7a 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/DeactivateAccountTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/DeactivateAccountTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/Matrix.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/Matrix.kt index 751b2a708c..0d71af864b 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/Matrix.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/Matrix.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,8 @@ package org.matrix.android.sdk.api import android.content.Context +import android.os.Handler +import android.os.Looper import androidx.lifecycle.ProcessLifecycleOwner import androidx.work.Configuration import androidx.work.WorkManager @@ -48,13 +50,17 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo @Inject internal lateinit var olmManager: OlmManager @Inject internal lateinit var sessionManager: SessionManager + private val uiHandler = Handler(Looper.getMainLooper()) + init { Monarchy.init(context) DaggerTestMatrixComponent.factory().create(context, matrixConfiguration).inject(this) if (context.applicationContext !is Configuration.Provider) { WorkManager.initialize(context, Configuration.Builder().setExecutor(Executors.newCachedThreadPool()).build()) } - ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver) + uiHandler.post { + ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver) + } } fun getUserAgent() = userAgentHolder.userAgent diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt index fdbfa57b5c..1c912b365f 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt @@ -1,6 +1,5 @@ /* - * Copyright 2016 OpenMarket Ltd - * Copyright 2018 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -218,7 +217,7 @@ class CommonTestHelper(context: Context) { .createAccount(userName, password, null, it) } - // Preform dummy step + // Perform dummy step val registrationResult = doSync { matrix.authenticationService .getRegistrationWizard() diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestData.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestData.kt index 283ddd6fde..76e59d9a90 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestData.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestData.kt @@ -1,5 +1,5 @@ /* - * Copyright 2018 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt index 9765d35bc5..370b416f54 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt @@ -1,5 +1,5 @@ /* - * Copyright 2018 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/MockOkHttpInterceptor.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/MockOkHttpInterceptor.kt index a9bd9403d2..e7978a9cb2 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/MockOkHttpInterceptor.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/MockOkHttpInterceptor.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/SessionTestParams.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/SessionTestParams.kt index 287cafcdfd..428d44b666 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/SessionTestParams.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/SessionTestParams.kt @@ -1,5 +1,5 @@ /* - * Copyright 2018 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestAssertUtil.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestAssertUtil.kt index d972ad621c..2253360adc 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestAssertUtil.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestAssertUtil.kt @@ -1,5 +1,5 @@ /* - * Copyright 2018 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestConstants.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestConstants.kt index cbfc9bbbf6..8eb7e251e2 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestConstants.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestConstants.kt @@ -1,5 +1,5 @@ /* - * Copyright 2018 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrixCallback.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrixCallback.kt index 800c6ae7e0..c2e1ec0f92 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrixCallback.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrixCallback.kt @@ -1,5 +1,5 @@ /* - * Copyright 2018 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrixComponent.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrixComponent.kt index e2ab16cad3..33de345630 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrixComponent.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrixComponent.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestModule.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestModule.kt index c3b11d65cc..3e4d5fe08e 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestModule.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestModule.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestNetworkModule.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestNetworkModule.kt index 80467d91f4..4cd92ca272 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestNetworkModule.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestNetworkModule.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/AttachmentEncryptionTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/AttachmentEncryptionTest.kt index 1e109f11ae..b6cb7f9e79 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/AttachmentEncryptionTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/AttachmentEncryptionTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreHelper.kt index 261c0903f0..75ccce0db9 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreHelper.kt @@ -1,5 +1,5 @@ /* - * Copyright 2018 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ package org.matrix.android.sdk.internal.crypto -import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStore import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreModule @@ -34,14 +33,8 @@ internal class CryptoStoreHelper { .modules(RealmCryptoStoreModule()) .build(), crossSigningKeysMapper = CrossSigningKeysMapper(MoshiProvider.providesMoshi()), - credentials = createCredential()) + userId = "userId_" + Random.nextInt(), + deviceId = "deviceId_sample" + ) } - - fun createCredential() = Credentials( - userId = "userId_" + Random.nextInt(), - homeServer = "http://matrix.org", - accessToken = "access_token", - refreshToken = null, - deviceId = "deviceId_sample" - ) } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreTest.kt index 79477e3a4d..1d838b5c84 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2018 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/ExportEncryptionTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/ExportEncryptionTest.kt index 0ee79c2e1e..17664c78aa 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/ExportEncryptionTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/ExportEncryptionTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2018 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/UnwedgingTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/UnwedgingTest.kt index e9a1775ac3..0e3b29118c 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/UnwedgingTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/UnwedgingTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/ExtensionsKtTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/ExtensionsKtTest.kt index 9467e861db..9fa7458ea7 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/ExtensionsKtTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/ExtensionsKtTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt index 09f14032d0..38c57bd22a 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/XSigningTest.kt @@ -1,3 +1,19 @@ +/* + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.matrix.android.sdk.internal.crypto.crosssigning import androidx.test.ext.junit.runners.AndroidJUnit4 diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/encryption/EncryptionTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/encryption/EncryptionTest.kt new file mode 100644 index 0000000000..e42059c639 --- /dev/null +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/encryption/EncryptionTest.kt @@ -0,0 +1,111 @@ +/* + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.crypto.encryption + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.amshove.kluent.shouldBe +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.matrix.android.sdk.InstrumentedTest +import org.matrix.android.sdk.api.NoOpMatrixCallback +import org.matrix.android.sdk.api.session.events.model.EventType +import org.matrix.android.sdk.api.session.events.model.toContent +import org.matrix.android.sdk.api.session.room.Room +import org.matrix.android.sdk.api.session.room.send.SendState +import org.matrix.android.sdk.api.session.room.timeline.Timeline +import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent +import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings +import org.matrix.android.sdk.common.CommonTestHelper +import org.matrix.android.sdk.common.CryptoTestHelper +import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM +import org.matrix.android.sdk.internal.crypto.model.event.EncryptionEventContent +import java.util.concurrent.CountDownLatch + +@RunWith(AndroidJUnit4::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class EncryptionTest : InstrumentedTest { + private val mTestHelper = CommonTestHelper(context()) + private val mCryptoTestHelper = CryptoTestHelper(mTestHelper) + + @Test + fun test_EncryptionEvent() { + performTest(roomShouldBeEncrypted = false) { room -> + // Send an encryption Event as an Event (and not as a state event) + room.sendEvent( + eventType = EventType.STATE_ROOM_ENCRYPTION, + content = EncryptionEventContent(algorithm = MXCRYPTO_ALGORITHM_MEGOLM).toContent() + ) + } + } + + @Test + fun test_EncryptionStateEvent() { + performTest(roomShouldBeEncrypted = true) { room -> + // Send an encryption Event as a State Event + room.sendStateEvent( + eventType = EventType.STATE_ROOM_ENCRYPTION, + stateKey = null, + body = EncryptionEventContent(algorithm = MXCRYPTO_ALGORITHM_MEGOLM).toContent(), + callback = NoOpMatrixCallback() + ) + } + } + + private fun performTest(roomShouldBeEncrypted: Boolean, action: (Room) -> Unit) { + val cryptoTestData = mCryptoTestHelper.doE2ETestWithAliceInARoom(encryptedRoom = false) + + val aliceSession = cryptoTestData.firstSession + val room = aliceSession.getRoom(cryptoTestData.roomId)!! + + room.isEncrypted() shouldBe false + + val timeline = room.createTimeline(null, TimelineSettings(10)) + val latch = CountDownLatch(1) + val timelineListener = object : Timeline.Listener { + override fun onTimelineFailure(throwable: Throwable) { + } + + override fun onNewTimelineEvents(eventIds: List) { + // noop + } + + override fun onTimelineUpdated(snapshot: List) { + val newMessages = snapshot + .filter { it.root.sendState == SendState.SYNCED } + .filter { it.root.getClearType() == EventType.STATE_ROOM_ENCRYPTION } + + if (newMessages.isNotEmpty()) { + timeline.removeListener(this) + latch.countDown() + } + } + } + timeline.start() + timeline.addListener(timelineListener) + + action.invoke(room) + + mTestHelper.await(latch) + timeline.dispose() + + room.isEncrypted() shouldBe roomShouldBeEncrypted + + cryptoTestData.cleanUp(mTestHelper) + } +} diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/KeyShareTests.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/KeyShareTests.kt index 7c1a88dc75..197e36df06 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/KeyShareTests.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/KeyShareTests.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/WithHeldTests.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/WithHeldTests.kt index 03f9de2894..80cc14fcb6 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/WithHeldTests.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/WithHeldTests.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupPasswordTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupPasswordTest.kt index f38b55beba..cc71f88fc0 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupPasswordTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupPasswordTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupScenarioData.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupScenarioData.kt index 29a0b5ffd6..864f3c12e4 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupScenarioData.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupScenarioData.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt index aef97d5687..ca8993fb00 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2018 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTestConstants.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTestConstants.kt index f31e67b0e8..1248c8f07d 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTestConstants.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTestConstants.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTestHelper.kt index f84a90708c..944d1036d3 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTestHelper.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/PrepareKeysBackupDataResult.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/PrepareKeysBackupDataResult.kt index c28b7990e0..6aefe98f86 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/PrepareKeysBackupDataResult.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/PrepareKeysBackupDataResult.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/StateObserver.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/StateObserver.kt index 90d2fd7812..ff8ce43b55 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/StateObserver.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/StateObserver.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/ssss/QuadSTests.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/ssss/QuadSTests.kt index 42cee74334..0489ee179f 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/ssss/QuadSTests.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/ssss/QuadSTests.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt index a6beeb123c..75c7ad4d53 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/HexParser.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/HexParser.kt index cd5aa32d59..29d4e7850a 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/HexParser.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/HexParser.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/QrCodeTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/QrCodeTest.kt index 54a0f7e771..ee604fc9ab 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/QrCodeTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/QrCodeTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,15 +17,14 @@ package org.matrix.android.sdk.internal.crypto.verification.qrcode import androidx.test.ext.junit.runners.AndroidJUnit4 -import org.matrix.android.sdk.InstrumentedTest +import org.amshove.kluent.shouldBeEqualTo import org.amshove.kluent.shouldBeNull -import org.amshove.kluent.shouldEqual -import org.amshove.kluent.shouldEqualTo import org.amshove.kluent.shouldNotBeNull import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters +import org.matrix.android.sdk.InstrumentedTest @RunWith(AndroidJUnit4::class) @FixMethodOrder(MethodSorters.JVM) @@ -66,32 +65,32 @@ class QrCodeTest : InstrumentedTest { @Test fun testEncoding1() { - qrCode1.toEncodedString() shouldEqual value1 + qrCode1.toEncodedString() shouldBeEqualTo value1 } @Test fun testEncoding2() { - qrCode2.toEncodedString() shouldEqual value2 + qrCode2.toEncodedString() shouldBeEqualTo value2 } @Test fun testEncoding3() { - qrCode3.toEncodedString() shouldEqual value3 + qrCode3.toEncodedString() shouldBeEqualTo value3 } @Test fun testSymmetry1() { - qrCode1.toEncodedString().toQrCodeData() shouldEqual qrCode1 + qrCode1.toEncodedString().toQrCodeData() shouldBeEqualTo qrCode1 } @Test fun testSymmetry2() { - qrCode2.toEncodedString().toQrCodeData() shouldEqual qrCode2 + qrCode2.toEncodedString().toQrCodeData() shouldBeEqualTo qrCode2 } @Test fun testSymmetry3() { - qrCode3.toEncodedString().toQrCodeData() shouldEqual qrCode3 + qrCode3.toEncodedString().toQrCodeData() shouldBeEqualTo qrCode3 } @Test @@ -102,7 +101,7 @@ class QrCodeTest : InstrumentedTest { checkHeader(byteArray) // Mode - byteArray[7] shouldEqualTo 0 + byteArray[7] shouldBeEqualTo 0 checkSizeAndTransaction(byteArray) @@ -120,7 +119,7 @@ class QrCodeTest : InstrumentedTest { checkHeader(byteArray) // Mode - byteArray[7] shouldEqualTo 1 + byteArray[7] shouldBeEqualTo 1 checkSizeAndTransaction(byteArray) compareArray(byteArray.copyOfRange(23, 23 + 32), kte_byteArray) @@ -137,7 +136,7 @@ class QrCodeTest : InstrumentedTest { checkHeader(byteArray) // Mode - byteArray[7] shouldEqualTo 2 + byteArray[7] shouldBeEqualTo 2 checkSizeAndTransaction(byteArray) compareArray(byteArray.copyOfRange(23, 23 + 32), tlx_byteArray) @@ -156,10 +155,10 @@ class QrCodeTest : InstrumentedTest { val result = qrCode.toEncodedString() val expected = value1.replace("\u0000\u000DMaTransaction", "\u0007\u00D0$longTransactionId") - result shouldEqual expected + result shouldBeEqualTo expected // Reverse operation - expected.toQrCodeData() shouldEqual qrCode + expected.toQrCodeData() shouldBeEqualTo qrCode } @Test @@ -170,7 +169,7 @@ class QrCodeTest : InstrumentedTest { val qrCode = qrCode1.copy(transactionId = longTransactionId) // Symmetric operation - qrCode.toEncodedString().toQrCodeData() shouldEqual qrCode + qrCode.toEncodedString().toQrCodeData() shouldBeEqualTo qrCode } } @@ -218,32 +217,32 @@ class QrCodeTest : InstrumentedTest { } private fun compareArray(actual: ByteArray, expected: ByteArray) { - actual.size shouldEqual expected.size + actual.size shouldBeEqualTo expected.size for (i in actual.indices) { - actual[i] shouldEqualTo expected[i] + actual[i] shouldBeEqualTo expected[i] } } private fun checkHeader(byteArray: ByteArray) { // MATRIX - byteArray[0] shouldEqualTo 'M'.toByte() - byteArray[1] shouldEqualTo 'A'.toByte() - byteArray[2] shouldEqualTo 'T'.toByte() - byteArray[3] shouldEqualTo 'R'.toByte() - byteArray[4] shouldEqualTo 'I'.toByte() - byteArray[5] shouldEqualTo 'X'.toByte() + byteArray[0] shouldBeEqualTo 'M'.toByte() + byteArray[1] shouldBeEqualTo 'A'.toByte() + byteArray[2] shouldBeEqualTo 'T'.toByte() + byteArray[3] shouldBeEqualTo 'R'.toByte() + byteArray[4] shouldBeEqualTo 'I'.toByte() + byteArray[5] shouldBeEqualTo 'X'.toByte() // Version - byteArray[6] shouldEqualTo 2 + byteArray[6] shouldBeEqualTo 2 } private fun checkSizeAndTransaction(byteArray: ByteArray) { // Size - byteArray[8] shouldEqualTo 0 - byteArray[9] shouldEqualTo 13 + byteArray[8] shouldBeEqualTo 0 + byteArray[9] shouldBeEqualTo 13 // Transaction - byteArray.copyOfRange(10, 10 + "MaTransaction".length).toString(Charsets.ISO_8859_1) shouldEqual "MaTransaction" + byteArray.copyOfRange(10, 10 + "MaTransaction".length).toString(Charsets.ISO_8859_1) shouldBeEqualTo "MaTransaction" } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/SharedSecretTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/SharedSecretTest.kt index 4032890723..97b93dcf5a 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/SharedSecretTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/SharedSecretTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/VerificationTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/VerificationTest.kt index 0c003215ee..1385dac1ec 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/VerificationTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/VerificationTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParserTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParserTest.kt index eebaa93415..1713578932 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParserTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParserTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/util/JsonCanonicalizerTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/util/JsonCanonicalizerTest.kt index 854d420a82..b5ab6589ff 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/util/JsonCanonicalizerTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/util/JsonCanonicalizerTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/ChunkEntityTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/ChunkEntityTest.kt index a2a1586864..69ae57e644 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/ChunkEntityTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/ChunkEntityTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,14 @@ package org.matrix.android.sdk.session.room.timeline import androidx.test.ext.junit.runners.AndroidJUnit4 import com.zhuinden.monarchy.Monarchy +import io.realm.Realm +import io.realm.RealmConfiguration +import io.realm.kotlin.createObject +import org.amshove.kluent.shouldBeEqualTo +import org.amshove.kluent.shouldBeTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith import org.matrix.android.sdk.InstrumentedTest import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.room.send.SendState @@ -29,14 +37,6 @@ import org.matrix.android.sdk.internal.database.model.SessionRealmModule import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection import org.matrix.android.sdk.session.room.timeline.RoomDataHelper.createFakeListOfEvents import org.matrix.android.sdk.session.room.timeline.RoomDataHelper.createFakeMessageEvent -import io.realm.Realm -import io.realm.RealmConfiguration -import io.realm.kotlin.createObject -import org.amshove.kluent.shouldBeTrue -import org.amshove.kluent.shouldEqual -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) internal class ChunkEntityTest : InstrumentedTest { @@ -60,10 +60,10 @@ internal class ChunkEntityTest : InstrumentedTest { val chunk: ChunkEntity = realm.createObject() val fakeEvent = createFakeMessageEvent().toEntity(ROOM_ID, SendState.SYNCED, System.currentTimeMillis()).let { - realm.copyToRealmOrUpdate(it) + realm.copyToRealm(it) } chunk.addTimelineEvent(ROOM_ID, fakeEvent, PaginationDirection.FORWARDS, emptyMap()) - chunk.timelineEvents.size shouldEqual 1 + chunk.timelineEvents.size shouldBeEqualTo 1 } } @@ -72,11 +72,11 @@ internal class ChunkEntityTest : InstrumentedTest { monarchy.runTransactionSync { realm -> val chunk: ChunkEntity = realm.createObject() val fakeEvent = createFakeMessageEvent().toEntity(ROOM_ID, SendState.SYNCED, System.currentTimeMillis()).let { - realm.copyToRealmOrUpdate(it) + realm.copyToRealm(it) } chunk.addTimelineEvent(ROOM_ID, fakeEvent, PaginationDirection.FORWARDS, emptyMap()) chunk.addTimelineEvent(ROOM_ID, fakeEvent, PaginationDirection.FORWARDS, emptyMap()) - chunk.timelineEvents.size shouldEqual 1 + chunk.timelineEvents.size shouldBeEqualTo 1 } } @@ -88,7 +88,7 @@ internal class ChunkEntityTest : InstrumentedTest { chunk1.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS) chunk2.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS) chunk1.merge(ROOM_ID, chunk2, PaginationDirection.BACKWARDS) - chunk1.timelineEvents.size shouldEqual 60 + chunk1.timelineEvents.size shouldBeEqualTo 60 } } @@ -104,7 +104,7 @@ internal class ChunkEntityTest : InstrumentedTest { chunk1.addAll(ROOM_ID, eventsForChunk1, PaginationDirection.FORWARDS) chunk2.addAll(ROOM_ID, eventsForChunk2, PaginationDirection.BACKWARDS) chunk1.merge(ROOM_ID, chunk2, PaginationDirection.BACKWARDS) - chunk1.timelineEvents.size shouldEqual 40 + chunk1.timelineEvents.size shouldBeEqualTo 40 chunk1.isLastForward.shouldBeTrue() } } @@ -119,7 +119,7 @@ internal class ChunkEntityTest : InstrumentedTest { chunk1.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS) chunk2.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS) chunk1.merge(ROOM_ID, chunk2, PaginationDirection.FORWARDS) - chunk1.prevToken shouldEqual prevToken + chunk1.prevToken shouldBeEqualTo prevToken } } @@ -133,7 +133,7 @@ internal class ChunkEntityTest : InstrumentedTest { chunk1.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS) chunk2.addAll(ROOM_ID, createFakeListOfEvents(30), PaginationDirection.BACKWARDS) chunk1.merge(ROOM_ID, chunk2, PaginationDirection.BACKWARDS) - chunk1.nextToken shouldEqual nextToken + chunk1.nextToken shouldBeEqualTo nextToken } } @@ -142,7 +142,7 @@ internal class ChunkEntityTest : InstrumentedTest { direction: PaginationDirection) { events.forEach { event -> val fakeEvent = event.toEntity(roomId, SendState.SYNCED, System.currentTimeMillis()).let { - realm.copyToRealmOrUpdate(it) + realm.copyToRealm(it) } addTimelineEvent(roomId, fakeEvent, direction, emptyMap()) } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/FakeGetContextOfEventTask.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/FakeGetContextOfEventTask.kt index 9a133032b6..b86c86c0c7 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/FakeGetContextOfEventTask.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/FakeGetContextOfEventTask.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/FakePaginationTask.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/FakePaginationTask.kt index 06828ef3d1..d09bfb18c6 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/FakePaginationTask.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/FakePaginationTask.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/FakeTokenChunkEvent.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/FakeTokenChunkEvent.kt index 0301157d09..657f622c5b 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/FakeTokenChunkEvent.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/FakeTokenChunkEvent.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/RoomDataHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/RoomDataHelper.kt index a6fe675218..1adf31be5f 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/RoomDataHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/RoomDataHelper.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.room.model.Membership -import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary +import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent import org.matrix.android.sdk.api.session.room.model.message.MessageType import kotlin.random.Random @@ -41,11 +41,11 @@ object RoomDataHelper { } } - fun createFakeEvent(type: String, - content: Content? = null, - prevContent: Content? = null, - sender: String = FAKE_TEST_SENDER, - stateKey: String = FAKE_TEST_SENDER + private fun createFakeEvent(type: String, + content: Content? = null, + prevContent: Content? = null, + sender: String = FAKE_TEST_SENDER, + stateKey: String = FAKE_TEST_SENDER ): Event { return Event( type = type, @@ -62,8 +62,8 @@ object RoomDataHelper { return createFakeEvent(EventType.MESSAGE, message) } - fun createFakeRoomMemberEvent(): Event { - val roomMember = RoomMemberSummary(Membership.JOIN, "Fake name #${Random.nextLong()}").toContent() + private fun createFakeRoomMemberEvent(): Event { + val roomMember = RoomMemberContent(Membership.JOIN, "Fake name #${Random.nextLong()}").toContent() return createFakeEvent(EventType.STATE_ROOM_MEMBER, roomMember) } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineBackToPreviousLastForwardTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineBackToPreviousLastForwardTest.kt index 8c5e7f17f2..3774e6f513 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineBackToPreviousLastForwardTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineBackToPreviousLastForwardTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineForwardPaginationTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineForwardPaginationTest.kt index facb905b35..34edf37733 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineForwardPaginationTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineForwardPaginationTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelinePreviousLastForwardTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelinePreviousLastForwardTest.kt index 28ce75c221..9ebac8766a 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelinePreviousLastForwardTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelinePreviousLastForwardTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineTest.kt index b0da49cdbb..9be0a5d5af 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -78,7 +78,7 @@ internal class TimelineTest : InstrumentedTest { // } // } // latch.await() -// timelineEvents.size shouldEqual initialLoad + paginationCount +// timelineEvents.size shouldBeEqualTo initialLoad + paginationCount // timeline.dispose() // } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt new file mode 100644 index 0000000000..7db159cd0b --- /dev/null +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/search/SearchMessagesTest.kt @@ -0,0 +1,177 @@ +/* + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.session.search + +import org.junit.Assert.assertTrue +import org.junit.Assert.fail +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.junit.runners.MethodSorters +import org.matrix.android.sdk.InstrumentedTest +import org.matrix.android.sdk.api.MatrixCallback +import org.matrix.android.sdk.api.extensions.orFalse +import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.room.model.message.MessageContent +import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings +import org.matrix.android.sdk.api.session.search.SearchResult +import org.matrix.android.sdk.common.CommonTestHelper +import org.matrix.android.sdk.common.CryptoTestHelper +import org.matrix.android.sdk.common.TestConstants +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit + +@RunWith(JUnit4::class) +@FixMethodOrder(MethodSorters.JVM) +class SearchMessagesTest : InstrumentedTest { + + private val MESSAGE = "Lorem ipsum dolor sit amet" + + private val commonTestHelper = CommonTestHelper(context()) + private val cryptoTestHelper = CryptoTestHelper(commonTestHelper) + + @Test + fun sendTextMessageAndSearchPartOfItUsingSession() { + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(false) + val aliceSession = cryptoTestData.firstSession + val aliceRoomId = cryptoTestData.roomId + aliceSession.cryptoService().setWarnOnUnknownDevices(false) + val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!! + val aliceTimeline = roomFromAlicePOV.createTimeline(null, TimelineSettings(10)) + aliceTimeline.start() + + commonTestHelper.sendTextMessage( + roomFromAlicePOV, + MESSAGE, + 2) + + run { + var lock = CountDownLatch(1) + + val eventListener = commonTestHelper.createEventListener(lock) { snapshot -> + snapshot.count { it.root.content.toModel()?.body?.startsWith(MESSAGE).orFalse() } == 2 + } + + aliceTimeline.addListener(eventListener) + commonTestHelper.await(lock) + + lock = CountDownLatch(1) + aliceSession + .searchService() + .search( + searchTerm = "lore", + limit = 10, + includeProfile = true, + afterLimit = 0, + beforeLimit = 10, + orderByRecent = true, + nextBatch = null, + roomId = aliceRoomId, + callback = object : MatrixCallback { + override fun onSuccess(data: SearchResult) { + super.onSuccess(data) + assertTrue(data.results?.size == 2) + assertTrue( + data.results + ?.all { + (it.event.content?.get("body") as? String)?.startsWith(MESSAGE).orFalse() + }.orFalse() + ) + lock.countDown() + } + + override fun onFailure(failure: Throwable) { + super.onFailure(failure) + fail(failure.localizedMessage) + lock.countDown() + } + } + ) + lock.await(TestConstants.timeOutMillis, TimeUnit.MILLISECONDS) + + aliceTimeline.removeAllListeners() + cryptoTestData.cleanUp(commonTestHelper) + } + + aliceSession.startSync(true) + } + + @Test + fun sendTextMessageAndSearchPartOfItUsingRoom() { + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(false) + val aliceSession = cryptoTestData.firstSession + val aliceRoomId = cryptoTestData.roomId + aliceSession.cryptoService().setWarnOnUnknownDevices(false) + val roomFromAlicePOV = aliceSession.getRoom(aliceRoomId)!! + val aliceTimeline = roomFromAlicePOV.createTimeline(null, TimelineSettings(10)) + aliceTimeline.start() + + commonTestHelper.sendTextMessage( + roomFromAlicePOV, + MESSAGE, + 2) + + run { + var lock = CountDownLatch(1) + + val eventListener = commonTestHelper.createEventListener(lock) { snapshot -> + snapshot.count { it.root.content.toModel()?.body?.startsWith(MESSAGE).orFalse() } == 2 + } + + aliceTimeline.addListener(eventListener) + commonTestHelper.await(lock) + + lock = CountDownLatch(1) + roomFromAlicePOV + .search( + searchTerm = "lore", + limit = 10, + includeProfile = true, + afterLimit = 0, + beforeLimit = 10, + orderByRecent = true, + nextBatch = null, + callback = object : MatrixCallback { + override fun onSuccess(data: SearchResult) { + super.onSuccess(data) + assertTrue(data.results?.size == 2) + assertTrue( + data.results + ?.all { + (it.event.content?.get("body") as? String)?.startsWith(MESSAGE).orFalse() + }.orFalse() + ) + lock.countDown() + } + + override fun onFailure(failure: Throwable) { + super.onFailure(failure) + fail(failure.localizedMessage) + lock.countDown() + } + } + ) + lock.await(TestConstants.timeOutMillis, TimeUnit.MILLISECONDS) + + aliceTimeline.removeAllListeners() + cryptoTestData.cleanUp(commonTestHelper) + } + + aliceSession.startSync(true) + } +} diff --git a/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/database/RealmDebugTools.kt b/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/database/RealmDebugTools.kt index 324a3c1062..e5f4af2377 100644 --- a/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/database/RealmDebugTools.kt +++ b/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/database/RealmDebugTools.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/interceptors/CurlLoggingInterceptor.kt b/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/interceptors/CurlLoggingInterceptor.kt index ee2c6076cc..2103dc954d 100644 --- a/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/interceptors/CurlLoggingInterceptor.kt +++ b/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/interceptors/CurlLoggingInterceptor.kt @@ -1,6 +1,6 @@ /* * Copyright (C) 2016 Jeff Gilfelt. - * Copyright 2019 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/interceptors/FormattedJsonHttpLogger.kt b/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/interceptors/FormattedJsonHttpLogger.kt index 349110aff8..5c03e8a855 100644 --- a/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/interceptors/FormattedJsonHttpLogger.kt +++ b/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/interceptors/FormattedJsonHttpLogger.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/main/AndroidManifest.xml b/matrix-sdk-android/src/main/AndroidManifest.xml index 52238f824c..220a168f60 100644 --- a/matrix-sdk-android/src/main/AndroidManifest.xml +++ b/matrix-sdk-android/src/main/AndroidManifest.xml @@ -1,12 +1,24 @@ + + + + + Entfernen nicht möglich Nachricht kann nicht gesendet werden - Bild konnte nicht hochgeladen werden - Netzwerk-Fehler Matrix-Fehler - - - - Es ist aktuell nicht möglich, einen leeren Raum erneut zu betreten. - Verschlüsselte Nachricht - E-Mail-Adresse Telefonnummer - %1$s sandte einen Sticker. - Einladung von %s Raumeinladung %1$s und %2$s Leerer Raum - %1$s und 1 andere(r) %1$s und %2$d andere - - Nachricht entfernt Nachricht entfernt von %1$s Nachricht entfernt [Grund: %1$s] Nachricht entfernt von %1$s [Grund: %2$s] %s hat diesen Raum aufgewertet. - Sende eine Nachricht… Sendewarteschlange leeren - Erste Synchronisation: Importiere Benutzerkonto… Erste Synchronisation: Importiere Cryptoschlüssel Erste Synchronisation: Importiere Räume @@ -102,7 +81,6 @@ Erste Synchronisation: Importiere verlassene Räume Erste Synchronisation: Importiere Gemeinschaften Erste Synchronisation: Importiere Benutzerdaten - %1$s hat die Einladung an %2$s, den Raum zu betreten, zurückgezogen %1$s\'s Einladung. Grund: %2$s %1$s hat %2$s eingeladen. Grund: %3$s @@ -117,33 +95,24 @@ %1$s hat Einladung an %2$s zu Betreten dieses Raumes zurückgezogen. Grund: %3$s %1$s hat die Einladung für %2$s angenommen. Grund: %3$s %1$s hat Einladung für %2$s verworfen. Grund: %3$s - %1$s fügt %2$s als eine Adresse für diesen Raum hinzu. %1$s fügt %2$s als Adressen für diesen Raum hinzu. - %1$s entfernt %2$s als eine Adresse für diesen Raum. %1$s entfernt %2$s als Adressen für diesen Raum. - %1$s fügt %2$s als Adresse für diesen Raum hinzu und entfernt %3$s. - %1$s legt die Hauptadresse fest für diesen Raum als %2$s fest. %1$s entfernt die Hauptadresse für diesen Raum. - %1$s hat Gästen erlaubt den Raum zu betreten. %1$s hat Gäste unterbunden den Raum zu betreten. - %1$s aktivierte Ende-zu-Ende-Verschlüsselung. %1$s aktivierte Ende-zu-Ende-Verschlüsselung (unbekannter Algorithmus %2$s). - %s fordert zur Überprüfung deines Schlüssels auf, jedoch unterstützt dein Client nicht die Schlüsselüberprüfung im Chat. Du musst die herkömmliche Schlüsselüberprüfung verwenden, um die Schlüssel zu überprüfen. - Du hast ein Bild gesendet. Du hast einen Sticker gesendet. - Deine Einladung %1$s hat den Raum erstellt Du hast den Raum erstellt @@ -170,7 +139,6 @@ Du hast den zukünftigen Nachrichtenverlauf für %1$s sichtbar gemacht Du hast Ende-zu-Ende-Verschlüsselung aktiviert (%1$s) Du hast den Raum aufgwertet. - Du hast eine VoIP-Konferenz angefordert Du hast den Raumnamen entfernt Du hast das Raumthema entfernt @@ -180,24 +148,20 @@ Du hast %1$s in den Raum eingeladen Du hast die Einladung für %1$s zurückgenommen Du hast die Einladung für %1$s akzeptiert - %1$s hat das %2$s Widget hinzugefügt Du hast das %1$s Widget hinzugefügt %1$s hat das %2$s Widget entfernt Du hast das %1$s Widget entfernt %1$s hat das %2$s Widget modifiziert Du hast das %1$s Widget modifiziert - Administrator Moderator Standard Benutzerdefiniert (%1$d) Benutzerdefiniert - Du hast die Berechtigungsstufe von %1$s geändert. %1$s hat die Berechtigungsstufe von %2$s geändert. %1$s von %2$s zu %3$s - Deine Einladung. Grund: %1$s Du hast %1$s eingeladen. Grund: %2$s Du bist dem Raum beigetreten. Grund: %1$s @@ -210,28 +174,37 @@ Du hast die Einladung für %1$s zurückgenommen. Grund: %2$s Du hast die Einladung von %1$s angenommen. Grund: %2$s Du hast die Einladung von %1$s abgelehnt. Grund: %2$s - Du hast die Raumaddresse %1$s hinzugefügt. Du hast die Raumaddressen %1$s hinzugefügt. - - Du hast die Raumaddresse %1$s vom Raum entfernt. - Du hast die Raumaddressen %1$s vom Raum entfernt. + Du hast die Raum-Adresse %1$s vom Raum entfernt. + Du hast die Raum-Adressen %1$s vom Raum entfernt. - Du hast den Raumaddressen %1$s hinzugefügt und %2$s entfernt. - Du hast die Hauptaddresse für diesen Raum auf %1$s gesetzt. Du hast die Hauptaddresse des Raums entfernt. - Du hast Gästen erlaubt dem Raum beizutreten. Du hast Gästen untersagt dem Raum beizutreten. - Du hast Ende-zu-Ende-Verschlüsselung aktiviert. Du hast Ende-zu-Ende-Verschlüsselung aktiviert (unbekannter Algorithmus %1$s). - %s hat Daten gesendet, um einen Anruf zu starten. Du hast Daten geschickt, um eine Anruf zu starten. - + Du hast Gästen erlaubt hier beizutreten. + %1$s hat Gästen erlaubt hier beizutreten. + Du bist beigetreten. Grund: %1$s + %1$s ist beigetreten. Grund: %2$s + Du hast die Einladung für %1$s zurückgezogen + %1$s hat die Einladung für %2$s zurückgezogen + Du hast %1$s eingeladen + %1$s hat %2$s eingeladen + Du hast zukünftige Nachrichten für %2$s sichtbar gemacht + %1$s hat zukünftige Nachrichten für %2$s sichtbar gemacht + Du hast den Raum verlassen + %1$s hat den Raum verlassen + Du bist beigetreten + %1$s ist beigetreten + Du hast eine Diskussion erstellt + %1$s hat eine Diskussion erstellt + \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-et/strings.xml b/matrix-sdk-android/src/main/res/values-et/strings.xml index 2fbe263464..71ee50f075 100644 --- a/matrix-sdk-android/src/main/res/values-et/strings.xml +++ b/matrix-sdk-android/src/main/res/values-et/strings.xml @@ -1,9 +1,8 @@ - + %1$s: %2$s %1$s saatis pildi. %1$s saatis kleepsu. - Kasutaja %s kutse %1$s kutsus kasutajat %2$s %1$s kutsus sind @@ -30,11 +29,9 @@ teadmata (%s). %1$s lülitas sisse läbiva krüptimise (%2$s) %s uuendas seda jututuba. - %1$s saatis VoIP konverentsi kutse VoIP-konverents algas VoIP-konverents lõppes - (samuti sai avatar muudetud) %1$s eemaldas jututoa nime %1$s eemaldas jututoa teema @@ -46,37 +43,25 @@ %1$s saatis jututoaga liitumiseks kutse kasutajale %2$s %1$s võttis tagasi jututoaga liitumise kutse kasutajalt %2$s %1$s võttis vastu kutse %2$s nimel - ** Ei õnnestu dekrüptida: %s ** Sõnumi saatja seade ei ole selle sõnumi jaoks saatnud dekrüptimisvõtmeid. - Ei saanud muuta sõnumit Sõnumi saatmine ei õnnestunud - Faili üles laadimine ei õnnestunud - Võrguühenduse viga Matrix\'i viga - Hetkel ei ole võimalik uuesti liituda tühja jututoaga. - Krüptitud sõnum - E-posti aadress Telefoninumber - Kutse kasutajalt %s Kutse jututuppa - %1$s ja %2$s - %1$s ja üks muu %1$s ja %2$d muud - Tühi jututuba - Alglaadimine: \nImpordin kontot… Alglaadimine: @@ -93,10 +78,8 @@ \nImpordin kogukondi Alglaadimine: \nImpordin kontoandmeid - Saadan sõnumit… Tühjenda saatmisjärjekord - Kasutaja %1$s kutse. Põhjus: %2$s %1$s kutsus kasutajat %2$s. Põhjus: %3$s %1$s kutsus sind. Põhjus: %2$s @@ -108,34 +91,25 @@ %1$s tühistas kasutajale %2$s saadetud kutse jututoaga liitumiseks. Põhjus: %3$s %1$s võttis vastu kutse %2$s jututoaga liitumiseks. Põhjus: %3$s %1$s võttis tagasi kasutajale %2$s saadetud kutse. Põhjus: %3$s - %1$s lülitas sisse läbiva krüptimise. %1$s lülitas sisse läbiva krüptimise (tundmatu algoritm %2$s). - %1$s lisas %2$s selle jututoa aadressiks. %1$s lisas %2$s selle jututoa aadressideks. - %1$s eemaldas %2$s kui selle jututoa aadressi. %1$s eemaldas %2$s selle jututoa aadresside hulgast. - %1$s lisas %2$s ja eemaldas %3$s selle jututoa aadresside loendist. - %1$s seadistas selle jututoa põhiaadressiks %2$s. %1$s eemaldas selle jututoa põhiaadressi. - %1$s lubas külalistel selle jututoaga liituda. %1$s seadistas, et külalised ei või selle jututoaga liituda. - %s soovib verifitseerida sinu võtmeid, kuid sinu kasutatav klient ei oska vestluse-sisest verifitseerimist teha. Sa pead kasutama traditsioonilist verifitseerimislahendust. - Kasutaja %1$s lõi jututoa Sina saatsid pildi. Sina saatsid kleepsu. - Sinu kutse Sa lõid jututoa Sina kutsusid kasutajat %1$s @@ -145,7 +119,7 @@ Sina müksasid %1$s välja %1$s taastas %2$s ligipääsu Sina taastasid %1$s ligipääsu - %1$s keelas %1$s ligipääsu + %1$s keelas %2$s ligipääsu Sina keelasid %1$s ligipääsu Sina võtsid tagasi %1$s kutse Sina muutsid oma tunnuspilti @@ -165,7 +139,6 @@ Sa seadistasid, et tulevane jututoa ajalugu on nähtav kasutajale %1$s Sa lülitasid sisse läbiva krüptimise (%1$s) Sa uuendasid seda jututuba. - Sa algatasid VoIP rühmakõne Sa eemaldasid jututoa nime Sa eemaldasid jututoa teema @@ -175,26 +148,22 @@ Sina saatsid kasutajale %1$s kutse jututoaga liitumiseks Sina võtsid tagasi jututoaga liitumise kutse kasutajalt %1$s Sina võtsid vastu kutse %1$s nimel - %1$s lisas %2$s vidina Sina lisasid %1$s vidina %1$s eemaldas %2$s vidina Sina eemdaldasid %1$s vidina %1$s muutis %2$s vidinat Sa muutsid %1$s vidinat - Peakasutaja Moderaator Tavakasutaja - Kohandatud kasutajaõigused (%1$s) + Kohandatud kasutajaõigused (%1$d) Kohandatud õigused - Sina muutsid kasutaja %1$s õigusi. %1$s muutis kasutaja %2$s õigusi. %1$s õiguste muutus %2$s -> %3$s - Sinu kutse. Põhjus %1$s - Sina kutsusid kasutajat %1$s. Põhjus: %1$s + Sina kutsusid kasutajat %1$s. Põhjus: %2$s Sina liitusid jututoaga. Põhjus: %1$s Sina lahkusid jututoast. Põhjus: %1$s Sina lükkasid kutse tagasi. Põhjus: %1$s @@ -207,26 +176,41 @@ Sina võtsid tagasi jututoaga liitumise kutse kasutajalt %1$s. Põhjus: %2$s Sina võtsid vastu kutse %1$s nimel. Põhjus: %2$s Sina võtsid tagasi kasutaja %1$s kutse. Põhjus: %2$s - Sina lisasid %1$s selle jututoa aadressiks. Sina lisasid %1$s selle jututoa aadressideks. - Sina eemaldasid %1$s, kui selle jututoa aadressi. Sina eemaldasid %1$s selle jututoa aadresside hulgast. - Sina lisasid %1$s selle jututoa aadressiks ning eemaldasid %2$s aadresside hulgast. - Sina seadistasid selle jututoa põhiaadressiks %1$s. Sina eemaldasid selle jututoa põhiaadressi. - Sina lubasid külalistel selle jututoaga liituda. Sina seadistasid, et külalised ei või selle jututoaga liituda. - Sa lülitasid sisse läbiva krüptimise. Sa lülitasid sisse läbiva krüptimise (kasutusel on tundmatu algoritm %1$s). - - + Sina seadistasid, et külalised ei või selle jututoaga liituda. + %1$s seadistas, et külalised ei või selle jututoaga liituda. + Sina lubasid külalistel selle jututoaga liituda. + %1$s lubas külalistel selle jututoaga liituda. + Sina lahkusid jututoast. Põhjus: %1$s + %1$s lahkus jututoast. Põhjus: %2$s + Sina liitusid jututoaga. Põhjus: %1$s + %1$s liitus jututoaga. Põhjus: %2$s + Sina võtsid tagasi jututoaga liitumise kutse kasutajalt %1$s + %1$s võttis tagasi jututoaga liitumise kutse kasutajalt %2$s + Sina kutsusid kasutajat %1$s + %1$s kutsus kasutajat %2$s + Sa uuendasid seda jututuba. + %s uuendas seda jututuba. + Sina seadistasid, et tulevased jututoa sõnumid on nähtavad kasutajale %1$s + %1$s seadistas, et tulevased jututoa sõnumid on nähtavad kasutajale %2$s + Sina lahkusid jututoast + %1$s lahkus jututoast + Sina liitusid + %1$s liitus + Sina alustasid vestlust + %1$s alustas vestlust + \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-fa/strings.xml b/matrix-sdk-android/src/main/res/values-fa/strings.xml index b88a98459d..25d92b4abe 100644 --- a/matrix-sdk-android/src/main/res/values-fa/strings.xml +++ b/matrix-sdk-android/src/main/res/values-fa/strings.xml @@ -40,10 +40,10 @@ (تصویر هم عوض شد) %1$s نام اتاق را پاک کرد %1$s موضوع اتاق را پاک کرد - پیام پاک شد - پیام به دست %1$s پاک شد - پیام پاک شد [دلیل: %1$s] - پیام به دست %1$s پاک شد [دلیل: %2$s] + پیام برداشته شد + پیام به دست %1$s برداشته شد + پیام برداشته شد [دلیل: %1$s] + پیام به دست %1$s برداشته شد [دلیل: %2$s] %1$s دعوتی برای پیوستن %2$s به اتاق فرستاد %1$s دعوت پیوستن به اتاق %2$s را باطل کرد %1$s دعوت برای %2$s را پذیرفت @@ -56,7 +56,7 @@ شکست در بارگذاری تصویر خطای شبکه - خطای ماتریس + خطای ماتریکس در حال حاضر امکان بازپیوست به اتاقی خالی وجود ندارد‌‌. @@ -135,6 +135,98 @@ %s درخواست تأیید کلیدتان را دارد، ولی کارخواهتان تأیید کلید درون گپ را پشتیبانی نمی‌کند. برای تأیید کلیدها لازم است از تأییدیهٔ کلید قدیمی استفاده کنید. %1$s اتاق را ایجاد کرد - %1$s نمایه خود را به‌روز کرد %2$s + %1$s نمایه‌اش را به‌روز کرد %2$s نمی‌توان ویرایش کرد + تصویری فرستادید. + برچسبی فرستادید. + + دعوتتان + اتاق را ایجاد کردید + از %1$s دعوت کردید + به اتاق پیوستید + اتاق را ترک کردید + دعوت را رد کردید + %1$s را اخراج کردید + تحریم %1$s را برداشتید + %1$s را تحریم کردید + دعوت %1$s را پس‌گرفتید + آواتارتان را عوض کردید + نام نمایشیتان را به %1$s تغییر دادید + نام نمایشیتان را از %1$s به %2$s تغییر دادید + نام نمایشیتان را برداشتید (%1$s بود) + موضوع را به %1$s تغییر دادید + %1$s آواتار اتاق را تغییر داد + آواتار اتاق را تغییر دادید + نام اتاق را به %1$s تغییر دادید + تماس تصویری گرفتید. + تماس صوتی گرفتید. + %s برای برپایی تماس، داده فرستاد. + برای برپایی تماس، داده فرستادید. + تماس را پاسخ دادید. + به تماس پایان دادید. + تاریخچهٔ آتی اتاق را برای %1$s نمایان کردید + رمزنگاری سرتاسری را روشن کردید (%1$s) + این اتاق را ارتقا دادید. + + دارخواست کنفرانس ویپ دادید + نام اتاق را برداشتید + موضوع اتاق را برداشتید + %1$s آواتار اتاق را برداشت + آواتار اتاق را برداشتید + نمایه‌تان را به‌روز کردید %1$s + برای %1$s دعوت پیوستن به اتاق فرستادید + دعوت پیوستن %1$s به اتاق را پس گرفتید + دعوت برای %1$s را پذیرفتید + + %1$s ابزارک %2$s را افزود + ابزارک %1$s را افزودید + %1$s ابزارک %2$s را برداشت + ابزارک %1$s را برداشتید + %1$s ابزارک %2$s را دستکاری کرد + ابزارک %1$s را دستکاری کردید + + مدیر + ناظم + پیش‌گزیده + سفارشی (%1$d) + سفارشی + + سطح قدرت %1$s را تغییر دادید. + %1$s سطح قدرت %2$s را تغییر داد. + %1$s از %2$s به %3$s + + دعوتتان. دلیل: %1$s + %1$s را دعوت کردید. دلیل: %2$s + به اتاق پیوستید. دلیل: %1$s + اتاق را ترک کردید. دلیل: %1$s + دعوت را رد کردید. دلیل: %1$s + %1$s را اخراج کردید. دلیل: %2$s + تحریم %1$s را برداشتید. دلیل: %2$s + %1$s را تحریم کردید. دلیل: %2$s + دعوتی به %1$s برای پیوستن به اتاق فرستادید. دلیل: %2$s + دعوت %1$s برای پیوستن به اتاق را پس گرفتید. دلیل: %2$s + دعوت برای %1$s را پذیرفتید. دلیل: %2$s + دعوت %1$s را رد کردید. دلیل: %2$s + + + نشانی %1$s را به این اتاق افزودید. + نشانی‌های %1$s را به این اتاق افزودید. + + + + نشانی %1$s ار از این اتاق برداشتید. + نشانی‌های %1$s ار از این اتاق برداشتید. + + + نشانی %1$s ار افزوده و %2$s را از این اتاق برداشتید. + + نشانی اصلی این اتاق را به %1$s تنظیم کردید. + نشانی اصلی این اتاق را برداشتید. + + به میهمانان اجازهٔ پیوستن به گروه دادید. + میمهانان را از پیوستن به گروه بازداشتید. + + رمزنگاری سرتاسری را روشن کردید. + رمزنگاری سرتاسری را روشن کردید (الگوریتم ناشناخته %1$s). + diff --git a/matrix-sdk-android/src/main/res/values-it/strings.xml b/matrix-sdk-android/src/main/res/values-it/strings.xml index cf081752a2..d4479be674 100644 --- a/matrix-sdk-android/src/main/res/values-it/strings.xml +++ b/matrix-sdk-android/src/main/res/values-it/strings.xml @@ -1,8 +1,7 @@ - + %1$s: %2$s %1$s ha inviato un\'immagine. - Invito di %s %1$s ha invitato %2$s %1$s ti ha invitato @@ -30,55 +29,40 @@ chiunque. sconosciuto (%s). %1$s ha attivato la crittografia end-to-end (%2$s) - %1$s ha richiesto una conferenza VoIP Conferenza VoIP iniziata Conferenza VoIP terminata - (anche l\'avatar è cambiato) %1$s ha rimosso il nome della stanza %1$s ha rimosso l\'argomento della stanza %1$s ha aggiornato il profilo %2$s %1$s ha mandato un invito a %2$s per unirsi alla stanza %1$s ha accettato l\'invito per %2$s - ** Impossibile decriptare: %s ** Il dispositivo del mittente non ci ha inviato le chiavi per questo messaggio. - Impossibile revisionare Impossibile inviare il messaggio - Invio dell\'immagine fallito - Errore di rete Errore di Matrix - Al momento non è possibile rientrare in una stanza vuota. - Messaggio criptato - Indirizzo email Numero di telefono - %1$s ha inviato un adesivo. - Invito da %s Invito nella stanza %1$s e %2$s Stanza vuota - %1$s e 1 altro %1$s e %2$d altri - - Messaggio rimosso Messaggio rimosso da %1$s Messaggio rimosso [motivo: %1$s] Messaggio rimosso da %1$s [motivo: %2$s] - Sync iniziale: \nImportazione account… Sync iniziale: @@ -95,12 +79,9 @@ \nImportazione comunità Sync iniziale: \nImportazione dati account - %s ha aggiornato questa stanza. - Invio messaggio in corso … Cancella la coda di invio - %1$s ha revocato l\'invito a %2$s di unirsi alla stanza Invito di %1$s. Motivo: %2$s %1$s ha invitato %2$s. Motivo: %3$s @@ -115,34 +96,25 @@ %1$s ha revocato l\'invito a %2$s di unirsi alla stanza. Motivo: %3$s %1$s ha accettato l\'invito per %2$s. Motivo: %3$s %1$s ha rifiutato l\'invito di %2$s. Motivo: %3$s - %1$s ha aggiunto %2$s come indirizzo per questa stanza. %1$s ha aggiunto %2$s come indirizzi per questa stanza. - %1$s ha rimosso %2$s come indirizzo per questa stanza. %1$s ha rimosso %3$s come indirizzi per questa stanza. - %1$s ha aggiunto %2$s e rimosso %3$s come indirizzi per questa stanza. - %1$s ha impostato l\'indirizzo principale per questa stanza a %2$s. %1$s ha rimosso l\'indirizzo principale per questa stanza. - %1$s ha permesso l\'accesso alla stanza per gli ospiti. %1$s ha impedito l\'accesso alla stanza per gli ospiti. - %1$s ha attivato la cifratura end-to-end. %1$s ha attivato la cifratura end-to-end (algoritmo %2$s non riconosciuto). - %s sta chiedendo di verificare la tua chiave, ma il tuo client non supporta la verifica in-chat. Dovrai usare il metodo di verifica obsoleto per verificare le chiavi. - %1$s ha creato la stanza Hai inviato un\'immagine. Hai inviato un adesivo. - Il tuo invito Hai creato la stanza Hai invitato %1$s @@ -170,7 +142,6 @@ Hai reso visibile la futura cronologia della stanza a %1$s Hai attivato la crittografia end-to-end (%1$s) Hai aggiornato questa stanza. - Hai richiesto una conferenza VoIP Hai rimosso il nome della stanza Hai rimosso l\'argomento della stanza @@ -180,24 +151,20 @@ Hai mandato un invito a %1$s a unirsi alla stanza Hai revocato l\'invito per %1$s a unirsi alla stanza Hai accettato l\'invito per %1$s - %1$s ha aggiunto il widget %2$s Hai aggiunto il widget %1$s %1$s ha rimosso il widget %2$s Hai rimosso il widget %1$s %1$s ha modificato il widget %2$s Hai modificato il widget %1$s - Amministratore Moderatore Predefinito Personalizzato (%1$d) Personalizzato - Hai cambiato il livello di potere di %1$s. %1$s ha cambiato il livello di potere di %2$s. %1$s da %2$s a %3$s - Il tuo invito. Motivo: %1$s Hai invitato %1$s. Motivo: %2$s Sei entrato nella stanza. Motivo: %1$s @@ -209,27 +176,42 @@ Hai mandato un invito a %1$s a unirsi alla stanza. Motivo: %2$s Hai revocato l\'invito a %1$s a unirsi alla stanza. Motivo: %2$s Hai accettato l\'invito per %1$s. Motivo: %2$s - Hai ritirato l\'invito di %2$s. Motivo: %2$s - + Hai ritirato l\'invito di %1$s. Motivo: %2$s Hai aggiunto %1$s come indirizzo per questa stanza. Hai aggiunto %1$s come indirizzi per questa stanza. - Hai rimosso %1$s come indirizzo per questa stanza. Hai rimosso %2$s come indirizzi per questa stanza. - Hai aggiunto %1$s e rimosso %2$s come indirizzi per questa stanza. - Hai impostato l\'indirizzo principale per questa stanza a %1$s. Hai rimosso l\'indirizzo principale per questa stanza. - Hai permesso l\'accesso alla stanza per gli ospiti. Hai impedito l\'accesso alla stanza per gli ospiti. - Hai attivato la crittografia end-to-end. Hai attivato la crittografia end-to-end (algoritmo %1$s sconosciuto). - - + Hai impedito l\'accesso alla stanza agli ospiti. + %1$s ha impedito l\'accesso alla stanza agli ospiti. + Hai permesso l\'accesso agli ospiti. + %1$s ha permesso l\'accesso agli ospiti. + Sei entrato. Motivo: %1$s + Sei uscito. Motivo: %1$s + %1$s è uscito. Motivo: %2$s + %1$s è entrato. Motivo: %2$s + Hai revocato l\'invito per %1$s + %1$s ha revocato l\'invito per %2$s + Hai invitato %1$s + %1$s ha invitato %2$s + Hai aggiornato la stanza. + %s ha aggiornato la stanza. + Hai reso visibili i messaggi futuri a %1$s + %1$s ha reso visibili i messaggi futuri a %2$s + Sei uscito dalla stanza + %1$s è uscito dalla stanza + Sei entrato + %1$s è entrato + Hai creato la discussione + %1$s ha creato la discussione + \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-kab/strings.xml b/matrix-sdk-android/src/main/res/values-kab/strings.xml index 0d1cad6550..e557a7c824 100644 --- a/matrix-sdk-android/src/main/res/values-kab/strings.xml +++ b/matrix-sdk-android/src/main/res/values-kab/strings.xml @@ -1,4 +1,4 @@ - + %1$s: %2$s %1$s t.yuzen tugna. @@ -23,16 +23,11 @@ Aseɣyad Amezwer Sagen - %1$s seg %2$s ɣer %3$s - Tegguma ad d-tali tugna - Tansa n yimayl - %1$s azen astiker. Tuzneḍ amenṭaḍ. - %1$s yekkes agdal i %2$s Tekkseḍ agdal i %1$s %1$s igdel %2$s @@ -46,12 +41,12 @@ Tbeddleḍ isem-ik•im i d-ittuseknen seg %1$s ɣer %2$s %1$s yekkes isem-is i d-ittuseknen (yella %2$s) Tekkseḍ isem-ik·im yettwaskanen (d %1$s) - %1$S isnifel asentel s: %2$S - Tesnifleḍ asentel s: %2$S + %1$s isnifel asentel s: %2$s + Tesnifleḍ asentel s: %1$s %1$s ibeddel avaṭar n texxamt Tbeddleḍ avaṭar n texxamt %1$s ibeddel isem n texxamt s: %2$s - Tbeddleḍ isem n texxamt s: %2$s + Tbeddleḍ isem n texxamt s: %1$s %s isɛedda siwel s tvidyut. Tesɛeddaḍ siwel s tvidyut. %s isɛedda asiwel s taɣect. @@ -68,15 +63,13 @@ yal yiwen. arussin (%s). %1$s isermed awgelhen seg yixef ɣer yixef (%2$s) - Tesremdeḍ awgelhen seg yixef ɣer yixef (%2$s) + Tesremdeḍ awgelhen seg yixef ɣer yixef (%1$s) %s ileqqem taxxamt-a. Tleqqmeḍ taxxamt-a. - %1$s isuter-d asarag VoIP Tsutreḍ-d asarag VoIP Asarag VoIP yebda Asarag VoIP yekfa - (avatar daɣen ibeddel) %1$s yekkes isem n texxamt Tekkseḍ isem n texxamt @@ -94,51 +87,37 @@ Tuzneḍ tinubga i %1$s akken ad yeddu ɣer texxamt %1$s iqbel tinubga i %2$s Tqebleḍ tinubga i %1$s - %1$s yerna awiǧit %2$s Terniḍ awiǧit %1$s %1$s yekkes awiǧit %2$s Tekkseḍ awiǧit %1$s %1$s ibeddel awiǧit %2$s Tbeddleḍ awiǧit %1$s - - Sagen (%1$) + Sagen (%1$d) Tbeddleḍ aswir n tezmert n %1$s. %1$s ibeddel aswir n tezmert n %2$s. ** Awgelhen d awezɣi: %s ** Ibenk n umazan ur aɣ-d-yuzin ara tisura i yizen-a. - Tuzna n yizen d tawezɣit - Tuccḍa deg uẓeṭṭa Tuccḍa deg Matrix - %1$s iga amazray n texxamyt i d-iteddun yettban i %2$s Tgiḍ amazray n texxamyt i d-iteddun yettban i %1$s %1$s issefsax tinubga i %2$s i wakken ad d-yekcem ɣer texxamt Tesfesxeḍ tinubga i %1$s i wakken ad d-yernu ɣer texxamt D awezɣi tura ad nales ad nuɣal ɣer texxamt tilemt. - Izen yettwawgelhen - Uṭṭun n tiliɣri - Tinubga sɣur %s Tinubga ɣer texxamt - %1$s d %2$s - %1$s d 1 wayeḍ %1$s d %2$d wiyaḍ - Tremdeḍ awgelhen seg yixef ɣer yixef (alguritm %1$s ur yettwassen ara). - %s isuter-d ad isenqed tasarut-ik·im, maca amsaɣ-ik·im ur issefrak ara asenqed n tsura deg yidiwenniyen. Ilaq-ak·am useqdec asenqed iqdim n tsura i usenqed n tsura. - Taxxamt tilemt - Amtawi n tazwara: \nAktar n umiḍan… Amtawi n tazwara: @@ -155,7 +134,6 @@ \nAktar n tmezdagnutin Amtawi n tazwara: \nAktar n yisefka n umiḍan - Tuzzna n yizen… Tinubga n %1$s. Tamentilt: %2$s Tinubga-k•m. Tamentilt: %1$s @@ -180,46 +158,37 @@ Tqebleḍ tinubga i %1$s. Tamentilt: %2$s %1$s issefsex tinubga n %2$s. Tamentilt: %3$s Tesfesxeḍ tinubga n %1$s. Tamentilt: %2$s - %1$s yerna %2$s d tansa i texxamt-a. %1$s yerna %2$s d tansiwin i texxamt-a. - Terniḍ %1$s d tansa i texxamt-a. Terniḍ %1$s d tansiwin i texxamt-a. - %1$s yekkes %2$s am tansa i texxamt-a. %1$s yekkes %3$s am tansiwin i texxamt-a. - Tekkseḍ %1$s am tansa i texxamt-a. Tekkseḍ %2$s am tansiwin i texxamt-a. - - %1$s yerna %2$s terniḍ tekkseḍ %3s am tansiwin i texxamt-a. + %1$s yerna %2$s terniḍ tekkseḍ %3$s am tansiwin i texxamt-a. Terniḍ %1$s terniḍ tekkseḍ %2$s am tansiwin i texxamt-a. - %1$s isbadu %2$s am tansa tagejdant i texxamt-a. Tesbaduḍ %1$s am tansa tagejdant i texxamt-a. %1$s yekkes tansa tagejdant i texxamt-a. Tekkseḍ tansa tagejdant i texxamt-a. - %1$s isireg inebgawen ad ddun ɣer texxamt. Tsirgeḍ inebgawen ad ddun ɣer texxamt. %1$s issewḥel inebgawen iwakken ur tteddun ara ɣer texxamt. Tesweḥleḍ inebgawen iwakken ur tteddun ara ɣer texxamt. - %1$s yermed awgelhen seg yixef ɣer yixef. Tremdeḍ awgelhen seg yixef ɣer yixef. %1$s yermed awgelhen seg yixef ɣer yixef (alguritm %2$s ur yettwassen ara). Sfeḍ tabdart n uraǧu n tuzzna - - %1$s issefsex tinubga n %2$s i tmerniwt ɣer texxamt. Tamentilt: %2$s + %1$s issefsex tinubga n %2$s i tmerniwt ɣer texxamt. Tamentilt: %3$s Tesfesxeḍ tinubga n %1$s i tmerna ɣer texxamt. Tamentilt: %2$s Yegguma ad yaru - + \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-pt-rBR/strings.xml b/matrix-sdk-android/src/main/res/values-pt-rBR/strings.xml index 52b935c097..4e62a21c0e 100644 --- a/matrix-sdk-android/src/main/res/values-pt-rBR/strings.xml +++ b/matrix-sdk-android/src/main/res/values-pt-rBR/strings.xml @@ -1,9 +1,7 @@ - + - %1$s: %2$s %1$s enviou uma foto. - convite de %s %1$s convidou %2$s %1$s convidou você @@ -31,64 +29,45 @@ qualquer pessoa. desconhecido (%s). %1$s ativou a criptografia de ponta a ponta (%2$s) - %1$s deseja iniciar uma chamada em grupo Chamada em grupo iniciada Chamada em grupo encerrada - (a foto de perfil também foi alterada) %1$s removeu o nome da sala %1$s removeu a descrição da sala %1$s atualizou o perfil %2$s %1$s enviou um convite para %2$s entrar na sala %1$s aceitou o convite para %2$s - ** Não foi possível descriptografar: %s ** O aparelho do remetente não nos enviou as chaves para esta mensagem. - Não foi possível redigir Não foi possível enviar a mensagem - O envio da imagem falhou - Erro de conexão à internet Erro no servidor Matrix - - - - Atualmente, não é possível entrar novamente em uma sala vazia. - Mensagem criptografada - Endereço de e-mail Número de telefone - - %1$s enviou uma figurinha. - Convite de %s Convite para sala %1$s e %2$s Sala vazia - %1$s e 1 outro %1$s e %2$d outros - - Você enviou uma foto. Você enviou uma figurinha. - Seu convite %1$s criou a sala Você criou a sala @@ -118,7 +97,6 @@ Você ativou a criptografia de ponta a ponta (%1$s) %s atualizou esta sala. Você atualizou esta sala. - Você solicitou uma chamada em grupo Você removeu o nome da sala Você removeu a descrição da sala @@ -133,24 +111,20 @@ %1$s cancelou o convite a %2$s para entrar na sala Você cancelou o convite a %1$s para entrar na sala Você aceitou o convite para %1$s - %1$s adicionou o widget %2$s Você adicionou o widget %1$s %1$s removeu o widget %2$s Você removeu o widget %1$s %1$s editou o widget %2$s Você editou o widget %1$s - Administrador Moderador Padrão Personalizado (%1$d) Personalizado - Você alterou o nível de permissão de %1$s. %1$s alterou o nível de permissão de %2$s. %1$s de %2$s para %3$s - Primeira sincronização: \nImportando a conta… Primeira sincronização: @@ -167,10 +141,8 @@ \nImportando as comunidades Primeira sincronização: \nImportando os dados da conta - Enviando mensagem… Limpar a fila de envio - Convite de %1$s. Motivo: %2$s O seu convite. Motivo: %1$s %1$s convidou %2$s. Motivo: %3$s @@ -196,45 +168,57 @@ Você aceitou o convite para %1$s. Motivo: %2$s %1$s desfez o convite de %2$s. Motivo: %3$s Você desfez o convite de %1$s. Motivo: %2$s - %1$s adicionou %2$s como um endereço desta sala. %1$s adicionou %2$s como endereços desta sala. - Você adicionou %1$s como um endereço desta sala. Você adicionou %1$s como endereços desta sala. - %1$s removeu %2$s como um endereço desta sala. %1$s removeu %3$s como endereços desta sala. - Você removeu %1$s como um endereço desta sala. Você removeu %2$s como endereços desta sala. - %1$s adicionou %2$s e removeu %3$s como endereços desta sala. Você adicionou %1$s e removeu %2$s como endereços desta sala. - %1$s definiu o endereço principal desta sala como %2$s. Você definiu o endereço principal desta sala como %1$s. %1$s removeu o endereço principal desta sala. Você removeu o endereço principal desta sala. - %1$s permitiu que convidados entrem na sala. Você permitiu que convidados entrem na sala. %1$s impediu que convidados entrassem na sala. Você impediu que convidados entrassem na sala. - %1$s ativou a criptografia de ponta a ponta. Você ativou a criptografia de ponta a ponta. %1$s ativou a criptografia de ponta a ponta (algoritmo não reconhecido %2$s). Você ativou a criptografia de ponta a ponta (algoritmo não reconhecido %1$s). - %s deseja confirmar a sua chave, mas o seu aplicativo não suporta a confirmação da chave da conversa. Você precisará usar a confirmação tradicional de chaves para confirmar chaves. - - + Você impediu que desconhecidos entrem na sala. + %1$s impediu que desconhecidos entrem na sala. + Você permitiu que desconhecidos entrem aqui. + %1$s permitiu que desconhecidos entrem aqui. + Você saiu. Motivo: %1$s + %1$s saiu. Motivo: %2$s + Você entrou. Motivo: %1$s + %1$s entrou. Motivo: %2$s + Você cancelou o convite para %1$s + %1$s cancelou o convite para %2$s + Você convidou %1$s + %1$s convidou %2$s + Você atualizou esta sala. + %s atualizou esta sala. + Você definiu que as mensagens enviadas a partir do presente momento estarão disponíveis para %1$s + %1$s definiu que as mensagens enviadas a partir do presente momento estarão disponíveis para %2$s + Você saiu da sala + %1$s saiu da sala + Você entrou + %1$s entrou + Você criou a sala + %1$s criou a sala + \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-ru/strings.xml b/matrix-sdk-android/src/main/res/values-ru/strings.xml index a4d752782e..97643a34fe 100644 --- a/matrix-sdk-android/src/main/res/values-ru/strings.xml +++ b/matrix-sdk-android/src/main/res/values-ru/strings.xml @@ -1,9 +1,7 @@ - + - %1$s: %2$s %1$s отправил(а) изображение. - %s приглашение %1$s пригласил(а) %2$s %1$s пригласил(а) вас @@ -31,67 +29,49 @@ всем. неизвестно (%s). %1$s включил(а) сквозное шифрование (%2$s) - %1$s запросил(а) VoIP конференцию VoIP-конференция начата VoIP-конференция завершена - (аватар также был изменен) %1$s удалил(а) название комнаты %1$s удалил(а) тему комнаты %1$s обновил(а) свой профиль %2$s %1$s отправил(а) приглашение %2$s присоединиться к комнате %1$s принял(а) приглашение от %2$s - ** Невозможно расшифровать: %s ** Устройство отправителя не предоставило нам ключ для расшифровки этого сообщения. - Не удалось изменить Не удалось отправить сообщение - Не удалось загрузить изображение - Сетевая ошибка Ошибка Matrix - - - - В настоящее время невозможно вновь присоединиться к пустой комнате. - Зашифрованное сообщение - Адрес электронной почты Номер телефона - %1$s отправил стикер. - Приглашение от %s Приглашение в комнату %1$s и %2$s Пустая комната - %1$s и 1 другой %1$s и %2$d другие %1$s и %2$d других - + - - Сообщение удалено %1$s удалил(а) сообщение Сообщение удалено [причина: %1$s] %1$s удалил(а) сообщение [причина: %2$s] - Начальная синхронизация: \nИмпорт учетной записи… Начальная синхронизация: @@ -108,12 +88,9 @@ \nИмпорт сообществ Начальная синхронизация: \nИмпорт данных учетной записи - %s обновил эту комнату. - Отправка сообщения… Очистить очередь отправки - %1$s отозвал приглашение %2$s присоединиться к комнате Приглашение %1$s. Причина: %2$s %1$s приглашен %2$s. Причина: %3$s @@ -128,36 +105,27 @@ %1$s отозвал приглашение %2$s присоединиться к комнате. Причина: %3$s %1$s принял приглашение для %2$s. Причина: %3$s %1$s отозвал приглашение %2$s. Причина: %3$s - %1$s создал(а) комнату %1$s добавил(а) %2$s в качестве адреса для этой комнаты. %1$s добавил(а) %2$s в качестве адресов для этой комнаты. %1$s добавил(а) %2$s в качестве адресов для этой комнаты. - %1$s удалил(а) адрес %2$s для комнаты. %1$s удалил(а) адреса %2$s для комнаты. %1$s удалил(а) адреса %2$s для комнаты. - %1$s добавил(а) адреса %2$s и удалил(а) %3$s для комнаты. - %1$s сделал(а) %2$s главным адресом комнаты. %1$s удалил(а) главный адрес комнаты. - %1$s разрешил(а) гостям входить в комнату. %1$s запретил(а) гостям входить в комнату. - %1$s включил(а) сквозное шифрование. %1$s включил(а) сквозное шифрование (неизвестный алгоритм %2$s). - %s запрашивает подтверждение вашего ключа, но ваш клиент не поддерживает подтверждение в чате. Используйте устаревшую проверку для сверки ключей. - Вы отправили изображение. Вы отправили стикер. - Ваше приглашение Вы создали комнату Вы пригласили %1$s @@ -181,7 +149,6 @@ Вы сделали будущую историю комнаты видимой для %1$s Вы включили сквозное шифрование (%1$s) Вы обновили эту комнату. - Вы начали групповой звонок Вы удалили название комнаты Вы удалили тему комнаты @@ -189,24 +156,20 @@ Вы отправили %1$s приглашение в эту комнату Вы отозвали у %1$s приглашение в эту комнату Вы приняли приглашение для %1$s - %1$s добавил(а) виджет %2$s Вы добавили виджет %1$s %1$s удалил(а) виджет %2$s Вы удалили виджет %1$s %1$s изменил(а) виджет %2$s Вы изменили виджет %1$s - Администратор Модератор По умолчанию Пользовательский (%1$d) Пользовательский - Вы изменили уровни доступа %1$s. %1$s изменил(а) уровни доступа %2$s. %1$s с %2$s на %3$s - Ваше приглашение. Причина: %1$s Вы пригласили %1$s. Причина: %2$s Вы вошли в комнату. Причина: %1$s @@ -219,35 +182,47 @@ Вы отозвали у %1$s приглашение в эту комнату. Причина: %2$s Вы приняли приглашение для %1$s. Причина: %2$s Вы отозвали приглашение %1$s. Причина: %2$s - Вы добавили адрес %1$s для этой комнаты. Вы добавили %1$s в качестве адресов для этой комнаты. Вы добавили %1$s в качестве адресов для этой комнаты. - Вы удалили адрес этой комнаты: %1$s. Вы удалили адреса этой комнаты: %1$s. Вы удалили адреса этой комнаты: %1$s. - Вы добавили адреса %1$s и удалили %2$s для этой комнаты. - Вы задали главный адрес этой комнаты %1$s. Вы удалили главный адрес этой комнаты. - Вы разрешили гостям входить в комнату. Вы запретили гостям входить в комнату. - Вы включили сквозное шифрование. Вы включили сквозное шифрование (неизвестный алгоритм %1$s). - %1$s изменил(а) аватар комнаты Вы изменили аватар комнаты %s отправил(а) данные для начала звонка. Вы отправили данные для начала звонка. %1$s удалил(а) аватар комнаты Вы удалили аватар комнаты - - + Вы запретили гостям входить в комнату. + %1$s запретил(а) гостям входить в комнату. + Вы разрешили гостям присоединяться сюда. + %1$s разрешил(а) гостям присоединиться сюда. + Вы вышли. Причина: %1$s + %1$s вышел(-ла). Причина: %2$s + Вы вошли. Причина: %1$s + %1$s вошел(-ла). Причина: %2$s + Вы отозвали приглашение %1$s + %1$s отозвал(а) приглашение %2$s + Вы пригласили %1$s + %1$s пригласил(а) %2$s + Вы сделали будущие сообщения видимыми для %1$s + %1$s сделал(а) будущие сообщения видимыми для %2$s + Вы покинули комнату + %1$s покинул(а) комнату + Вы вошли + %1$s вошел(ла) + Вы создали обсуждение + %1$s создал(а) обсуждение + \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-sk/strings.xml b/matrix-sdk-android/src/main/res/values-sk/strings.xml index da869eacc2..c75c8b4832 100644 --- a/matrix-sdk-android/src/main/res/values-sk/strings.xml +++ b/matrix-sdk-android/src/main/res/values-sk/strings.xml @@ -1,8 +1,7 @@ - + %1$s: %2$s %1$s poslal/a obrázok. - Pozvanie od %s %1$s pozval/a %2$s %1$s vás pozval/a @@ -30,58 +29,42 @@ pre každého. neznámym (%s). %1$s povolil/a E2E šifrovanie (%2$s) - %1$s požiadal/a o VoIP konferenciu Začala sa VoIP konferencia Skončila sa VoIP konferencia - (aj obrázok v profile) %1$s odstránil/a názov miestnosti %1$s odstránil/a tému miestnosti %1$s aktualizoval/a svoj profil %2$s %1$s pozval/a %2$s vstúpiť do miestnosti %1$s prijal/a pozvanie pre %2$s - ** Nie je možné dešifrovať: %s ** Zo zariadenia odosieľateľa nebolo možné získať kľúče potrebné na dešifrovanie tejto správy. - Nie je možné vymazať Nie je možné odoslať správu - Nepodarilo sa nahrať obrázok - Chyba siete Chyba Matrix - V súčasnosti nie je možné znovu vstúpiť do prázdnej miestnosti. - Šifrovaná správa - Emailová adresa Telefónne číslo - %1$s poslal/a nálepku. - Pozvanie od %s Pozvanie do miestnosti %1$s a %2$s Prázdna miestnosť - %1$s a 1 ďalší %1$s a %2$d ďalší %1$s a %2$d ďalších - + - - %s aktualizoval/a túto miestnosť. - Odstránená správa Odstránená správa používateľom %1$s Odstránená správa [dôvod: %1$s] Odstránená správa používateľom %1$s [dôvod: %2$s] - Úvodná synchronizácia: \nPrebieha import účtu… Úvodná synchronizácia: @@ -98,10 +81,8 @@ \nPrebieha import komunít Úvodná synchronizácia: \nPrebieha import údajov účtu - Odosielanie správy… Vymazať správy na odoslanie - %1$s zamietol/a pozvanie používateľa %2$s vstúpiť do miestnosti Pozvanie od %1$s. Dôvod: %2$s %1$s pozval/a %2$s. Dôvod: %3$s @@ -116,28 +97,22 @@ %1$s zamietol/a pozvanie používateľa %2$s vstúpiť do miestnosti. Dôvod: %3$s %1$s prijal/a pozvanie pre %2$s. Dôvod: %3$s %1$s vzal/a späť pozvanie %2$s. Dôvod: %3$s - %1$s pridal/a adresu %2$s pre túto miestnosť. %1$s pridal/a adresy %2$s pre túto miestnosť. %1$s pridal/a adresy %2$s pre túto miestnosť. - %1$s odstránil/a adresu %2$s pre túto miestnosť. %1$s odstránil/a adresy %3$s pre túto miestnosť. %1$s odstránil/a adresy %3$s pre túto miestnosť. - %1$s pridal/a adresy %2$s a odstránil/a adresy %3$s pre túto miestnosť. - %1$s nastavil/a hlavnú adresu tejto miestnosti %2$s. %1$s odstránil/a hlavnú adresu tejto miestnosti. - %1$s povolil/a hosťom///návštevníkom prístup do tejto miestnosti. Poslali ste obrázok. Poslali ste nálepku. - Pozvanie od vás %1$s vytvoril/a miestnosť Vytvorili ste miestnosť @@ -166,7 +141,6 @@ Sprístupnili ste budúcu históriu miestnosti %1$s Povolili ste E2E šifrovanie (%1$s) Aktualizovali ste túto miestnosť. - Požiadali ste o VoIP konferenciu Odstránili ste názov miestnosti Odstránili ste tému miestnosti @@ -174,26 +148,22 @@ Odstránili ste obrázok miestnosti Aktualizovali ste svoj profil %1$s Pozvali ste %1$s vstúpiť do miestnosti - Zamietli ste pozvanie používateľa %2$s vstúpiť do miestnosti + Zamietli ste pozvanie používateľa %1$s vstúpiť do miestnosti Prijali ste pozvanie pre %1$s - %1$s pridal/a widget %2$s Pridali ste widget %1$s %1$s odstránil/a widget %2$s Odstránili ste widget %1$s %1$s upravil/a widget %2$s Upravili ste widget %1$s - Správca Moderátor Predvolený Vlastná úroveň (%1$d) Vlastná úroveň - Zmenili ste úroveň moci používateľa %1$s. %1$s zmenil úroveň moci používateľa %2$s. %1$s z %2$s na %3$s - Pozvanie od vás. Dôvod: %1$s Pozvali ste %1$s. Dôvod: %2$s Vstúpili ste do miestnosti. Dôvod: %1$s @@ -206,33 +176,25 @@ Zamietli ste pozvanie používateľa %1$s vstúpiť do miestnosti. Dôvod: %2$s Prijali ste pozvanie pre %1$s. Dôvod: %2$s Vzali ste späť pozvanie %1$s. Dôvod: %2$s - Pridali ste adresu %1$s pre túto miestnosť. Pridali ste adresy %1$s pre túto miestnosť. Pridali ste adresy %1$s pre túto miestnosť. - Odstránili ste adresu %1$s pre túto miestnosť. Odstránili ste adresy %2$s pre túto miestnosť. Odstránili ste adresy %2$s pre túto miestnosť. - Pridali ste %1$s a odstránili adresy %2$s pre túto miestnosť. - - Nastavili ste hlavnú adresu tejto miestnosti %2$s. + Nastavili ste hlavnú adresu tejto miestnosti %1$s. Odstránili ste hlavnú adresu tejto miestnosti. - Povolili ste hosťom///návštevníkom prístup do tejto miestnosti. %1$s zakázal/a hosťom///návštevníkom prístup do tejto miestnosti. Zakázali ste hosťom///návštevníkom prístup do tejto miestnosti. - %1$s povolil/a E2E šifrovanie. Povolili ste E2E šifrovanie. %1$s povolil/a E2E šifrovanie (Nerozpoznaný algorytmus %2$s). Povolili ste E2E šifrovanie (Nerozpoznaný algorytmus %1$s). - %s požaduje overenie vašich šifrovacích kľúčov, ale váš klient nepodporuje overenie kľúčov v konverzácii. Budete musieť použiť zastaralú metódu overenia. - - + \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-sq/strings.xml b/matrix-sdk-android/src/main/res/values-sq/strings.xml index 14a7c61bbc..9756a11762 100644 --- a/matrix-sdk-android/src/main/res/values-sq/strings.xml +++ b/matrix-sdk-android/src/main/res/values-sq/strings.xml @@ -138,4 +138,96 @@ %s po kërkon të verifikojë kyçin tuaj, por klienti juaj nuk mbulon verifikim kyçesh brenda fjalosjeje. Që të verifikoni kyça, do t’ju duhet të përdorni verifikim të dikurshëm kyçesh. %1$s krijo dhomën + Dërguat një figurë. + Dërguat një ngjitës. + + Ftesa juaj + Krijuat dhomën + Ftuat %1$s + Hytë në dhomë + Dolët nga dhoma + Hodhët poshtë ftesën + Përzutë %1$s + Hoqët dëbimin për %1$s + Dëbuat %1$s + Tërhoqët mbrapsht ftesën për %1$s + Ndryshuat avatarin tuaj + Caktuat si emrin tuaj në ekran %1$s + E ndryshuat emrin tuaj në ekran nga %1$s në %2$s + Hoqët emrin tuaj në ekran (qe %1$s) + E ndryshuat temën në: %1$s + %1$s ndryshoi avatarin e dhomës + Ndryshuat avatarin e dhomës + Ndryshuat emrin e dhomës në: %1$s + Filluat një thirrje video. + Filluat një thirrje zanore. + %s dërgoi të dhëna për ujdisjen e thirrjes. + Dërguat të dhëna për ujdisjen e thirrjes. + Iu përgjigjët thirrjes. + E përfunduat thirrjen. + E bëtë historikun e ardhshëm të dhomë të dukshëm për %1$s + Aktivizuat fshehtëzim skaj-më-skaj (%1$s) + Përmirësuat këtë dhomë. + + Kërkuat një konferencë VoIP + Hoqët emrin e dhomës + Hoqët temën e dhomës + %1$s hoqi avatarin e dhomës + Hoqët avatarin e dhomës + Përditësuat profilin tuaj %1$s + Dërguat një ftesë te %1$s për të ardhur te dhoma + Shfuqizuat ftesën për ardhjen në dhomë të %1$s + Pranuat ftesën për %1$s + + %1$s shtoi widget-in %2$s + Shtuat widget-in %1$s + %1$s hoqi widget-in %2$s + Hoqët widget-in %1$s + %1$s ndryshoi widget-in %2$s + Ndryshuat widget-in %1$s + + Përgjegjës + Moderator + Parazgjedhje + Vetjake (%1$d) + Vetjake + + Ndryshuat shkallën e pushtetit për %1$s. + %1$s ndryshoi shkallën e pushtetit për %2$s. + %1$s nga %2$s në %3$s + + Ftesa juaj. Arsye: %1$s + Ftuat %1$s. Arsye: %2$s + Erdhët në dhomë, Arsye: %1$s + Ikët nga dhoma. Arsye: %1$s + Hodhët poshtë ftesën. Arsye: %1$s + Përzutë %1$s. Arsye: %2$s + Hoqët dëbimin për %1$s. Arsye: %2$s + Dëbuat %1$s. Arsye: %2$s + Dërguat një ftesë për %1$s të vijë në dhomë. Arsye: %2$s + Shfuqizuat ftesën për ardhjen në dhomë të %1$s. Arsye: %2$s + Pranuat ftesën për %1$s. Arsye: %2$s + Tërhoqët mbrapsht ftesën për %1$s. Arsye: %2$s + + + Shtuat %1$s si një adresë për këtë dhomë. + Shtuat %1$s si adresa për këtë dhomë. + + + + Hoqët %1$s si një adresë për këtë dhomë. + Hoqët %1$s si adresa për këtë dhomë. + + + Shtuat %1$s dhe hoqët %2$s si adresa për këtë dhomë. + + Caktuat si adresë kryesore për këtë dhomë %1$s. + Hoqët adresën kryesore për këtë dhomë. + + Keni lejuar të vijnë mysafirë në dhomë. + U keni penguar mysafirëve të vijnë në dhomë. + + Aktivizuat fshehtëzimin skaj-më-skaj. + Aktivizuat fshehtëzimin skaj-më-skaj (algoritëm %1$s i panjohur). + diff --git a/matrix-sdk-android/src/main/res/values-sv/strings.xml b/matrix-sdk-android/src/main/res/values-sv/strings.xml index 8f8ba5e306..25e51b69e5 100644 --- a/matrix-sdk-android/src/main/res/values-sv/strings.xml +++ b/matrix-sdk-android/src/main/res/values-sv/strings.xml @@ -1,11 +1,10 @@ - + %1$s: %2$s %1$s skickade en bild. Du skickade en bild. %1$s skickade en dekal. Du skickade en dekal. - Inbjudan från %s Inbjudan från dig %1$s skapade rummet @@ -62,12 +61,10 @@ Du aktiverade totalsträckskryptering (%1$s) %s uppgraderade det här rummet. Du uppgraderade det här rummet. - %1$s begärde ett VoIP-gruppsamtal Du begärde ett VoIP-gruppsamtal VoIP-gruppsamtal startat VoIP-gruppsamtal avslutat - (avataren blev även bytt) %1$s tog bort rummets namn Du tog bort rummets namn @@ -87,54 +84,39 @@ Du drog tillbaka inbjudan för %1$s att gå med i rummet %1$s accepterade inbjudan för %2$s Du accepterade inbjudan för %1$s - %1$s lade till %2$s-widget Du lade till %1$s-widget %1$s tog bort %2$s-widget Du tog bort %1$s-widget %1$s modifierade %2$s-widget Du modifierade %1$s-widget - Admin Moderator Standard Anpassad (%1$d) Anpassad - Du ändrade behörighetsnivå för %1$s. %1$s ändrade behörighetsnivå för %2$s. %1$s från %2$s till %3$s - ** Kan inte avkryptera: %s ** Avsändarens enhet har inte gett oss nycklarna för det här meddelandet. - Kunde inte dölja Kunde inte skicka meddelandet - Misslyckades att ladda upp bilden - Nätverksfel Matrixfel - Det går för närvarande inte att gå med i ett tomt rum igen. - Krypterat meddelande - E-postadress Telefonnummer - Inbjudan från %s Rumsinbjudan - %1$s och %2$s - %1$s och en till %1$s och %2$d till - Tomt rum - Inledande synk: \nImporterar konto… Inledande synk: @@ -151,10 +133,8 @@ \nImporterar gemenskaper Inledande synk: \nImporterar kontodata - Skickar meddelande… Rensa sändningskö - Inbjudan från %1$s. Anledning: %2$s Inbjudan från dig. Anledning: %1$s %1$s bjöd in %2$s. Anledning: %3$s @@ -180,45 +160,57 @@ Du accepterade inbjudan för %1$s. Anledning: %2$s %1$s drog tillbaka inbjudan för %2$s. Anledning: %3$s Du drog tillbaka inbjudan för %1$s. Anledning: %2$s - %1$s lade till %2$s som en adress för det här rummet. %1$s lade till %2$s som adresser för det här rummet. - Du lade till %1$s som en adress för det här rummet. Du lade till %1$s som adresser för det här rummet. - %1$s tog bort %2$s som en adress för det här rummet. %1$s tog bort %2$s som adresser för det här rummet. - Du tog bort %1$s som en adress för det här rummet. Du tog bort %2$s som adresser för det här rummet. - %1$s lade till %2$s och tog bort %3$s som adresser för det här rummet. Du lade till %1$s och tog bort %2$s som adresser för det här rummet. - %1$s satta huvudadressen för det här rummet till %2$s. Du satta huvudadressen för det här rummet till %1$s. %1$s tog bort huvudadressen för det här rummet. Du tog bort huvudadressen för det här rummet. - %1$s tillät gäster att gå med i rummet. Du tillät gäster att gå med i rummet. %1$s hindrade gäster från att gå med i rummet. Du hindrade gäster från att gå med i rummet. - %1$s aktiverade totalsträckskryptering. Du aktiverade totalsträckskryptering. %1$s aktiverade totalsträckskryptering (okänd algoritm %2$s). Du aktiverade totalsträckskryptering (okänd algoritm %1$s). - %s begär att verifiera din nyckel, men din klient stöder inte nyckelverifiering i chatten. Du behöver använda legacynyckelverifiering för att verifiera nycklar. - - + Du hindrade gäster från att gå med i rummet. + %1$s hindrade gäster från att gå med i rummet. + Du tillät gäster att gå med här. + %1$s tillät gäster att gå med här. + Du lämnade. Anledning: %1$s + %1$s Lämnade. Anledning: %2$s + Du gick med. Anledning: %1$s + %1$s gick med. Anledning: %2$s + Du drog tillbaka inbjudan för %1$s + %1$s drog tillbaka inbjudan för %2$s + Du bjöd in %1$s + %1$s bjöd in %2$s + Du uppgraderade här. + %s uppgraderade här. + Du gjorde framtida meddelanden synliga för %1$s + %1$s gjorde framtida meddelanden synliga för %2$s + Du lämnade rummet + %1$s lämnade rummet + Du gick med + %1$s gick med + Du skapade diskussionen + %1$s skapade diskussionen + \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-zh-rCN/strings.xml b/matrix-sdk-android/src/main/res/values-zh-rCN/strings.xml index 60322821d4..496bbe6bf8 100644 --- a/matrix-sdk-android/src/main/res/values-zh-rCN/strings.xml +++ b/matrix-sdk-android/src/main/res/values-zh-rCN/strings.xml @@ -1,7 +1,6 @@ - + %1$s 发送了一张图片。 - %s 的邀请 %1$s 邀请了 %2$s %1$s 邀请了您 @@ -27,42 +26,30 @@ 任何人。 未知(%s)。 %1$s 开启了端到端加密(%2$s) - %1$s 请求了一次 VoIP 会议 VoIP 会议已开始 VoIP 会议已结束 - (头像也被更改) %1$s 移除了聊天室名称 %1$s 移除了聊天室主题 ** 无法解密:%s ** 发送者的设备没有向我们发送此消息的密钥。 - 无法发送消息 - 上传图像失败 - 网络错误 Matrix 错误 - 目前无法重新加入一个空的聊天室。 - 已加密消息 - 电子邮箱地址 手机号码 - %1$s 撤回了对 %2$s 的邀请 %1$s 让未来的聊天室历史记录对 %2$s 可见 %1$s 更新了他的个人档案 %2$s %1$s 向 %2$s 发送了加入聊天室的邀请 %1$s 接受了 %2$s 的邀请 - 无法撤回 - %1$s:%2$s %1$s 发送了一张贴纸。 - 空聊天室 来自 %s 的邀请 聊天室邀请 @@ -70,7 +57,6 @@ %1$s 与其他 %2$d 位 - 消息已被移除 消息已被 %1$s 移除 消息已被移除 [原因: %1$s] @@ -91,14 +77,10 @@ \n正在导入社区 初始化同步: \n正在导入账号数据 - %s 升级了此聊天室。 - 正在发送消息… 清除正在发送队列 - %1$s 撤回了对 %2$s 加入聊天室的邀请 - %1$s 的邀请。理由:%2$s %1$s 邀请了 %2$s。理由:%3$s %1$s 邀请了您。理由:%2$s @@ -112,32 +94,23 @@ %1$s 撤销了 %2$s 加入聊天室的邀請。理由:%3$s %1$s 接受 %2$s 的邀請。理由:%3$s %1$s 撤回了对 %2$s 的邀请。理由:%3$s - %1$s 新增了 %2$s 为此聊天室的地址。 - %1$s 移除了此聊天室的 %3$s 地址。 - %1$s 为此聊天室新增了 %2$s 并移除 %3$s 地址。 - %1$s 将此聊天室的主地址设为了 %2$s。 %1$s 为此聊天室移除了主地址。 - %1$s 已允许访客加入聊天室。 %1$s 已禁止访客加入聊天室。 - %1$s 已开启端到端加密。 %1$s 已开启端到端加密(无法识别的演算法 %2$s)。 - %s 正在请求验证您的密钥,但您的客户端不支援聊天中密钥验证。 您将必须使用旧版的密钥验证来验证金钥。 - %1$s 创建了这个聊天室 您发送了一张图片。 您发送了一张贴纸。 - 您的邀请 您创建了这个聊天室 您邀请了 %1$s @@ -165,7 +138,6 @@ 您已让未来的聊天室记录对 %1$s 可见 您开启了端到端加密(%1$s) 您升级了此聊天室。 - 您请求了 VoIP 会议 您移除了聊天室名称 您移除了聊天室主题 @@ -175,24 +147,20 @@ 您向 %1$s 发送了加入聊天室的邀请 您已撤回了对 %1$s 加入聊天室的邀请 您接受了 %1$s 的邀请 - %1$s 添加了 %2$s 小部件 您添加了 %1$s 小部件 %1$s 移除了 %2$s 小部件 您移除了 %1$s 小部件 %1$s 修改了 %2$s 小部件 您修改了 %1$s 小部件 - 管理员 审核员 默认 自定义(%1$d) 自定义 - 您更改了%1$s 的权力等级。 %1$s 更改了 %2$s 的权力等级。 %1$s 从 %2$s 到 %3$s - 您的邀请。理由:%1$s 您邀请了 %1$s。理由:%2$s 您加入了聊天室。理由:%1$s @@ -205,24 +173,35 @@ 您撤销了 %1$s 加入聊天室的邀请。理由:%2$s 您接受了 %1$s 的邀请。理由:%2$s 您撤回了 %1$s 的邀请。理由:%2$s - 您新增了 %1$s 为此聊天室的地址。 - 您移除了此聊天室的 %2$s 地址。 - 您为此聊天室新增了 %1$s 并移除了 %2$s 地址。 - 您将此聊天室的主地址设为了 %1$s。 您移除了此聊天室的主地址。 - 您已允许访客加入聊天室。 您已禁止访客加入聊天室。 - 您已开启端到端加密。 您已开启端到端加密(无法识别的算法 %1$s)。 - - + 您已离开。理由:%1$s + %1$s 已离开。理由:%2$s + 您已加入。理由:%1$s + %1$s 已加入。理由:%2$s + 您撤回了对 %1$s 的邀请 + %1$s 撤回了对 %2$s 的邀请 + 您邀请了 %1$s + %1$s 邀请了 %2$s + 您在此处升级。 + %s 在此处升级。 + 您使未来的消息对 %2$s 可见 + %1$s 使未来的消息对 %2$s 可见 + 您离开了聊天室 + %1$s 离开了聊天室 + 您已加入 + %1$s 已加入 + 您创建了讨论 + %1$s 创建了讨论 + \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values-zh-rTW/strings.xml b/matrix-sdk-android/src/main/res/values-zh-rTW/strings.xml index 355890923c..4a3293b195 100644 --- a/matrix-sdk-android/src/main/res/values-zh-rTW/strings.xml +++ b/matrix-sdk-android/src/main/res/values-zh-rTW/strings.xml @@ -1,8 +1,7 @@ - + %1$s:%2$s %1$s 傳送了一張圖片。 - %s 的邀請 %1$s 邀請了 %2$s %1$s 邀請您 @@ -30,52 +29,38 @@ 任何人。 未知 (%s)。 %1$s 開啟了端對端加密 (%2$s) - %1$s 請求了 VoIP 會議通話 VoIP 會議通話已開始 VoIP 會議通話已結束 - (大頭貼也變更了) %1$s 移除了房間名稱 %1$s 移除了房間主題 %1$s 更新了他們的基本資料 %2$s %1$s 傳送加入房間的邀請給 %2$s %1$s 接受 %2$s 的邀請 - ** 無法解密:%s ** 傳送者的裝置並未在此訊息傳送他們的金鑰。 - 無法編輯 無法傳送訊息 - 上傳圖片失敗 - 網路錯誤 Matrix 錯誤 - 目前無法重新加入空房間。 - 已加密的訊息 - 電子郵件 電話號碼 - %1$s 傳送了一張貼圖。 - 來自%s 的邀請 聊天室邀請 %1$s 和 %2$s - 空聊天室 %1$s 和 和其他 %2$d 個人 - 訊息已移除 訊息已被 %1$s 移除 訊息已移除 [理由:%1$s] 訊息已被 %1$s 移除 [理由:%2$s] - 初始化同步: \n正在匯入帳號…… 初始化同步: @@ -92,12 +77,9 @@ \n正在匯入社群 初始化同步: \n正在匯入帳號資料 - %s 已升級此聊天室。 - 正在傳送訊息…… 清除傳送佇列 - %1$s 撤銷了 %2$s 加入聊天室的邀請 %1$s 的邀請。理由:%2$s %1$s 邀請了 %2$s。理由:%3$s @@ -112,32 +94,23 @@ %1$s 撤銷了 %2$s 加入聊天室的邀請。理由:%3$s %1$s 接受 %2$s 的邀請。理由:%3$s %1$s 撤回了對 %2$s 的邀請。理由:%3$s - %1$s 新增了 %2$s 為此聊天室的地址。 - %1$s 移除了此聊天室的 %3$s 地址。 - %1$s 為此聊天室新增 %2$s 並移除 %3$s 地址。 - %1$s 為此聊天室設定了 %2$s 為主地址。 %1$s 為此聊天室移除了主要地址。 - %1$s 已允許訪客加入聊天室。 %1$s 已禁止訪客加入聊天室。 - %1$s 已開啟端到端加密。 %1$s 已開啟端到端加密(無法識別的演算法 %2$s)。 - %s 正在請求驗證您的金鑰,但您的客戶端不支援聊天中金鑰驗證。您將必須使用舊版的金鑰驗證來驗證金鑰。 - %1$s 建立了聊天室 您傳送了圖片。 您傳送了貼圖。 - 您的邀請 您建立了聊天室 您邀請了 %1$s @@ -165,7 +138,6 @@ 您已將未來的聊天室歷史設定為對 %1$s 可見 您開啟了端到端加密 (%1$s) 您升級了此聊天室。 - 您請求了 VoIP 會議 您移除了聊天室名稱 您移除了聊天室主題 @@ -175,24 +147,20 @@ 您傳送了邀請給 %1$s 以加入聊天室 您已撤銷對 %1$s 加入聊天室的邀請 您接受了 %1$s 的邀請 - %1$s 新增了 %2$s 小工具 您新增了 %1$s 小工具 %1$s 移除了 %2$s 小工具 您移除了 %1$s 小工具 %1$s 修改了 %2$s 小工具 您修改了 %1$s 小工具 - 管理員 板主 預設 自訂 (%1$d) 自訂 - 您變更了 %1$s 的權力等級。 %1$s 變更了 %2$s 的權力等級。 %1$s 從 %2$s 到 %3$s - 您的邀請。理由:%1$s 您邀請了 %1$s。理由:%2$s 您加入了聊天室。理由:%1$s @@ -205,24 +173,39 @@ 您撤銷了 %1$s 加入聊天室的邀請。理由:%2$s 您接受了 %1$s 的邀請。理由:%2$s 您撤回了 %1$s 的邀請。理由:%2$s - 您為此聊天室新增了 %1$s 作為地址。 - 您為此聊天室移除了 %2$s 作為地址。 - 您為此聊天室新增了 %1$s 並移除了 %2$s 作為地址。 - 您將此聊天室的主要地址設定為 %1$s。 您將此聊天室的主要地址移除。 - 您已允許訪客加入聊天室。 您已阻止訪客加入聊天室。 - 您開啟了端到端加密。 您開啟了端到端加密(無法識別的演算法 %1$s)。 - - + 您已避免訪客加入此聊天室。 + %1$s 已避免訪客加入此聊天室。 + 您已允許訪客加入這裡。 + %1$s 已允許訪客加入這裡。 + 您已離開。理由:%1$s + %1$s 已離開。理由:%2$s + 您已加入。理由:%1$s + %1$s 已加入。理由:%2$s + 您已撤銷對 %1$s 的邀請 + %1$s 已撤銷對 %2$s 的邀請 + 您已邀請了 %1$s + %1$s 邀請了 %2$s + 您已在此升級。 + %s 已在此升級。 + 您讓未來的訊息對 %1$s 可見 + %1$s 已讓未來的訊息對 %2$s 可見 + 您已離開聊天室 + %1$s 已離開聊天室 + 您已加入 + %1$s 已加入 + 您已建立此討論 + %1$s 已建立此討論 + \ No newline at end of file diff --git a/matrix-sdk-android/src/main/res/values/strings.xml b/matrix-sdk-android/src/main/res/values/strings.xml index f64ec9926e..3f75b715f6 100644 --- a/matrix-sdk-android/src/main/res/values/strings.xml +++ b/matrix-sdk-android/src/main/res/values/strings.xml @@ -10,13 +10,19 @@ Your invitation %1$s created the room You created the room + %1$s created the discussion + You created the discussion %1$s invited %2$s You invited %1$s %1$s invited you %1$s joined the room You joined the room + %1$s joined + You joined %1$s left the room You left the room + %1$s left the room + You left the room %1$s rejected the invitation You rejected the invitation %1$s kicked %2$s @@ -53,6 +59,8 @@ You ended the call. %1$s made future room history visible to %2$s You made future room history visible to %1$s + %1$s made future messages visible to %2$s + You made future messages visible to %1$s all room members, from the point they are invited. all room members, from the point they joined. all room members. @@ -62,6 +70,8 @@ You turned on end-to-end encryption (%1$s) %s upgraded this room. You upgraded this room. + %s upgraded here. + You upgraded here. %1$s requested a VoIP conference You requested a VoIP conference @@ -83,8 +93,12 @@ You updated your profile %1$s %1$s sent an invitation to %2$s to join the room You sent an invitation to %1$s to join the room + %1$s invited %2$s + You invited %1$s %1$s revoked the invitation for %2$s to join the room You revoked the invitation for %1$s to join the room + %1$s revoked the invitation for %2$s + You revoked the invitation for %1$s %1$s accepted the invitation for %2$s You accepted the invitation for %1$s @@ -171,8 +185,12 @@ %1$s invited you. Reason: %2$s %1$s joined the room. Reason: %2$s You joined the room. Reason: %1$s + %1$s joined. Reason: %2$s + You joined. Reason: %1$s %1$s left the room. Reason: %2$s You left the room. Reason: %1$s + %1$s left. Reason: %2$s + You left. Reason: %1$s %1$s rejected the invitation. Reason: %2$s You rejected the invitation. Reason: %1$s %1$s kicked %2$s. Reason: %3$s @@ -220,8 +238,12 @@ "%1$s has allowed guests to join the room." "You have allowed guests to join the room." + "%1$s has allowed guests to join here." + "You have allowed guests to join here." "%1$s has prevented guests from joining the room." "You have prevented guests from joining the room." + "%1$s has prevented guests from joining the room." + "You have prevented guests from joining the room." %1$s turned on end-to-end encryption. You turned on end-to-end encryption. diff --git a/matrix-sdk-android/src/release/java/org/matrix/android/sdk/internal/network/interceptors/CurlLoggingInterceptor.kt b/matrix-sdk-android/src/release/java/org/matrix/android/sdk/internal/network/interceptors/CurlLoggingInterceptor.kt index c4fed36216..f46e2ca35b 100644 --- a/matrix-sdk-android/src/release/java/org/matrix/android/sdk/internal/network/interceptors/CurlLoggingInterceptor.kt +++ b/matrix-sdk-android/src/release/java/org/matrix/android/sdk/internal/network/interceptors/CurlLoggingInterceptor.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/release/java/org/matrix/android/sdk/internal/network/interceptors/FormattedJsonHttpLogger.kt b/matrix-sdk-android/src/release/java/org/matrix/android/sdk/internal/network/interceptors/FormattedJsonHttpLogger.kt index 69b15a1fa5..a815cec353 100644 --- a/matrix-sdk-android/src/release/java/org/matrix/android/sdk/internal/network/interceptors/FormattedJsonHttpLogger.kt +++ b/matrix-sdk-android/src/release/java/org/matrix/android/sdk/internal/network/interceptors/FormattedJsonHttpLogger.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/sharedTest/java/org/matrix/android/sdk/test/shared/TestRules.kt b/matrix-sdk-android/src/sharedTest/java/org/matrix/android/sdk/test/shared/TestRules.kt index 52aa7ea0c7..662656ae5e 100644 --- a/matrix-sdk-android/src/sharedTest/java/org/matrix/android/sdk/test/shared/TestRules.kt +++ b/matrix-sdk-android/src/sharedTest/java/org/matrix/android/sdk/test/shared/TestRules.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/MatrixTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/MatrixTest.kt index b0933c7106..f6a7f525db 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/MatrixTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/MatrixTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/api/auth/data/VersionsKtTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/api/auth/data/VersionsKtTest.kt index 69e2f12eb7..c413d9ccae 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/api/auth/data/VersionsKtTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/api/auth/data/VersionsKtTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/api/pushrules/PushRuleActionsTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/api/pushrules/PushRuleActionsTest.kt index f213e1b1c1..b734444990 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/api/pushrules/PushRuleActionsTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/api/pushrules/PushRuleActionsTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/api/pushrules/PushRulesConditionTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/api/pushrules/PushRulesConditionTest.kt index b2c8d3bda8..c0b869a90e 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/api/pushrules/PushRulesConditionTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/api/pushrules/PushRulesConditionTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/keysbackup/util/Base58Test.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/keysbackup/util/Base58Test.kt index b2d10968b6..2f01a43a67 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/keysbackup/util/Base58Test.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/keysbackup/util/Base58Test.kt @@ -1,5 +1,5 @@ /* - * Copyright 2018 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/keysbackup/util/RecoveryKeyTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/keysbackup/util/RecoveryKeyTest.kt index 6b9d388623..64ffe52acd 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/keysbackup/util/RecoveryKeyTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/keysbackup/util/RecoveryKeyTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2018 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/store/db/HelperTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/store/db/HelperTest.kt index cac2d1cba9..0bcc7983c5 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/store/db/HelperTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/store/db/HelperTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2018 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/BinaryStringTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/BinaryStringTest.kt index 0f8fe58b7f..b04834f9f4 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/BinaryStringTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/BinaryStringTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,13 +17,13 @@ package org.matrix.android.sdk.internal.crypto.verification.qrcode import org.matrix.android.sdk.MatrixTest -import org.amshove.kluent.shouldEqualTo +import org.amshove.kluent.shouldBeEqualTo import org.junit.FixMethodOrder import org.junit.Test import org.junit.runners.MethodSorters @FixMethodOrder(MethodSorters.JVM) -class BinaryStringTest: MatrixTest { +class BinaryStringTest : MatrixTest { /** * I want to put bytes to a String, and vice versa @@ -37,17 +37,17 @@ class BinaryStringTest: MatrixTest { val str = byteArray.toString(Charsets.ISO_8859_1) - str.length shouldEqualTo 256 + str.length shouldBeEqualTo 256 // Ok convert back to bytearray val result = str.toByteArray(Charsets.ISO_8859_1) - result.size shouldEqualTo 256 + result.size shouldBeEqualTo 256 for (i in 0..255) { - result[i] shouldEqualTo i.toByte() - result[i] shouldEqualTo byteArray[i] + result[i] shouldBeEqualTo i.toByte() + result[i] shouldBeEqualTo byteArray[i] } } } diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/task/CoroutineSequencersTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/task/CoroutineSequencersTest.kt index 7bef439417..667e0b2471 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/task/CoroutineSequencersTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/task/CoroutineSequencersTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/multipicker/build.gradle b/multipicker/build.gradle index 8f2226e884..b6e500e493 100644 --- a/multipicker/build.gradle +++ b/multipicker/build.gradle @@ -41,15 +41,10 @@ android { } dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'androidx.appcompat:appcompat:1.1.0' - implementation 'androidx.core:core-ktx:1.3.0' - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test.ext:junit:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' - - implementation 'androidx.exifinterface:exifinterface:1.3.0-alpha01' + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation "androidx.fragment:fragment:1.3.0-beta01" + implementation 'androidx.exifinterface:exifinterface:1.3.0' // Log implementation 'com.jakewharton.timber:timber:4.7.1' diff --git a/multipicker/src/main/java/im/vector/lib/multipicker/AudioPicker.kt b/multipicker/src/main/java/im/vector/lib/multipicker/AudioPicker.kt index 17c01bc8e3..516022100d 100644 --- a/multipicker/src/main/java/im/vector/lib/multipicker/AudioPicker.kt +++ b/multipicker/src/main/java/im/vector/lib/multipicker/AudioPicker.kt @@ -16,7 +16,6 @@ package im.vector.lib.multipicker -import android.app.Activity import android.content.Context import android.content.Intent import android.media.MediaMetadataRetriever @@ -26,19 +25,13 @@ import im.vector.lib.multipicker.entity.MultiPickerAudioType /** * Audio file picker implementation */ -class AudioPicker(override val requestCode: Int) : Picker(requestCode) { +class AudioPicker : Picker() { /** * Call this function from onActivityResult(int, int, Intent). - * Returns selected audio files or empty list if request code is wrong - * or result code is not Activity.RESULT_OK - * or user did not select any files. + * Returns selected audio files or empty list if user did not select any files. */ - override fun getSelectedFiles(context: Context, requestCode: Int, resultCode: Int, data: Intent?): List { - if (requestCode != this.requestCode && resultCode != Activity.RESULT_OK) { - return emptyList() - } - + override fun getSelectedFiles(context: Context, data: Intent?): List { val audioList = mutableListOf() getSelectedUriList(data).forEach { selectedUri -> @@ -84,7 +77,7 @@ class AudioPicker(override val requestCode: Int) : Picker( } override fun createIntent(): Intent { - return Intent(Intent.ACTION_OPEN_DOCUMENT).apply { + return Intent(Intent.ACTION_GET_CONTENT).apply { addCategory(Intent.CATEGORY_OPENABLE) putExtra(Intent.EXTRA_ALLOW_MULTIPLE, !single) type = "audio/*" diff --git a/multipicker/src/main/java/im/vector/lib/multipicker/CameraPicker.kt b/multipicker/src/main/java/im/vector/lib/multipicker/CameraPicker.kt index be6fdb5ee8..3f24a28c28 100644 --- a/multipicker/src/main/java/im/vector/lib/multipicker/CameraPicker.kt +++ b/multipicker/src/main/java/im/vector/lib/multipicker/CameraPicker.kt @@ -16,13 +16,12 @@ package im.vector.lib.multipicker -import android.app.Activity import android.content.Context import android.content.Intent import android.net.Uri import android.provider.MediaStore +import androidx.activity.result.ActivityResultLauncher import androidx.core.content.FileProvider -import androidx.fragment.app.Fragment import im.vector.lib.multipicker.entity.MultiPickerImageType import im.vector.lib.multipicker.utils.ImageUtils import java.io.File @@ -33,33 +32,18 @@ import java.util.Locale /** * Implementation of taking a photo with Camera */ -class CameraPicker(val requestCode: Int) { +class CameraPicker { /** - * Start camera by using an Activity - * @param activity Activity to handle onActivityResult(). + * Start camera by using a ActivityResultLauncher * @return Uri of taken photo or null if the operation is cancelled. */ - fun startWithExpectingFile(activity: Activity): Uri? { - val photoUri = createPhotoUri(activity) + fun startWithExpectingFile(context: Context, activityResultLauncher: ActivityResultLauncher): Uri? { + val photoUri = createPhotoUri(context) val intent = createIntent().apply { putExtra(MediaStore.EXTRA_OUTPUT, photoUri) } - activity.startActivityForResult(intent, requestCode) - return photoUri - } - - /** - * Start camera by using a Fragment - * @param fragment Fragment to handle onActivityResult(). - * @return Uri of taken photo or null if the operation is cancelled. - */ - fun startWithExpectingFile(fragment: Fragment): Uri? { - val photoUri = createPhotoUri(fragment.requireContext()) - val intent = createIntent().apply { - putExtra(MediaStore.EXTRA_OUTPUT, photoUri) - } - fragment.startActivityForResult(intent, requestCode) + activityResultLauncher.launch(intent) return photoUri } @@ -69,40 +53,38 @@ class CameraPicker(val requestCode: Int) { * or result code is not Activity.RESULT_OK * or user cancelled the operation. */ - fun getTakenPhoto(context: Context, requestCode: Int, resultCode: Int, photoUri: Uri): MultiPickerImageType? { - if (requestCode == this.requestCode && resultCode == Activity.RESULT_OK) { - val projection = arrayOf( - MediaStore.Images.Media.DISPLAY_NAME, - MediaStore.Images.Media.SIZE - ) + fun getTakenPhoto(context: Context, photoUri: Uri): MultiPickerImageType? { + val projection = arrayOf( + MediaStore.Images.Media.DISPLAY_NAME, + MediaStore.Images.Media.SIZE + ) - context.contentResolver.query( - photoUri, - projection, - null, - null, - null - )?.use { cursor -> - val nameColumn = cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME) - val sizeColumn = cursor.getColumnIndex(MediaStore.Images.Media.SIZE) + context.contentResolver.query( + photoUri, + projection, + null, + null, + null + )?.use { cursor -> + val nameColumn = cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME) + val sizeColumn = cursor.getColumnIndex(MediaStore.Images.Media.SIZE) - if (cursor.moveToNext()) { - val name = cursor.getString(nameColumn) - val size = cursor.getLong(sizeColumn) + if (cursor.moveToNext()) { + val name = cursor.getString(nameColumn) + val size = cursor.getLong(sizeColumn) - val bitmap = ImageUtils.getBitmap(context, photoUri) - val orientation = ImageUtils.getOrientation(context, photoUri) + val bitmap = ImageUtils.getBitmap(context, photoUri) + val orientation = ImageUtils.getOrientation(context, photoUri) - return MultiPickerImageType( - name, - size, - context.contentResolver.getType(photoUri), - photoUri, - bitmap?.width ?: 0, - bitmap?.height ?: 0, - orientation - ) - } + return MultiPickerImageType( + name, + size, + context.contentResolver.getType(photoUri), + photoUri, + bitmap?.width ?: 0, + bitmap?.height ?: 0, + orientation + ) } } return null diff --git a/multipicker/src/main/java/im/vector/lib/multipicker/ContactPicker.kt b/multipicker/src/main/java/im/vector/lib/multipicker/ContactPicker.kt index e9ae096174..315fe6cbf2 100644 --- a/multipicker/src/main/java/im/vector/lib/multipicker/ContactPicker.kt +++ b/multipicker/src/main/java/im/vector/lib/multipicker/ContactPicker.kt @@ -16,7 +16,6 @@ package im.vector.lib.multipicker -import android.app.Activity import android.content.ContentResolver import android.content.Context import android.content.Intent @@ -26,19 +25,13 @@ import im.vector.lib.multipicker.entity.MultiPickerContactType /** * Contact Picker implementation */ -class ContactPicker(override val requestCode: Int) : Picker(requestCode) { +class ContactPicker : Picker() { /** * Call this function from onActivityResult(int, int, Intent). - * Returns selected contact or empty list if request code is wrong - * or result code is not Activity.RESULT_OK - * or user did not select any files. + * Returns selected contact or empty list if user did not select any contacts. */ - override fun getSelectedFiles(context: Context, requestCode: Int, resultCode: Int, data: Intent?): List { - if (requestCode != this.requestCode && resultCode != Activity.RESULT_OK) { - return emptyList() - } - + override fun getSelectedFiles(context: Context, data: Intent?): List { val contactList = mutableListOf() data?.data?.let { selectedUri -> diff --git a/multipicker/src/main/java/im/vector/lib/multipicker/FilePicker.kt b/multipicker/src/main/java/im/vector/lib/multipicker/FilePicker.kt index 296685886d..39bd93d03e 100644 --- a/multipicker/src/main/java/im/vector/lib/multipicker/FilePicker.kt +++ b/multipicker/src/main/java/im/vector/lib/multipicker/FilePicker.kt @@ -16,7 +16,6 @@ package im.vector.lib.multipicker -import android.app.Activity import android.content.Context import android.content.Intent import android.provider.OpenableColumns @@ -25,19 +24,13 @@ import im.vector.lib.multipicker.entity.MultiPickerFileType /** * Implementation of selecting any type of files */ -class FilePicker(override val requestCode: Int) : Picker(requestCode) { +class FilePicker : Picker() { /** * Call this function from onActivityResult(int, int, Intent). - * Returns selected files or empty list if request code is wrong - * or result code is not Activity.RESULT_OK - * or user did not select any files. + * Returns selected files or empty list if user did not select any files. */ - override fun getSelectedFiles(context: Context, requestCode: Int, resultCode: Int, data: Intent?): List { - if (requestCode != this.requestCode && resultCode != Activity.RESULT_OK) { - return emptyList() - } - + override fun getSelectedFiles(context: Context, data: Intent?): List { val fileList = mutableListOf() getSelectedUriList(data).forEach { selectedUri -> @@ -64,7 +57,7 @@ class FilePicker(override val requestCode: Int) : Picker(re } override fun createIntent(): Intent { - return Intent(Intent.ACTION_OPEN_DOCUMENT).apply { + return Intent(Intent.ACTION_GET_CONTENT).apply { addCategory(Intent.CATEGORY_OPENABLE) putExtra(Intent.EXTRA_ALLOW_MULTIPLE, !single) type = "*/*" diff --git a/multipicker/src/main/java/im/vector/lib/multipicker/ImagePicker.kt b/multipicker/src/main/java/im/vector/lib/multipicker/ImagePicker.kt index 39bd0c27cc..ce73058039 100644 --- a/multipicker/src/main/java/im/vector/lib/multipicker/ImagePicker.kt +++ b/multipicker/src/main/java/im/vector/lib/multipicker/ImagePicker.kt @@ -16,7 +16,6 @@ package im.vector.lib.multipicker -import android.app.Activity import android.content.Context import android.content.Intent import android.provider.MediaStore @@ -26,19 +25,13 @@ import im.vector.lib.multipicker.utils.ImageUtils /** * Image Picker implementation */ -class ImagePicker(override val requestCode: Int) : Picker(requestCode) { +class ImagePicker : Picker() { /** * Call this function from onActivityResult(int, int, Intent). - * Returns selected image files or empty list if request code is wrong - * or result code is not Activity.RESULT_OK - * or user did not select any files. + * Returns selected image files or empty list if user did not select any files. */ - override fun getSelectedFiles(context: Context, requestCode: Int, resultCode: Int, data: Intent?): List { - if (requestCode != this.requestCode && resultCode != Activity.RESULT_OK) { - return emptyList() - } - + override fun getSelectedFiles(context: Context, data: Intent?): List { val imageList = mutableListOf() getSelectedUriList(data).forEach { selectedUri -> @@ -82,7 +75,7 @@ class ImagePicker(override val requestCode: Int) : Picker( } override fun createIntent(): Intent { - return Intent(Intent.ACTION_OPEN_DOCUMENT).apply { + return Intent(Intent.ACTION_GET_CONTENT).apply { addCategory(Intent.CATEGORY_OPENABLE) putExtra(Intent.EXTRA_ALLOW_MULTIPLE, !single) type = "image/*" diff --git a/multipicker/src/main/java/im/vector/lib/multipicker/MultiPicker.kt b/multipicker/src/main/java/im/vector/lib/multipicker/MultiPicker.kt index d28dcf9586..7e639a9bd3 100644 --- a/multipicker/src/main/java/im/vector/lib/multipicker/MultiPicker.kt +++ b/multipicker/src/main/java/im/vector/lib/multipicker/MultiPicker.kt @@ -26,23 +26,16 @@ class MultiPicker { val CONTACT by lazy { MultiPicker() } val CAMERA by lazy { MultiPicker() } - const val REQUEST_CODE_PICK_IMAGE = 5000 - const val REQUEST_CODE_PICK_VIDEO = 5001 - const val REQUEST_CODE_PICK_FILE = 5002 - const val REQUEST_CODE_PICK_AUDIO = 5003 - const val REQUEST_CODE_PICK_CONTACT = 5004 - const val REQUEST_CODE_TAKE_PHOTO = 5005 - @Suppress("UNCHECKED_CAST") fun get(type: MultiPicker): T { return when (type) { - IMAGE -> ImagePicker(REQUEST_CODE_PICK_IMAGE) as T - VIDEO -> VideoPicker(REQUEST_CODE_PICK_VIDEO) as T - FILE -> FilePicker(REQUEST_CODE_PICK_FILE) as T - AUDIO -> AudioPicker(REQUEST_CODE_PICK_AUDIO) as T - CONTACT -> ContactPicker(REQUEST_CODE_PICK_CONTACT) as T - CAMERA -> CameraPicker(REQUEST_CODE_TAKE_PHOTO) as T - else -> throw IllegalArgumentException("Unsupported type $type") + IMAGE -> ImagePicker() as T + VIDEO -> VideoPicker() as T + FILE -> FilePicker() as T + AUDIO -> AudioPicker() as T + CONTACT -> ContactPicker() as T + CAMERA -> CameraPicker() as T + else -> throw IllegalArgumentException("Unsupported type $type") } } } diff --git a/multipicker/src/main/java/im/vector/lib/multipicker/Picker.kt b/multipicker/src/main/java/im/vector/lib/multipicker/Picker.kt index 65ec77e02a..ba765a3b1d 100644 --- a/multipicker/src/main/java/im/vector/lib/multipicker/Picker.kt +++ b/multipicker/src/main/java/im/vector/lib/multipicker/Picker.kt @@ -16,28 +16,25 @@ package im.vector.lib.multipicker -import android.app.Activity import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.content.pm.ResolveInfo import android.net.Uri -import androidx.fragment.app.Fragment +import androidx.activity.result.ActivityResultLauncher /** * Abstract class to provide all types of Pickers */ -abstract class Picker(open val requestCode: Int) { +abstract class Picker { protected var single = false /** * Call this function from onActivityResult(int, int, Intent). - * @return selected files or empty list if request code is wrong - * or result code is not Activity.RESULT_OK - * or user did not select any files. + * @return selected files or empty list if user did not select any files. */ - abstract fun getSelectedFiles(context: Context, requestCode: Int, resultCode: Int, data: Intent?): List + abstract fun getSelectedFiles(context: Context, data: Intent?): List /** * Use this function to retrieve files which are shared from another application or internally @@ -61,7 +58,7 @@ abstract class Picker(open val requestCode: Int) { context.grantUriPermission(packageName, it, Intent.FLAG_GRANT_READ_URI_PERMISSION) } } - return getSelectedFiles(context, requestCode, Activity.RESULT_OK, data) + return getSelectedFiles(context, data) } /** @@ -75,19 +72,11 @@ abstract class Picker(open val requestCode: Int) { abstract fun createIntent(): Intent /** - * Start Storage Access Framework UI by using an Activity. - * @param activity Activity to handle onActivityResult(). + * Start Storage Access Framework UI by using a ActivityResultLauncher. + * @param activityResultLauncher to handle the result. */ - fun startWith(activity: Activity) { - activity.startActivityForResult(createIntent().apply { addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) }, requestCode) - } - - /** - * Start Storage Access Framework UI by using a Fragment. - * @param fragment Fragment to handle onActivityResult(). - */ - fun startWith(fragment: Fragment) { - fragment.startActivityForResult(createIntent().apply { addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) }, requestCode) + fun startWith(activityResultLauncher: ActivityResultLauncher) { + activityResultLauncher.launch(createIntent().apply { addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) }) } protected fun getSelectedUriList(data: Intent?): List { diff --git a/multipicker/src/main/java/im/vector/lib/multipicker/VideoPicker.kt b/multipicker/src/main/java/im/vector/lib/multipicker/VideoPicker.kt index 7127e9defd..c7c06f795f 100644 --- a/multipicker/src/main/java/im/vector/lib/multipicker/VideoPicker.kt +++ b/multipicker/src/main/java/im/vector/lib/multipicker/VideoPicker.kt @@ -16,7 +16,6 @@ package im.vector.lib.multipicker -import android.app.Activity import android.content.Context import android.content.Intent import android.media.MediaMetadataRetriever @@ -26,19 +25,13 @@ import im.vector.lib.multipicker.entity.MultiPickerVideoType /** * Video Picker implementation */ -class VideoPicker(override val requestCode: Int) : Picker(requestCode) { +class VideoPicker : Picker() { /** * Call this function from onActivityResult(int, int, Intent). - * Returns selected video files or empty list if request code is wrong - * or result code is not Activity.RESULT_OK - * or user did not select any files. + * Returns selected video files or empty list if user did not select any files. */ - override fun getSelectedFiles(context: Context, requestCode: Int, resultCode: Int, data: Intent?): List { - if (requestCode != this.requestCode && resultCode != Activity.RESULT_OK) { - return emptyList() - } - + override fun getSelectedFiles(context: Context, data: Intent?): List { val videoList = mutableListOf() getSelectedUriList(data).forEach { selectedUri -> @@ -93,7 +86,7 @@ class VideoPicker(override val requestCode: Int) : Picker( } override fun createIntent(): Intent { - return Intent(Intent.ACTION_OPEN_DOCUMENT).apply { + return Intent(Intent.ACTION_GET_CONTENT).apply { addCategory(Intent.CATEGORY_OPENABLE) putExtra(Intent.EXTRA_ALLOW_MULTIPLE, !single) type = "video/*" diff --git a/tools/check/check_code_quality.sh b/tools/check/check_code_quality.sh index 8f850734fc..e855440e81 100755 --- a/tools/check/check_code_quality.sh +++ b/tools/check/check_code_quality.sh @@ -75,6 +75,15 @@ ${searchForbiddenStringsScript} ./tools/check/forbidden_strings_in_code.txt \ resultForbiddenStringInCode=$? +echo +echo "Search for forbidden patterns specific for SDK code..." + +${searchForbiddenStringsScript} ./tools/check/forbidden_strings_in_code_sdk.txt \ + ./matrix-sdk-android/src \ + ./matrix-sdk-android-rx/src + +resultForbiddenStringInCodeSdk=$? + echo echo "Search for forbidden patterns in resources..." @@ -148,7 +157,7 @@ fi echo -if [[ ${resultNbOfDrawable} -eq 0 ]] && [[ ${resultForbiddenStringInCode} -eq 0 ]] && [[ ${resultForbiddenStringInResource} -eq 0 ]] && [[ ${resultLongFiles} -eq 0 ]] && [[ ${resultPngInDrawable} -eq 0 ]]; then +if [[ ${resultNbOfDrawable} -eq 0 ]] && [[ ${resultForbiddenStringInCode} -eq 0 ]] && [[ ${resultForbiddenStringInCodeSdk} -eq 0 ]] && [[ ${resultForbiddenStringInResource} -eq 0 ]] && [[ ${resultLongFiles} -eq 0 ]] && [[ ${resultPngInDrawable} -eq 0 ]]; then echo "MAIN OK" else echo "❌ MAIN ERROR" diff --git a/tools/check/forbidden_strings_in_code.txt b/tools/check/forbidden_strings_in_code.txt index 3ced7de7e2..6e879df7ab 100644 --- a/tools/check/forbidden_strings_in_code.txt +++ b/tools/check/forbidden_strings_in_code.txt @@ -163,8 +163,8 @@ Formatter\.formatShortFileSize===1 # DISABLED # android\.text\.TextUtils -### This is not a rule, but a warning: the number of "enum class" has changed. For Json classes, it is mandatory that they have `@JsonClass(generateAdapter = false)`. If it is ok, change the value in file forbidden_strings_in_code.txt -enum class===78 +### This is not a rule, but a warning: the number of "enum class" has changed. For Json classes, it is mandatory that they have `@JsonClass(generateAdapter = false)`. If the enum is not used as a Json class, change the value in file forbidden_strings_in_code.txt +enum class===82 ### Do not import temporary legacy classes import org.matrix.android.sdk.internal.legacy.riot===3 diff --git a/tools/check/forbidden_strings_in_code_sdk.txt b/tools/check/forbidden_strings_in_code_sdk.txt new file mode 100644 index 0000000000..270efef459 --- /dev/null +++ b/tools/check/forbidden_strings_in_code_sdk.txt @@ -0,0 +1,28 @@ +# +# Copyright 2020 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This file list String which are not allowed in source code. +# Use Perl regex to write forbidden strings +# Note: line cannot start with a space. Use \s instead. +# It is possible to specify an authorized number of occurrence with === suffix. Default is 0 +# Example: +# AuthorizedStringThreeTimes===3 + +# Extension:java +# Extension:kt + +### "The Matrix.org Foundation C.I.C." copyright is required on this file, and not "New Vector Ltd" +New Vector Ltd diff --git a/tools/check/forbidden_strings_in_resources.txt b/tools/check/forbidden_strings_in_resources.txt index 0bbe90b31f..6fb6b184ba 100644 --- a/tools/check/forbidden_strings_in_resources.txt +++ b/tools/check/forbidden_strings_in_resources.txt @@ -79,9 +79,5 @@ layout_constraintLeft_ ### Use im.vector.app.core.preference.VectorPreference to support multiline of the title &2 + exit 1 +fi + +# Get the command line parameters +SERVER_KEY=$1 +FCM_TOKEN=$2 + +echo +echo "Check validity of API key, InvalidRegistration error is OK" +# https://developers.google.com/cloud-messaging/http + +curl -H "Authorization: key=$SERVER_KEY" \ + -H Content-Type:"application/json" \ + -d "{\"registration_ids\":[\"ABC\"]}" \ + -s \ + https://fcm.googleapis.com/fcm/send \ + | python -m json.tool + +# should obtain something like this: +# {"multicast_id":5978845027639121780,"success":0,"failure":1,"canonical_ids":0,"results":[{"error":"InvalidRegistration"}]} + +# content of the notification +DATA='{"event_id":"$THIS_IS_A_FAKE_EVENT_ID"}' + +echo +echo +echo "Send a push, you should see success:1..." + +curl -H "Authorization: key=$SERVER_KEY" \ + -H Content-Type:"application/json" \ + -d "{ \"data\" : $DATA, \"to\":\"$FCM_TOKEN\" }" \ + -s \ + https://fcm.googleapis.com/fcm/send \ + | python -m json.tool + +echo +echo + +# should obtain something like this: +# {"multicast_id":7967233883611790812,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:1472636210339069%84ac25d9f9fd7ecd"}]} + diff --git a/vector/build.gradle b/vector/build.gradle index 5d14065479..754f61b3e9 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -17,7 +17,7 @@ androidExtensions { // Note: 2 digits max for each value ext.versionMajor = 1 ext.versionMinor = 0 -ext.versionPatch = 8 +ext.versionPatch = 9 ext.scVersion = 23 @@ -176,6 +176,19 @@ android { } } */ + + // The following argument makes the Android Test Orchestrator run its + // "pm clear" command after each test invocation. This command ensures + // that the app's state is completely cleared between tests. + testInstrumentationRunnerArguments clearPackageData: 'true' + } + + testOptions { + // Disables animations during instrumented tests you run from the command line… + // This property does not affect tests that you run using Android Studio.” + animationsDisabled = true + + execution 'ANDROIDX_TEST_ORCHESTRATOR' } signingConfigs { @@ -271,20 +284,24 @@ android { dependencies { - def epoxy_version = '3.11.0' - def fragment_version = '1.2.5' + def epoxy_version = '4.1.0' + def fragment_version = '1.3.0-beta01' def arrow_version = "0.8.2" - def coroutines_version = "1.3.8" def markwon_version = '4.1.2' def big_image_viewer_version = '1.6.2' def glide_version = '4.11.0' - def moshi_version = '1.8.0' - def daggerVersion = '2.25.4' + def moshi_version = '1.11.0' + def daggerVersion = '2.29.1' def autofill_version = "1.0.0" def work_version = '2.4.0' def arch_version = '2.1.0' def lifecycle_version = '2.2.0' + // Tests + def kluent_version = '1.61' + def androidxTest_version = '1.3.0' + def espresso_version = '3.3.0' + implementation project(":matrix-sdk-android") implementation project(":matrix-sdk-android-rx") implementation project(":diff-match-patch") @@ -293,16 +310,16 @@ dependencies { implementation 'com.android.support:multidex:1.0.3' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" - implementation "androidx.recyclerview:recyclerview:1.2.0-alpha05" + implementation "androidx.recyclerview:recyclerview:1.2.0-alpha06" implementation 'androidx.appcompat:appcompat:1.2.0' implementation "androidx.fragment:fragment:$fragment_version" implementation "androidx.fragment:fragment-ktx:$fragment_version" - // Keep at 2.0.0-beta4 at the moment, as updating is breaking some UI - implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta4' - implementation 'androidx.core:core-ktx:1.3.1' + implementation 'androidx.constraintlayout:constraintlayout:2.0.2' + implementation "androidx.sharetarget:sharetarget:1.0.0" + implementation 'androidx.core:core-ktx:1.3.2' implementation "org.threeten:threetenbp:1.4.0:no-tzdb" implementation "com.gabrielittner.threetenbp:lazythreetenbp:0.7.0" @@ -330,9 +347,10 @@ dependencies { implementation 'com.jakewharton.rxbinding3:rxbinding-material:3.0.0' implementation("com.airbnb.android:epoxy:$epoxy_version") + implementation "com.airbnb.android:epoxy-glide-preloading:$epoxy_version" kapt "com.airbnb.android:epoxy-processor:$epoxy_version" implementation "com.airbnb.android:epoxy-paging:$epoxy_version" - implementation 'com.airbnb.android:mvrx:1.3.0' + implementation 'com.airbnb.android:mvrx:1.5.1' // Work implementation "androidx.work:work-runtime-ktx:$work_version" @@ -348,7 +366,7 @@ dependencies { // UI implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1' - implementation 'com.google.android.material:material:1.3.0-alpha01' + implementation 'com.google.android.material:material:1.3.0-alpha02' implementation 'me.gujun.android:span:1.7' implementation "io.noties.markwon:core:$markwon_version" implementation "io.noties.markwon:html:$markwon_version" @@ -383,7 +401,7 @@ dependencies { implementation "com.github.piasy:GlideImageViewFactory:$big_image_viewer_version" // implementation 'com.github.MikeOrtiz:TouchImageView:3.0.2' - implementation 'com.github.chrisbanes:PhotoView:2.0.0' + implementation 'com.github.chrisbanes:PhotoView:2.1.4' implementation "com.github.bumptech.glide:glide:$glide_version" kapt "com.github.bumptech.glide:compiler:$glide_version" @@ -400,7 +418,7 @@ dependencies { kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.5.0' // gplay flavor only - gplayImplementation('com.google.firebase:firebase-messaging:20.2.4') { + gplayImplementation('com.google.firebase:firebase-messaging:20.3.0') { exclude group: 'com.google.firebase', module: 'firebase-core' exclude group: 'com.google.firebase', module: 'firebase-analytics' exclude group: 'com.google.firebase', module: 'firebase-measurement-connector' @@ -424,20 +442,22 @@ dependencies { implementation 'me.dm7.barcodescanner:zxing:1.9.13' // TESTS - testImplementation 'junit:junit:4.12' - testImplementation 'org.amshove.kluent:kluent-android:1.44' + testImplementation 'junit:junit:4.13' + testImplementation "org.amshove.kluent:kluent-android:$kluent_version" // Plant Timber tree for test testImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1' // Activate when you want to check for leaks, from time to time. //debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.3' - androidTestImplementation 'androidx.test:core:1.2.0' - androidTestImplementation 'androidx.test:runner:1.2.0' - androidTestImplementation 'androidx.test:rules:1.2.0' - androidTestImplementation 'androidx.test.ext:junit:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' - androidTestImplementation 'org.amshove.kluent:kluent-android:1.44' + androidTestImplementation "androidx.test:core:$androidxTest_version" + androidTestImplementation "androidx.test:runner:$androidxTest_version" + androidTestImplementation "androidx.test:rules:$androidxTest_version" + androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation "androidx.test.espresso:espresso-core:$espresso_version" + androidTestImplementation "androidx.test.espresso:espresso-contrib:$espresso_version" + androidTestImplementation "androidx.test.espresso:espresso-intents:$espresso_version" + androidTestImplementation "org.amshove.kluent:kluent-android:$kluent_version" androidTestImplementation "androidx.arch.core:core-testing:$arch_version" // Plant Timber tree for test androidTestImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1' diff --git a/vector/lint.xml b/vector/lint.xml index bd49091a3f..4ac0f20e51 100644 --- a/vector/lint.xml +++ b/vector/lint.xml @@ -27,6 +27,7 @@ + diff --git a/vector/src/androidTest/java/im/vector/app/EspressoExt.kt b/vector/src/androidTest/java/im/vector/app/EspressoExt.kt new file mode 100644 index 0000000000..d247d88caa --- /dev/null +++ b/vector/src/androidTest/java/im/vector/app/EspressoExt.kt @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app + +import android.app.Activity +import android.view.View +import androidx.lifecycle.Observer +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.IdlingResource +import androidx.test.espresso.PerformException +import androidx.test.espresso.UiController +import androidx.test.espresso.ViewAction +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.util.HumanReadables +import androidx.test.espresso.util.TreeIterables +import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread +import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation +import androidx.test.runner.lifecycle.ActivityLifecycleCallback +import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry +import androidx.test.runner.lifecycle.Stage +import org.hamcrest.Matcher +import org.hamcrest.Matchers +import org.hamcrest.StringDescription +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.sync.SyncState +import org.matrix.android.sdk.api.util.Optional +import org.matrix.android.sdk.internal.crypto.store.PrivateKeysInfo +import java.util.concurrent.TimeoutException + +object EspressoHelper { + fun getCurrentActivity(): Activity? { + var currentActivity: Activity? = null + getInstrumentation().runOnMainSync { + currentActivity = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(Stage.RESUMED).elementAtOrNull(0) + } + return currentActivity + } +} + +fun waitForView(viewMatcher: Matcher, timeout: Long = 10_000, waitForDisplayed: Boolean = true): ViewAction { + return object : ViewAction { + override fun getConstraints(): Matcher { + return Matchers.any(View::class.java) + } + + override fun getDescription(): String { + val matcherDescription = StringDescription() + viewMatcher.describeTo(matcherDescription) + return "wait for a specific view <$matcherDescription> to be ${if (waitForDisplayed) "displayed" else "not displayed during $timeout millis."}" + } + + override fun perform(uiController: UiController, view: View) { + println("*** waitForView 1 $view") + uiController.loopMainThreadUntilIdle() + val startTime = System.currentTimeMillis() + val endTime = startTime + timeout + val visibleMatcher = isDisplayed() + + do { + println("*** waitForView loop $view end:$endTime current:${System.currentTimeMillis()}") + val viewVisible = TreeIterables.breadthFirstViewTraversal(view) + .any { viewMatcher.matches(it) && visibleMatcher.matches(it) } + + println("*** waitForView loop viewVisible:$viewVisible") + if (viewVisible == waitForDisplayed) return + println("*** waitForView loop loopMainThreadForAtLeast...") + uiController.loopMainThreadForAtLeast(50) + println("*** waitForView loop ...loopMainThreadForAtLeast") + } while (System.currentTimeMillis() < endTime) + + println("*** waitForView timeout $view") + // Timeout happens. + throw PerformException.Builder() + .withActionDescription(this.description) + .withViewDescription(HumanReadables.describe(view)) + .withCause(TimeoutException()) + .build() + } + } +} + +fun initialSyncIdlingResource(session: Session): IdlingResource { + val res = object : IdlingResource, Observer { + private var callback: IdlingResource.ResourceCallback? = null + + override fun getName() = "InitialSyncIdlingResource for ${session.myUserId}" + + override fun isIdleNow(): Boolean { + val isIdle = session.hasAlreadySynced() + return isIdle + } + + override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback?) { + this.callback = callback + } + + override fun onChanged(t: SyncState?) { + val isIdle = session.hasAlreadySynced() + if (isIdle) { + callback?.onTransitionToIdle() + session.getSyncStateLive().removeObserver(this) + } + } + } + + runOnUiThread { + session.getSyncStateLive().observeForever(res) + } + + return res +} + +fun activityIdlingResource(activityClass: Class<*>): IdlingResource { + val res = object : IdlingResource, ActivityLifecycleCallback { + private var callback: IdlingResource.ResourceCallback? = null + + var hasResumed = false + private var currentActivity : Activity? = null + + val uniqTS = System.currentTimeMillis() + override fun getName() = "activityIdlingResource_${activityClass.name}_$uniqTS" + + override fun isIdleNow(): Boolean { + val currentActivity = currentActivity ?: ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(Stage.RESUMED).elementAtOrNull(0) + + val isIdle = hasResumed || currentActivity?.javaClass?.let { activityClass.isAssignableFrom(it) } ?: false + println("*** [$name] isIdleNow activityIdlingResource $currentActivity isIdle:$isIdle") + return isIdle + } + + override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback?) { + println("*** [$name] registerIdleTransitionCallback $callback") + this.callback = callback + // if (hasResumed) callback?.onTransitionToIdle() + } + + override fun onActivityLifecycleChanged(activity: Activity?, stage: Stage?) { + println("*** [$name] onActivityLifecycleChanged $activity $stage") + currentActivity = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(Stage.RESUMED).elementAtOrNull(0) + val isIdle = currentActivity?.javaClass?.let { activityClass.isAssignableFrom(it) } ?: false + println("*** [$name] onActivityLifecycleChanged $currentActivity isIdle:$isIdle") + if (isIdle) { + hasResumed = true + println("*** [$name] onActivityLifecycleChanged callback: $callback") + callback?.onTransitionToIdle() + ActivityLifecycleMonitorRegistry.getInstance().removeLifecycleCallback(this) + } + } + } + ActivityLifecycleMonitorRegistry.getInstance().addLifecycleCallback(res) + return res +} + +fun withIdlingResource(idlingResource: IdlingResource, block: (() -> Unit)) { + println("*** withIdlingResource register") + IdlingRegistry.getInstance().register(idlingResource) + block.invoke() + println("*** withIdlingResource unregister") + IdlingRegistry.getInstance().unregister(idlingResource) +} + +fun allSecretsKnownIdling(session: Session): IdlingResource { + val res = object : IdlingResource, Observer> { + private var callback: IdlingResource.ResourceCallback? = null + + var privateKeysInfo: PrivateKeysInfo? = session.cryptoService().crossSigningService().getCrossSigningPrivateKeys() + override fun getName() = "AllSecretsKnownIdling_${session.myUserId}" + + override fun isIdleNow(): Boolean { + println("*** [$name]/isIdleNow allSecretsKnownIdling ${privateKeysInfo?.allKnown()}") + return privateKeysInfo?.allKnown() == true + } + + override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback?) { + this.callback = callback + } + + override fun onChanged(t: Optional?) { + println("*** [$name] allSecretsKnownIdling ${t?.getOrNull()}") + privateKeysInfo = t?.getOrNull() + if (t?.getOrNull()?.allKnown() == true) { + session.cryptoService().crossSigningService().getLiveCrossSigningPrivateKeys().removeObserver(this) + callback?.onTransitionToIdle() + } + } + } + + runOnUiThread { + session.cryptoService().crossSigningService().getLiveCrossSigningPrivateKeys().observeForever(res) + } + + return res +} diff --git a/vector/src/androidTest/java/im/vector/app/RegistrationTest.kt b/vector/src/androidTest/java/im/vector/app/RegistrationTest.kt new file mode 100644 index 0000000000..b88356db59 --- /dev/null +++ b/vector/src/androidTest/java/im/vector/app/RegistrationTest.kt @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app + +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.action.ViewActions.closeSoftKeyboard +import androidx.test.espresso.action.ViewActions.typeText +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isEnabled +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.ext.junit.rules.ActivityScenarioRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.LargeTest +import im.vector.app.features.MainActivity +import im.vector.app.features.home.HomeActivity +import org.hamcrest.CoreMatchers.not +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +@LargeTest +class RegistrationTest { + + @get:Rule + val activityRule = ActivityScenarioRule(MainActivity::class.java) + + @Test + fun simpleRegister() { + val userId: String = "UiAutoTest_${System.currentTimeMillis()}" + val password: String = "password" + val homeServerUrl: String = "http://10.0.2.2:8080" + + // Check splashscreen is there + onView(withId(R.id.loginSplashSubmit)) + .check(matches(isDisplayed())) + .check(matches(withText(R.string.login_splash_submit))) + + // Click on get started + onView(withId(R.id.loginSplashSubmit)) + .perform(click()) + + // Check that home server options are shown + onView(withId(R.id.loginServerTitle)) + .check(matches(isDisplayed())) + .check(matches(withText(R.string.login_server_title))) + + // Chose custom server + onView(withId(R.id.loginServerChoiceOther)) + .perform(click()) + + // Enter local synapse + onView((withId(R.id.loginServerUrlFormHomeServerUrl))) + .perform(typeText(homeServerUrl)) + + // Click on continue + onView(withId(R.id.loginServerUrlFormSubmit)) + .check(matches(isEnabled())) + .perform(closeSoftKeyboard(), click()) + + // Click on the signup button + onView(withId(R.id.loginSignupSigninSubmit)) + .check(matches(isDisplayed())) + .perform(click()) + + // Ensure password flow supported + onView(withId(R.id.loginField)) + .check(matches(isDisplayed())) + onView(withId(R.id.passwordField)) + .check(matches(isDisplayed())) + + // Ensure user id + onView((withId(R.id.loginField))) + .perform(typeText(userId)) + + // Ensure login button not yet enabled + onView(withId(R.id.loginSubmit)) + .check(matches(not(isEnabled()))) + + // Ensure password + onView((withId(R.id.passwordField))) + .perform(closeSoftKeyboard(), typeText(password)) + + // Submit + onView(withId(R.id.loginSubmit)) + .check(matches(isEnabled())) + .perform(closeSoftKeyboard(), click()) + + withIdlingResource(activityIdlingResource(HomeActivity::class.java)) { + onView(withId(R.id.roomListContainer)) + .check(matches(isDisplayed())) + } + + val activity = EspressoHelper.getCurrentActivity()!! + val uiSession = (activity as HomeActivity).activeSessionHolder.getActiveSession() + + // Wait for initial sync and check room list is there + withIdlingResource(initialSyncIdlingResource(uiSession)) { + onView(withId(R.id.roomListContainer)) + .check(matches(isDisplayed())) + } + } +} diff --git a/vector/src/androidTest/java/im/vector/app/SecurityBootstrapTest.kt b/vector/src/androidTest/java/im/vector/app/SecurityBootstrapTest.kt new file mode 100644 index 0000000000..3ab8fe7dd9 --- /dev/null +++ b/vector/src/androidTest/java/im/vector/app/SecurityBootstrapTest.kt @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app + +import android.app.Activity +import android.app.Instrumentation.ActivityResult +import android.content.Intent +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.action.ViewActions.closeSoftKeyboard +import androidx.test.espresso.action.ViewActions.pressBack +import androidx.test.espresso.action.ViewActions.replaceText +import androidx.test.espresso.action.ViewActions.typeText +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.intent.Intents +import androidx.test.espresso.intent.Intents.intending +import androidx.test.espresso.intent.matcher.IntentMatchers.hasAction +import androidx.test.espresso.intent.matcher.IntentMatchers.isInternal +import androidx.test.espresso.matcher.RootMatchers +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.ext.junit.rules.ActivityScenarioRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.LargeTest +import androidx.test.platform.app.InstrumentationRegistry +import im.vector.app.features.MainActivity +import im.vector.app.features.crypto.recover.SetupMode +import im.vector.app.features.home.HomeActivity +import org.hamcrest.CoreMatchers.not +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.matrix.android.sdk.api.Matrix +import org.matrix.android.sdk.api.session.Session + +@RunWith(AndroidJUnit4::class) +@LargeTest +class SecurityBootstrapTest : VerificationTestBase() { + + var existingSession: Session? = null + + @get:Rule + val activityRule = ActivityScenarioRule(MainActivity::class.java) + + @Before + fun createSessionWithCrossSigning() { + val context = InstrumentationRegistry.getInstrumentation().targetContext + val matrix = Matrix.getInstance(context) + val userName = "foobar_${System.currentTimeMillis()}" + existingSession = createAccountAndSync(matrix, userName, password, true) + stubAllExternalIntents() + } + + private fun stubAllExternalIntents() { + // By default Espresso Intents does not stub any Intents. Stubbing needs to be setup before + // every test run. In this case all external Intents will be blocked. + Intents.init() + intending(not(isInternal())).respondWith(ActivityResult(Activity.RESULT_OK, null)) + } + + @Test + fun testBasicBootstrap() { + val userId: String = existingSession!!.myUserId + + doLogin(homeServerUrl, userId, password) + + // Thread.sleep(6000) + withIdlingResource(activityIdlingResource(HomeActivity::class.java)) { + onView(withId(R.id.roomListContainer)) + .check(matches(isDisplayed())) + .perform(closeSoftKeyboard()) + } + + val activity = EspressoHelper.getCurrentActivity()!! + val uiSession = (activity as HomeActivity).activeSessionHolder.getActiveSession() + + withIdlingResource(initialSyncIdlingResource(uiSession)) { + onView(withId(R.id.roomListContainer)) + .check(matches(isDisplayed())) + } + + activity.navigator.open4SSetup(activity, SetupMode.NORMAL) + + Thread.sleep(1000) + + onView(withId(R.id.bootstrapSetupSecureUseSecurityKey)) + .check(matches(isDisplayed())) + + onView(withId(R.id.bootstrapSetupSecureUseSecurityPassphrase)) + .check(matches(isDisplayed())) + .perform(click()) + + onView(isRoot()) + .perform(waitForView(withText(R.string.bootstrap_info_text_2))) + + // test back + onView(isRoot()).perform(pressBack()) + + Thread.sleep(1000) + + onView(withId(R.id.bootstrapSetupSecureUseSecurityKey)) + .check(matches(isDisplayed())) + + onView(withId(R.id.bootstrapSetupSecureUseSecurityPassphrase)) + .check(matches(isDisplayed())) + .perform(click()) + + onView(isRoot()) + .perform(waitForView(withText(R.string.bootstrap_info_text_2))) + + onView(withId(R.id.ssss_passphrase_enter_edittext)) + .perform(typeText("person woman man camera tv")) + + onView(withId(R.id.bootstrapSubmit)) + .perform(closeSoftKeyboard(), click()) + + // test bad pass + onView(withId(R.id.ssss_passphrase_enter_edittext)) + .perform(typeText("person woman man cmera tv")) + + onView(withId(R.id.bootstrapSubmit)) + .perform(closeSoftKeyboard(), click()) + + onView(withText(R.string.passphrase_passphrase_does_not_match)).check(matches(isDisplayed())) + + onView(withId(R.id.ssss_passphrase_enter_edittext)) + .perform(replaceText("person woman man camera tv")) + + onView(withId(R.id.bootstrapSubmit)) + .perform(closeSoftKeyboard(), click()) + + onView(withId(R.id.bottomSheetScrollView)) + .perform(waitForView(withText(R.string.bottom_sheet_save_your_recovery_key_content))) + + intending(hasAction(Intent.ACTION_SEND)).respondWith(ActivityResult(Activity.RESULT_OK, null)) + + onView(withId(R.id.recoveryCopy)) + .perform(click()) + + Thread.sleep(1000) + + // Dismiss dialog + onView(withText(R.string.ok)).inRoot(RootMatchers.isDialog()).perform(click()) + + onView(withId(R.id.bottomSheetScrollView)) + .perform(waitForView(withText(R.string.bottom_sheet_save_your_recovery_key_content))) + + onView(withText(R.string._continue)).perform(click()) + + // Assert that all is configured + assert(uiSession.cryptoService().crossSigningService().isCrossSigningInitialized()) + assert(uiSession.cryptoService().crossSigningService().canCrossSign()) + assert(uiSession.cryptoService().crossSigningService().allPrivateKeysKnown()) + assert(uiSession.cryptoService().keysBackupService().isEnabled) + assert(uiSession.cryptoService().keysBackupService().currentBackupVersion != null) + assert(uiSession.sharedSecretStorageService.isRecoverySetup()) + assert(uiSession.sharedSecretStorageService.isMegolmKeyInBackup()) + } +} diff --git a/vector/src/androidTest/java/im/vector/app/SleepViewAction.java b/vector/src/androidTest/java/im/vector/app/SleepViewAction.java new file mode 100644 index 0000000000..8623f24756 --- /dev/null +++ b/vector/src/androidTest/java/im/vector/app/SleepViewAction.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app; + +import android.view.View; + +import androidx.test.espresso.UiController; +import androidx.test.espresso.ViewAction; + +import org.hamcrest.Matcher; + +import static androidx.test.espresso.matcher.ViewMatchers.isRoot; + +public class SleepViewAction { + + public static ViewAction sleep(final long millis) { + return new ViewAction() { + @Override + public Matcher getConstraints() { + return isRoot(); + } + + @Override + public String getDescription() { + return "Wait for at least " + millis + " millis"; + } + + @Override + public void perform(final UiController uiController, final View view) { + uiController.loopMainThreadUntilIdle(); + uiController.loopMainThreadForAtLeast(millis); + } + }; + } +} \ No newline at end of file diff --git a/vector/src/androidTest/java/im/vector/app/TestMatrixCallback.kt b/vector/src/androidTest/java/im/vector/app/TestMatrixCallback.kt new file mode 100644 index 0000000000..2e254d48ef --- /dev/null +++ b/vector/src/androidTest/java/im/vector/app/TestMatrixCallback.kt @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app + +import androidx.annotation.CallSuper +import junit.framework.TestCase.fail +import org.matrix.android.sdk.api.MatrixCallback +import timber.log.Timber +import java.util.concurrent.CountDownLatch + +/** + * Simple implementation of MatrixCallback, which count down the CountDownLatch on each API callback + * @param onlySuccessful true to fail if an error occurs. This is the default behavior + * @param + */ +open class TestMatrixCallback(private val countDownLatch: CountDownLatch, + private val onlySuccessful: Boolean = true) : MatrixCallback { + + @CallSuper + override fun onSuccess(data: T) { + countDownLatch.countDown() + } + + @CallSuper + override fun onFailure(failure: Throwable) { + Timber.e(failure, "TestApiCallback") + + if (onlySuccessful) { + fail("onFailure " + failure.localizedMessage) + } + + countDownLatch.countDown() + } +} diff --git a/vector/src/androidTest/java/im/vector/app/VerificationTestBase.kt b/vector/src/androidTest/java/im/vector/app/VerificationTestBase.kt new file mode 100644 index 0000000000..2a1b6d802f --- /dev/null +++ b/vector/src/androidTest/java/im/vector/app/VerificationTestBase.kt @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app + +import android.net.Uri +import androidx.lifecycle.Observer +import androidx.test.espresso.Espresso +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.matcher.ViewMatchers +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import org.hamcrest.CoreMatchers +import org.junit.Assert +import org.matrix.android.sdk.api.Matrix +import org.matrix.android.sdk.api.MatrixCallback +import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig +import org.matrix.android.sdk.api.auth.data.LoginFlowResult +import org.matrix.android.sdk.api.auth.registration.RegistrationResult +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.sync.SyncState +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit + +abstract class VerificationTestBase { + + val password = "password" + val homeServerUrl: String = "http://10.0.2.2:8080" + + fun doLogin(homeServerUrl: String, userId: String, password: String) { + Espresso.onView(ViewMatchers.withId(R.id.loginSplashSubmit)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .check(ViewAssertions.matches(ViewMatchers.withText(R.string.login_splash_submit))) + + Espresso.onView(ViewMatchers.withId(R.id.loginSplashSubmit)) + .perform(ViewActions.click()) + + Espresso.onView(ViewMatchers.withId(R.id.loginServerTitle)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .check(ViewAssertions.matches(ViewMatchers.withText(R.string.login_server_title))) + + // Chose custom server + Espresso.onView(ViewMatchers.withId(R.id.loginServerChoiceOther)) + .perform(ViewActions.click()) + + // Enter local synapse + Espresso.onView((ViewMatchers.withId(R.id.loginServerUrlFormHomeServerUrl))) + .perform(ViewActions.typeText(homeServerUrl)) + + Espresso.onView(ViewMatchers.withId(R.id.loginServerUrlFormSubmit)) + .check(ViewAssertions.matches(ViewMatchers.isEnabled())) + .perform(ViewActions.closeSoftKeyboard(), ViewActions.click()) + + // Click on the signin button + Espresso.onView(ViewMatchers.withId(R.id.loginSignupSigninSignIn)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .perform(ViewActions.click()) + + // Ensure password flow supported + Espresso.onView(ViewMatchers.withId(R.id.loginField)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.passwordField)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + + Espresso.onView((ViewMatchers.withId(R.id.loginField))) + .perform(ViewActions.typeText(userId)) + Espresso.onView(ViewMatchers.withId(R.id.loginSubmit)) + .check(ViewAssertions.matches(CoreMatchers.not(ViewMatchers.isEnabled()))) + + Espresso.onView((ViewMatchers.withId(R.id.passwordField))) + .perform(ViewActions.closeSoftKeyboard(), ViewActions.typeText(password)) + + Espresso.onView(ViewMatchers.withId(R.id.loginSubmit)) + .check(ViewAssertions.matches(ViewMatchers.isEnabled())) + .perform(ViewActions.closeSoftKeyboard(), ViewActions.click()) + } + + private fun createAccount(userId: String = "UiAutoTest", password: String = "password", homeServerUrl: String = "http://10.0.2.2:8080") { + Espresso.onView(ViewMatchers.withId(R.id.loginSplashSubmit)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .check(ViewAssertions.matches(ViewMatchers.withText(R.string.login_splash_submit))) + + Espresso.onView(ViewMatchers.withId(R.id.loginSplashSubmit)) + .perform(ViewActions.click()) + + Espresso.onView(ViewMatchers.withId(R.id.loginServerTitle)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .check(ViewAssertions.matches(ViewMatchers.withText(R.string.login_server_title))) + + // Chose custom server + Espresso.onView(ViewMatchers.withId(R.id.loginServerChoiceOther)) + .perform(ViewActions.click()) + + // Enter local synapse + Espresso.onView((ViewMatchers.withId(R.id.loginServerUrlFormHomeServerUrl))) + .perform(ViewActions.typeText(homeServerUrl)) + + Espresso.onView(ViewMatchers.withId(R.id.loginServerUrlFormSubmit)) + .check(ViewAssertions.matches(ViewMatchers.isEnabled())) + .perform(ViewActions.closeSoftKeyboard(), ViewActions.click()) + + // Click on the signup button + Espresso.onView(ViewMatchers.withId(R.id.loginSignupSigninSubmit)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .perform(ViewActions.click()) + + // Ensure password flow supported + Espresso.onView(ViewMatchers.withId(R.id.loginField)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.passwordField)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + + Espresso.onView((ViewMatchers.withId(R.id.loginField))) + .perform(ViewActions.typeText(userId)) + Espresso.onView(ViewMatchers.withId(R.id.loginSubmit)) + .check(ViewAssertions.matches(CoreMatchers.not(ViewMatchers.isEnabled()))) + + Espresso.onView((ViewMatchers.withId(R.id.passwordField))) + .perform(ViewActions.typeText(password)) + + Espresso.onView(ViewMatchers.withId(R.id.loginSubmit)) + .check(ViewAssertions.matches(ViewMatchers.isEnabled())) + .perform(ViewActions.closeSoftKeyboard(), ViewActions.click()) + + Espresso.onView(ViewMatchers.withId(R.id.homeDrawerFragmentContainer)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + } + + fun createAccountAndSync(matrix: Matrix, userName: String, + password: String, + withInitialSync: Boolean): Session { + val hs = createHomeServerConfig() + + doSync { + matrix.authenticationService() + .getLoginFlow(hs, it) + } + + doSync { + matrix.authenticationService() + .getRegistrationWizard() + .createAccount(userName, password, null, it) + } + + // Perform dummy step + val registrationResult = doSync { + matrix.authenticationService() + .getRegistrationWizard() + .dummy(it) + } + + Assert.assertTrue(registrationResult is RegistrationResult.Success) + val session = (registrationResult as RegistrationResult.Success).session + if (withInitialSync) { + syncSession(session) + } + + return session + } + + fun createHomeServerConfig(): HomeServerConnectionConfig { + return HomeServerConnectionConfig.Builder() + .withHomeServerUri(Uri.parse(homeServerUrl)) + .build() + } + + // Transform a method with a MatrixCallback to a synchronous method + inline fun doSync(block: (MatrixCallback) -> Unit): T { + val lock = CountDownLatch(1) + var result: T? = null + + val callback = object : TestMatrixCallback(lock) { + override fun onSuccess(data: T) { + result = data + super.onSuccess(data) + } + } + + block.invoke(callback) + + lock.await(20_000, TimeUnit.MILLISECONDS) + + Assert.assertNotNull(result) + return result!! + } + + fun syncSession(session: Session) { + val lock = CountDownLatch(1) + + GlobalScope.launch(Dispatchers.Main) { session.open() } + + session.startSync(true) + + val syncLiveData = runBlocking(Dispatchers.Main) { + session.getSyncStateLive() + } + val syncObserver = object : Observer { + override fun onChanged(t: SyncState?) { + if (session.hasAlreadySynced()) { + lock.countDown() + syncLiveData.removeObserver(this) + } + } + } + GlobalScope.launch(Dispatchers.Main) { syncLiveData.observeForever(syncObserver) } + + lock.await(20_000, TimeUnit.MILLISECONDS) + } +} diff --git a/vector/src/androidTest/java/im/vector/app/VerifySessionInteractiveTest.kt b/vector/src/androidTest/java/im/vector/app/VerifySessionInteractiveTest.kt new file mode 100644 index 0000000000..d218b6ef7e --- /dev/null +++ b/vector/src/androidTest/java/im/vector/app/VerifySessionInteractiveTest.kt @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app + +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.IdlingResource +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.action.ViewActions.closeSoftKeyboard +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItem +import androidx.test.espresso.matcher.ViewMatchers.hasDescendant +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.ext.junit.rules.ActivityScenarioRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.LargeTest +import androidx.test.platform.app.InstrumentationRegistry +import im.vector.app.features.MainActivity +import im.vector.app.features.home.HomeActivity +import org.hamcrest.CoreMatchers.not +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.matrix.android.sdk.api.Matrix +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction +import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod +import org.matrix.android.sdk.api.session.crypto.verification.VerificationService +import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction +import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState +import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth + +@RunWith(AndroidJUnit4::class) +@LargeTest +class VerifySessionInteractiveTest : VerificationTestBase() { + + var existingSession: Session? = null + + @get:Rule + val activityRule = ActivityScenarioRule(MainActivity::class.java) + + @Before + fun createSessionWithCrossSigning() { + val context = InstrumentationRegistry.getInstrumentation().targetContext + val matrix = Matrix.getInstance(context) + val userName = "foobar_${System.currentTimeMillis()}" + existingSession = createAccountAndSync(matrix, userName, password, true) + doSync { + existingSession!!.cryptoService().crossSigningService() + .initializeCrossSigning(UserPasswordAuth( + user = existingSession!!.myUserId, + password = "password" + ), it) + } + } + + @Test + fun checkVerifyPopup() { + val userId: String = existingSession!!.myUserId + + doLogin(homeServerUrl, userId, password) + + // Thread.sleep(6000) + withIdlingResource(activityIdlingResource(HomeActivity::class.java)) { + onView(withId(R.id.roomListContainer)) + .check(matches(isDisplayed())) + .perform(closeSoftKeyboard()) + } + + val activity = EspressoHelper.getCurrentActivity()!! + val uiSession = (activity as HomeActivity).activeSessionHolder.getActiveSession() + + withIdlingResource(initialSyncIdlingResource(uiSession)) { + onView(withId(R.id.roomListContainer)) + .check(matches(isDisplayed())) + } + + // THIS IS THE ONLY WAY I FOUND TO CLICK ON ALERTERS... :( + // Cannot wait for view because of alerter animation? ... + onView(isRoot()) + .perform(waitForView(withId(com.tapadoo.alerter.R.id.llAlertBackground))) +// Thread.sleep(1000) +// onView(withId(com.tapadoo.alerter.R.id.llAlertBackground)) +// .perform(click()) + Thread.sleep(1000) + val popup = activity.findViewById(com.tapadoo.alerter.R.id.llAlertBackground) + activity.runOnUiThread { + popup.performClick() + } + + onView(isRoot()) + .perform(waitForView(withId(R.id.bottomSheetFragmentContainer))) +// .check() +// onView(withId(R.id.bottomSheetFragmentContainer)) +// .check(matches(isDisplayed())) + +// onView(isRoot()).perform(SleepViewAction.sleep(2000)) + + onView(withText(R.string.use_latest_app)) + .check(matches(isDisplayed())) + + // 4S is not setup so passphrase option should be hidden + onView(withId(R.id.bottomSheetFragmentContainer)) + .check(matches(not(hasDescendant(withText(R.string.verification_cannot_access_other_session))))) + + val request = existingSession!!.cryptoService().verificationService().requestKeyVerification( + listOf(VerificationMethod.SAS, VerificationMethod.QR_CODE_SCAN, VerificationMethod.QR_CODE_SHOW), + existingSession!!.myUserId, + listOf(uiSession.sessionParams.deviceId!!) + ) + + val transactionId = request.transactionId!! + val sasReadyIdle = verificationStateIdleResource(transactionId, VerificationTxState.ShortCodeReady, uiSession) + val otherSessionSasReadyIdle = verificationStateIdleResource(transactionId, VerificationTxState.ShortCodeReady, existingSession!!) + + onView(isRoot()).perform(SleepViewAction.sleep(1000)) + + // Assert QR code option is there and available + onView(withId(R.id.bottomSheetVerificationRecyclerView)) + .check(matches(hasDescendant(withText(R.string.verification_scan_their_code)))) + + onView(withId(R.id.bottomSheetVerificationRecyclerView)) + .check(matches(hasDescendant(withId(R.id.itemVerificationQrCodeImage)))) + + onView(withId(R.id.bottomSheetVerificationRecyclerView)) + .perform( + actionOnItem( + hasDescendant(withText(R.string.verification_scan_emoji_title)), + click() + ) + ) + + val firstSessionTr = existingSession!!.cryptoService().verificationService().getExistingTransaction( + existingSession!!.myUserId, + transactionId + ) as SasVerificationTransaction + + IdlingRegistry.getInstance().register(sasReadyIdle) + IdlingRegistry.getInstance().register(otherSessionSasReadyIdle) + onView(isRoot()).perform(SleepViewAction.sleep(300)) + // will only execute when Idle is ready + val expectedEmojis = firstSessionTr.getEmojiCodeRepresentation() + val targets = listOf(R.id.emoji0, R.id.emoji1, R.id.emoji2, R.id.emoji3, R.id.emoji4, R.id.emoji5, R.id.emoji6) + targets.forEachIndexed { index, res -> + onView(withId(res)) + .check( + matches(hasDescendant(withText(expectedEmojis[index].nameResId))) + ) + } + + IdlingRegistry.getInstance().unregister(sasReadyIdle) + IdlingRegistry.getInstance().unregister(otherSessionSasReadyIdle) + + val verificationSuccessIdle = + verificationStateIdleResource(transactionId, VerificationTxState.Verified, uiSession) + + // CLICK ON THEY MATCH + + onView(withId(R.id.bottomSheetVerificationRecyclerView)) + .perform( + actionOnItem( + hasDescendant(withText(R.string.verification_sas_match)), + click() + ) + ) + + firstSessionTr.userHasVerifiedShortCode() + + onView(isRoot()).perform(SleepViewAction.sleep(1000)) + + withIdlingResource(verificationSuccessIdle) { + onView(withId(R.id.bottomSheetVerificationRecyclerView)) + .check( + matches(hasDescendant(withText(R.string.verification_conclusion_ok_self_notice))) + ) + } + + // Wait a bit before done (to delay a bit sending of secrets to let other have time + // to mark as verified :/ + Thread.sleep(5_000) + // Click on done + onView(withId(R.id.bottomSheetVerificationRecyclerView)) + .perform( + actionOnItem( + hasDescendant(withText(R.string.done)), + click() + ) + ) + + // Wait until local secrets are known (gossip) + withIdlingResource(allSecretsKnownIdling(uiSession)) { + onView(withId(R.id.groupToolbarAvatarImageView)) + .perform(click()) + } + } + + fun signout() { + onView((withId(R.id.groupToolbarAvatarImageView))) + .perform(click()) + + onView((withId(R.id.homeDrawerHeaderSettingsView))) + .perform(click()) + + onView(withText("General")) + .perform(click()) + } + + fun verificationStateIdleResource(transactionId: String, checkForState: VerificationTxState, session: Session): IdlingResource { + val idle = object : IdlingResource, VerificationService.Listener { + private var callback: IdlingResource.ResourceCallback? = null + + private var currentState: VerificationTxState? = null + + override fun getName() = "verificationSuccessIdle" + + override fun isIdleNow(): Boolean { + return currentState == checkForState + } + + override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback?) { + this.callback = callback + } + + fun update(state: VerificationTxState) { + currentState = state + if (state == checkForState) { + session.cryptoService().verificationService().removeListener(this) + callback?.onTransitionToIdle() + } + } + + /** + * Called when a transaction is created, either by the user or initiated by the other user. + */ + override fun transactionCreated(tx: VerificationTransaction) { + if (tx.transactionId == transactionId) update(tx.state) + } + + /** + * Called when a transaction is updated. You may be interested to track the state of the VerificationTransaction. + */ + override fun transactionUpdated(tx: VerificationTransaction) { + if (tx.transactionId == transactionId) update(tx.state) + } + } + + session.cryptoService().verificationService().addListener(idle) + return idle + } + + object UITestVerificationUtils +} diff --git a/vector/src/androidTest/java/im/vector/app/VerifySessionPassphraseTest.kt b/vector/src/androidTest/java/im/vector/app/VerifySessionPassphraseTest.kt new file mode 100644 index 0000000000..f8c2a89ea8 --- /dev/null +++ b/vector/src/androidTest/java/im/vector/app/VerifySessionPassphraseTest.kt @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app + +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.action.ViewActions.closeSoftKeyboard +import androidx.test.espresso.action.ViewActions.typeText +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItem +import androidx.test.espresso.matcher.ViewMatchers.hasDescendant +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.ext.junit.rules.ActivityScenarioRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.LargeTest +import androidx.test.platform.app.InstrumentationRegistry +import im.vector.app.core.resources.StringProvider +import im.vector.app.features.MainActivity +import im.vector.app.features.crypto.quads.SharedSecureStorageActivity +import im.vector.app.features.crypto.recover.BootstrapCrossSigningTask +import im.vector.app.features.crypto.recover.Params +import im.vector.app.features.crypto.recover.SetupMode +import im.vector.app.features.home.HomeActivity +import kotlinx.coroutines.runBlocking +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.matrix.android.sdk.api.Matrix +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth + +@RunWith(AndroidJUnit4::class) +@LargeTest +class VerifySessionPassphraseTest : VerificationTestBase() { + + var existingSession: Session? = null + val passphrase = "person woman camera tv" + + @get:Rule + val activityRule = ActivityScenarioRule(MainActivity::class.java) + + @Before + fun createSessionWithCrossSigningAnd4S() { + val context = InstrumentationRegistry.getInstrumentation().targetContext + val matrix = Matrix.getInstance(context) + val userName = "foobar_${System.currentTimeMillis()}" + existingSession = createAccountAndSync(matrix, userName, password, true) + doSync { + existingSession!!.cryptoService().crossSigningService() + .initializeCrossSigning(UserPasswordAuth( + user = existingSession!!.myUserId, + password = "password" + ), it) + } + + val task = BootstrapCrossSigningTask(existingSession!!, StringProvider(context.resources)) + + runBlocking { + task.execute(Params( + userPasswordAuth = UserPasswordAuth(password = password), + passphrase = passphrase, + setupMode = SetupMode.NORMAL + )) + } + } + + @Test + fun checkVerifyWithPassphrase() { + val userId: String = existingSession!!.myUserId + + doLogin(homeServerUrl, userId, password) + + // Thread.sleep(6000) + withIdlingResource(activityIdlingResource(HomeActivity::class.java)) { + onView(withId(R.id.roomListContainer)) + .check(matches(isDisplayed())) + .perform(closeSoftKeyboard()) + } + + val activity = EspressoHelper.getCurrentActivity()!! + val uiSession = (activity as HomeActivity).activeSessionHolder.getActiveSession() + + withIdlingResource(initialSyncIdlingResource(uiSession)) { + onView(withId(R.id.roomListContainer)) + .check(matches(isDisplayed())) + } + + // THIS IS THE ONLY WAY I FOUND TO CLICK ON ALERTERS... :( + // Cannot wait for view because of alerter animation? ... + Thread.sleep(6000) + val popup = activity.findViewById(com.tapadoo.alerter.R.id.llAlertBackground) + activity.runOnUiThread { + popup.performClick() + } + + onView(withId(R.id.bottomSheetFragmentContainer)) + .check(matches(isDisplayed())) + + onView(isRoot()).perform(SleepViewAction.sleep(2000)) + + onView(withText(R.string.use_latest_app)) + .check(matches(isDisplayed())) + + // 4S is not setup so passphrase option should be hidden + onView(withId(R.id.bottomSheetFragmentContainer)) + .check(matches(hasDescendant(withText(R.string.verification_cannot_access_other_session)))) + + onView(withId(R.id.bottomSheetVerificationRecyclerView)) + .perform( + actionOnItem( + hasDescendant(withText(R.string.verification_cannot_access_other_session)), + click() + ) + ) + + withIdlingResource(activityIdlingResource(SharedSecureStorageActivity::class.java)) { + onView(withId(R.id.ssss__root)).check(matches(isDisplayed())) + } + + onView((withId(R.id.ssss_passphrase_enter_edittext))) + .perform(typeText(passphrase)) + + onView((withId(R.id.ssss_passphrase_submit))) + .perform(click()) + + System.out.println("*** passphrase 1") + + withIdlingResource(activityIdlingResource(HomeActivity::class.java)) { + System.out.println("*** passphrase 1.1") + onView(withId(R.id.bottomSheetVerificationRecyclerView)) + .check( + matches(hasDescendant(withText(R.string.verification_conclusion_ok_self_notice))) + ) + } + + System.out.println("*** passphrase 2") + // check that all secrets are known? + assert(uiSession.cryptoService().crossSigningService().canCrossSign()) + assert(uiSession.cryptoService().crossSigningService().allPrivateKeysKnown()) + + Thread.sleep(10_000) + } +} diff --git a/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt b/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt index 5590e19c10..9cca462d1a 100644 --- a/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt +++ b/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt @@ -28,6 +28,7 @@ import butterknife.OnClick import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ScreenComponent +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO import im.vector.app.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_CAMERA @@ -196,33 +197,29 @@ class DebugMenuActivity : VectorBaseActivity() { } private fun doScanQRCode() { - QrCodeScannerActivity.startForResult(this) + QrCodeScannerActivity.startForResult(this, qrStartForActivityResult) } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - if (resultCode == Activity.RESULT_OK) { - when (requestCode) { - QrCodeScannerActivity.QR_CODE_SCANNER_REQUEST_CODE -> { - toast("QrCode: " + QrCodeScannerActivity.getResultText(data) + " is QRCode: " + QrCodeScannerActivity.getResultIsQrCode(data)) + private val qrStartForActivityResult = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + toast("QrCode: " + QrCodeScannerActivity.getResultText(activityResult.data) + + " is QRCode: " + QrCodeScannerActivity.getResultIsQrCode(activityResult.data)) - // Also update the current QR Code (reverse operation) - // renderQrCode(QrCodeScannerActivity.getResultText(data) ?: "") - val result = QrCodeScannerActivity.getResultText(data)!! + // Also update the current QR Code (reverse operation) + // renderQrCode(QrCodeScannerActivity.getResultText(data) ?: "") + val result = QrCodeScannerActivity.getResultText(activityResult.data)!! - val qrCodeData = result.toQrCodeData() - Timber.e("qrCodeData: $qrCodeData") + val qrCodeData = result.toQrCodeData() + Timber.e("qrCodeData: $qrCodeData") - if (result.length != buffer.size) { - Timber.e("Error, length are not the same") - } else { - // Convert to ByteArray - val byteArrayResult = result.toByteArray(Charsets.ISO_8859_1) - for (i in byteArrayResult.indices) { - if (buffer[i] != byteArrayResult[i]) { - Timber.e("Error for byte $i, expecting ${buffer[i]} and get ${byteArrayResult[i]}") - } - } + if (result.length != buffer.size) { + Timber.e("Error, length are not the same") + } else { + // Convert to ByteArray + val byteArrayResult = result.toByteArray(Charsets.ISO_8859_1) + for (i in byteArrayResult.indices) { + if (buffer[i] != byteArrayResult[i]) { + Timber.e("Error for byte $i, expecting ${buffer[i]} and get ${byteArrayResult[i]}") } } } diff --git a/vector/src/debug/res/xml/shortcuts.xml b/vector/src/debug/res/xml/shortcuts.xml new file mode 100644 index 0000000000..100eac5691 --- /dev/null +++ b/vector/src/debug/res/xml/shortcuts.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestAutoStartBoot.kt b/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestAutoStartBoot.kt index b7834ecf45..e46a07f712 100644 --- a/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestAutoStartBoot.kt +++ b/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestAutoStartBoot.kt @@ -15,6 +15,8 @@ */ package im.vector.app.fdroid.features.settings.troubleshoot +import android.content.Intent +import androidx.activity.result.ActivityResultLauncher import im.vector.app.R import im.vector.app.core.resources.StringProvider import im.vector.app.features.settings.VectorPreferences @@ -28,7 +30,7 @@ class TestAutoStartBoot @Inject constructor(private val vectorPreferences: Vecto private val stringProvider: StringProvider) : TroubleshootTest(R.string.settings_troubleshoot_test_service_boot_title) { - override fun perform() { + override fun perform(activityResultLauncher: ActivityResultLauncher) { if (vectorPreferences.autoStartOnBoot()) { description = stringProvider.getString(R.string.settings_troubleshoot_test_service_boot_success) status = TestStatus.SUCCESS @@ -38,7 +40,7 @@ class TestAutoStartBoot @Inject constructor(private val vectorPreferences: Vecto quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_service_boot_quickfix) { override fun doFix() { vectorPreferences.setAutoStartOnBoot(true) - manager?.retry() + manager?.retry(activityResultLauncher) } } status = TestStatus.FAILED diff --git a/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBackgroundRestrictions.kt b/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBackgroundRestrictions.kt index 3e053d7fec..abdd696724 100644 --- a/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBackgroundRestrictions.kt +++ b/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBackgroundRestrictions.kt @@ -15,7 +15,9 @@ */ package im.vector.app.fdroid.features.settings.troubleshoot +import android.content.Intent import android.net.ConnectivityManager +import androidx.activity.result.ActivityResultLauncher import androidx.appcompat.app.AppCompatActivity import androidx.core.content.getSystemService import androidx.core.net.ConnectivityManagerCompat @@ -28,7 +30,7 @@ class TestBackgroundRestrictions @Inject constructor(private val context: AppCom private val stringProvider: StringProvider) : TroubleshootTest(R.string.settings_troubleshoot_test_bg_restricted_title) { - override fun perform() { + override fun perform(activityResultLauncher: ActivityResultLauncher) { context.getSystemService()!!.apply { // Checks if the device is on a metered network if (isActiveNetworkMetered) { diff --git a/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBatteryOptimization.kt b/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBatteryOptimization.kt index 510ade0a33..b1eeae6681 100644 --- a/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBatteryOptimization.kt +++ b/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBatteryOptimization.kt @@ -15,12 +15,13 @@ */ package im.vector.app.fdroid.features.settings.troubleshoot +import android.content.Intent +import androidx.activity.result.ActivityResultLauncher import androidx.appcompat.app.AppCompatActivity import im.vector.app.R import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.isIgnoringBatteryOptimizations import im.vector.app.core.utils.requestDisablingBatteryOptimization -import im.vector.app.features.settings.troubleshoot.NotificationTroubleshootTestManager import im.vector.app.features.settings.troubleshoot.TroubleshootTest import javax.inject.Inject @@ -29,7 +30,7 @@ class TestBatteryOptimization @Inject constructor( private val stringProvider: StringProvider ) : TroubleshootTest(R.string.settings_troubleshoot_test_battery_title) { - override fun perform() { + override fun perform(activityResultLauncher: ActivityResultLauncher) { if (isIgnoringBatteryOptimizations(context)) { description = stringProvider.getString(R.string.settings_troubleshoot_test_battery_success) status = TestStatus.SUCCESS @@ -38,7 +39,7 @@ class TestBatteryOptimization @Inject constructor( description = stringProvider.getString(R.string.settings_troubleshoot_test_battery_failed) quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_battery_quickfix) { override fun doFix() { - requestDisablingBatteryOptimization(context, null, NotificationTroubleshootTestManager.REQ_CODE_FIX) + requestDisablingBatteryOptimization(context, activityResultLauncher) } } status = TestStatus.FAILED diff --git a/vector/src/fdroid/java/im/vector/app/push/fcm/FcmHelper.kt b/vector/src/fdroid/java/im/vector/app/push/fcm/FcmHelper.kt index 5f0ee396ee..7603e738d7 100755 --- a/vector/src/fdroid/java/im/vector/app/push/fcm/FcmHelper.kt +++ b/vector/src/fdroid/java/im/vector/app/push/fcm/FcmHelper.kt @@ -1,5 +1,4 @@ /* - * Copyright 2014 OpenMarket Ltd * Copyright 2018 New Vector Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/vector/src/fdroid/java/im/vector/app/push/fcm/NotificationTroubleshootTestManagerFactory.kt b/vector/src/fdroid/java/im/vector/app/push/fcm/NotificationTroubleshootTestManagerFactory.kt index 65b8609446..6236aad65c 100644 --- a/vector/src/fdroid/java/im/vector/app/push/fcm/NotificationTroubleshootTestManagerFactory.kt +++ b/vector/src/fdroid/java/im/vector/app/push/fcm/NotificationTroubleshootTestManagerFactory.kt @@ -22,17 +22,21 @@ import im.vector.app.fdroid.features.settings.troubleshoot.TestBatteryOptimizati import im.vector.app.features.settings.troubleshoot.NotificationTroubleshootTestManager import im.vector.app.features.settings.troubleshoot.TestAccountSettings import im.vector.app.features.settings.troubleshoot.TestDeviceSettings +import im.vector.app.features.settings.troubleshoot.TestNotification import im.vector.app.features.settings.troubleshoot.TestPushRulesSettings import im.vector.app.features.settings.troubleshoot.TestSystemSettings import javax.inject.Inject -class NotificationTroubleshootTestManagerFactory @Inject constructor(private val testSystemSettings: TestSystemSettings, - private val testAccountSettings: TestAccountSettings, - private val testDeviceSettings: TestDeviceSettings, - private val testPushRulesSettings: TestPushRulesSettings, - private val testAutoStartBoot: TestAutoStartBoot, - private val testBackgroundRestrictions: TestBackgroundRestrictions, - private val testBatteryOptimization: TestBatteryOptimization) { +class NotificationTroubleshootTestManagerFactory @Inject constructor( + private val testSystemSettings: TestSystemSettings, + private val testAccountSettings: TestAccountSettings, + private val testDeviceSettings: TestDeviceSettings, + private val testPushRulesSettings: TestPushRulesSettings, + private val testAutoStartBoot: TestAutoStartBoot, + private val testBackgroundRestrictions: TestBackgroundRestrictions, + private val testBatteryOptimization: TestBatteryOptimization, + private val testNotification: TestNotification +) { fun create(fragment: Fragment): NotificationTroubleshootTestManager { val mgr = NotificationTroubleshootTestManager(fragment) @@ -43,6 +47,7 @@ class NotificationTroubleshootTestManagerFactory @Inject constructor(private val mgr.addTest(testAutoStartBoot) mgr.addTest(testBackgroundRestrictions) mgr.addTest(testBatteryOptimization) + mgr.addTest(testNotification) return mgr } } diff --git a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestFirebaseToken.kt b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestFirebaseToken.kt index 318867af91..32888dafd7 100644 --- a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestFirebaseToken.kt +++ b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestFirebaseToken.kt @@ -15,12 +15,13 @@ */ package im.vector.app.gplay.features.settings.troubleshoot +import android.content.Intent +import androidx.activity.result.ActivityResultLauncher import androidx.appcompat.app.AppCompatActivity import com.google.firebase.iid.FirebaseInstanceId import im.vector.app.R import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.startAddGoogleAccountIntent -import im.vector.app.features.settings.troubleshoot.NotificationTroubleshootTestManager import im.vector.app.features.settings.troubleshoot.TroubleshootTest import im.vector.app.push.fcm.FcmHelper import timber.log.Timber @@ -32,7 +33,7 @@ import javax.inject.Inject class TestFirebaseToken @Inject constructor(private val context: AppCompatActivity, private val stringProvider: StringProvider) : TroubleshootTest(R.string.settings_troubleshoot_test_fcm_title) { - override fun perform() { + override fun perform(activityResultLauncher: ActivityResultLauncher) { status = TestStatus.RUNNING try { FirebaseInstanceId.getInstance().instanceId @@ -48,7 +49,7 @@ class TestFirebaseToken @Inject constructor(private val context: AppCompatActivi description = stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed_account_missing, errorMsg) quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_fcm_failed_account_missing_quick_fix) { override fun doFix() { - startAddGoogleAccountIntent(context, NotificationTroubleshootTestManager.REQ_CODE_FIX) + startAddGoogleAccountIntent(context, activityResultLauncher) } } } else { diff --git a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPlayServices.kt b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPlayServices.kt index 133fe1cb05..92e713de81 100644 --- a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPlayServices.kt +++ b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPlayServices.kt @@ -15,6 +15,8 @@ */ package im.vector.app.gplay.features.settings.troubleshoot +import android.content.Intent +import androidx.activity.result.ActivityResultLauncher import androidx.appcompat.app.AppCompatActivity import com.google.android.gms.common.ConnectionResult import com.google.android.gms.common.GoogleApiAvailability @@ -31,7 +33,7 @@ class TestPlayServices @Inject constructor(private val context: AppCompatActivit private val stringProvider: StringProvider) : TroubleshootTest(R.string.settings_troubleshoot_test_play_services_title) { - override fun perform() { + override fun perform(activityResultLauncher: ActivityResultLauncher) { val apiAvailability = GoogleApiAvailability.getInstance() val resultCode = apiAvailability.isGooglePlayServicesAvailable(context) if (resultCode == ConnectionResult.SUCCESS) { diff --git a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPushFromPushGateway.kt b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPushFromPushGateway.kt new file mode 100644 index 0000000000..da93d54075 --- /dev/null +++ b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPushFromPushGateway.kt @@ -0,0 +1,74 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.app.gplay.features.settings.troubleshoot + +import android.content.Intent +import androidx.activity.result.ActivityResultLauncher +import androidx.appcompat.app.AppCompatActivity +import im.vector.app.R +import im.vector.app.core.error.ErrorFormatter +import im.vector.app.core.pushers.PushersManager +import im.vector.app.core.resources.StringProvider +import im.vector.app.features.settings.troubleshoot.TroubleshootTest +import im.vector.app.push.fcm.FcmHelper +import org.matrix.android.sdk.api.MatrixCallback +import org.matrix.android.sdk.api.session.pushers.PushGatewayFailure +import org.matrix.android.sdk.api.util.Cancelable +import javax.inject.Inject + +/** + * Test Push by asking the Push Gateway to send a Push back + */ +class TestPushFromPushGateway @Inject constructor(private val context: AppCompatActivity, + private val stringProvider: StringProvider, + private val errorFormatter: ErrorFormatter, + private val pushersManager: PushersManager) + : TroubleshootTest(R.string.settings_troubleshoot_test_push_loop_title) { + + private var action: Cancelable? = null + + override fun perform(activityResultLauncher: ActivityResultLauncher) { + val fcmToken = FcmHelper.getFcmToken(context) ?: run { + status = TestStatus.FAILED + return + } + action = pushersManager.testPush(fcmToken, object : MatrixCallback { + override fun onFailure(failure: Throwable) { + description = if (failure is PushGatewayFailure.PusherRejected) { + stringProvider.getString(R.string.settings_troubleshoot_test_push_loop_failed) + } else { + errorFormatter.toHumanReadable(failure) + } + status = TestStatus.FAILED + } + + override fun onSuccess(data: Unit) { + // Wait for the push to be received + description = stringProvider.getString(R.string.settings_troubleshoot_test_push_loop_waiting_for_push) + status = TestStatus.RUNNING + } + }) + } + + override fun onPushReceived() { + description = stringProvider.getString(R.string.settings_troubleshoot_test_push_loop_success) + status = TestStatus.SUCCESS + } + + override fun cancel() { + action?.cancel() + } +} diff --git a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestTokenRegistration.kt b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestTokenRegistration.kt index 6cb9c38fc6..f400c17d46 100644 --- a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestTokenRegistration.kt +++ b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestTokenRegistration.kt @@ -15,6 +15,8 @@ */ package im.vector.app.gplay.features.settings.troubleshoot +import android.content.Intent +import androidx.activity.result.ActivityResultLauncher import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.Observer import androidx.work.WorkInfo @@ -37,7 +39,7 @@ class TestTokenRegistration @Inject constructor(private val context: AppCompatAc private val activeSessionHolder: ActiveSessionHolder) : TroubleshootTest(R.string.settings_troubleshoot_test_token_registration_title) { - override fun perform() { + override fun perform(activityResultLauncher: ActivityResultLauncher) { // Check if we have a registered pusher for this token val fcmToken = FcmHelper.getFcmToken(context) ?: run { status = TestStatus.FAILED @@ -59,9 +61,9 @@ class TestTokenRegistration @Inject constructor(private val context: AppCompatAc WorkManager.getInstance(context).getWorkInfoByIdLiveData(workId).observe(context, Observer { workInfo -> if (workInfo != null) { if (workInfo.state == WorkInfo.State.SUCCEEDED) { - manager?.retry() + manager?.retry(activityResultLauncher) } else if (workInfo.state == WorkInfo.State.FAILED) { - manager?.retry() + manager?.retry(activityResultLauncher) } } }) diff --git a/vector/src/gplay/java/im/vector/app/gplay/push/fcm/VectorFirebaseMessagingService.kt b/vector/src/gplay/java/im/vector/app/gplay/push/fcm/VectorFirebaseMessagingService.kt index 8fdb65c8d0..cfd241d4f9 100755 --- a/vector/src/gplay/java/im/vector/app/gplay/push/fcm/VectorFirebaseMessagingService.kt +++ b/vector/src/gplay/java/im/vector/app/gplay/push/fcm/VectorFirebaseMessagingService.kt @@ -19,10 +19,12 @@ package im.vector.app.gplay.push.fcm +import android.content.Intent import android.os.Handler import android.os.Looper import androidx.lifecycle.Lifecycle import androidx.lifecycle.ProcessLifecycleOwner +import androidx.localbroadcastmanager.content.LocalBroadcastManager import com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.RemoteMessage import im.vector.app.BuildConfig @@ -34,6 +36,7 @@ import im.vector.app.features.badge.BadgeProxy import im.vector.app.features.notifications.NotifiableEventResolver import im.vector.app.features.notifications.NotifiableMessageEvent import im.vector.app.features.notifications.NotificationDrawerManager +import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.notifications.SimpleNotifiableEvent import im.vector.app.features.settings.VectorPreferences import im.vector.app.push.fcm.FcmHelper @@ -60,11 +63,13 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() { override fun onCreate() { super.onCreate() - notificationDrawerManager = vectorComponent().notificationDrawerManager() - notifiableEventResolver = vectorComponent().notifiableEventResolver() - pusherManager = vectorComponent().pusherManager() - activeSessionHolder = vectorComponent().activeSessionHolder() - vectorPreferences = vectorComponent().vectorPreferences() + with(vectorComponent()) { + notificationDrawerManager = notificationDrawerManager() + notifiableEventResolver = notifiableEventResolver() + pusherManager = pusherManager() + activeSessionHolder = activeSessionHolder() + vectorPreferences = vectorPreferences() + } } /** @@ -73,6 +78,13 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() { * @param message the message */ override fun onMessageReceived(message: RemoteMessage) { + // Diagnostic Push + if (message.data["event_id"] == PushersManager.TEST_EVENT_ID) { + val intent = Intent(NotificationUtils.PUSH_ACTION) + LocalBroadcastManager.getInstance(this).sendBroadcast(intent) + return + } + if (!vectorPreferences.areNotificationEnabledForDevice()) { Timber.i("Notification are disabled for this device") return diff --git a/vector/src/gplay/java/im/vector/app/push/fcm/FcmHelper.kt b/vector/src/gplay/java/im/vector/app/push/fcm/FcmHelper.kt index d139cad9d3..913eab211d 100755 --- a/vector/src/gplay/java/im/vector/app/push/fcm/FcmHelper.kt +++ b/vector/src/gplay/java/im/vector/app/push/fcm/FcmHelper.kt @@ -1,6 +1,4 @@ /* - * Copyright 2014 OpenMarket Ltd - * Copyright 2017 Vector Creations Ltd * Copyright 2018 New Vector Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/vector/src/gplay/java/im/vector/app/push/fcm/NotificationTroubleshootTestManagerFactory.kt b/vector/src/gplay/java/im/vector/app/push/fcm/NotificationTroubleshootTestManagerFactory.kt index b2dad09483..e96c603e60 100644 --- a/vector/src/gplay/java/im/vector/app/push/fcm/NotificationTroubleshootTestManagerFactory.kt +++ b/vector/src/gplay/java/im/vector/app/push/fcm/NotificationTroubleshootTestManagerFactory.kt @@ -19,20 +19,26 @@ import androidx.fragment.app.Fragment import im.vector.app.features.settings.troubleshoot.NotificationTroubleshootTestManager import im.vector.app.features.settings.troubleshoot.TestAccountSettings import im.vector.app.features.settings.troubleshoot.TestDeviceSettings +import im.vector.app.features.settings.troubleshoot.TestNotification import im.vector.app.features.settings.troubleshoot.TestPushRulesSettings import im.vector.app.features.settings.troubleshoot.TestSystemSettings import im.vector.app.gplay.features.settings.troubleshoot.TestFirebaseToken import im.vector.app.gplay.features.settings.troubleshoot.TestPlayServices +import im.vector.app.gplay.features.settings.troubleshoot.TestPushFromPushGateway import im.vector.app.gplay.features.settings.troubleshoot.TestTokenRegistration import javax.inject.Inject -class NotificationTroubleshootTestManagerFactory @Inject constructor(private val testSystemSettings: TestSystemSettings, - private val testAccountSettings: TestAccountSettings, - private val testDeviceSettings: TestDeviceSettings, - private val testBingRulesSettings: TestPushRulesSettings, - private val testPlayServices: TestPlayServices, - private val testFirebaseToken: TestFirebaseToken, - private val testTokenRegistration: TestTokenRegistration) { +class NotificationTroubleshootTestManagerFactory @Inject constructor( + private val testSystemSettings: TestSystemSettings, + private val testAccountSettings: TestAccountSettings, + private val testDeviceSettings: TestDeviceSettings, + private val testBingRulesSettings: TestPushRulesSettings, + private val testPlayServices: TestPlayServices, + private val testFirebaseToken: TestFirebaseToken, + private val testTokenRegistration: TestTokenRegistration, + private val testPushFromPushGateway: TestPushFromPushGateway, + private val testNotification: TestNotification +) { fun create(fragment: Fragment): NotificationTroubleshootTestManager { val mgr = NotificationTroubleshootTestManager(fragment) @@ -43,6 +49,8 @@ class NotificationTroubleshootTestManagerFactory @Inject constructor(private val mgr.addTest(testPlayServices) mgr.addTest(testFirebaseToken) mgr.addTest(testTokenRegistration) + mgr.addTest(testPushFromPushGateway) + mgr.addTest(testNotification) return mgr } } diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml index 2d00d94a01..1a55d22c13 100644 --- a/vector/src/main/AndroidManifest.xml +++ b/vector/src/main/AndroidManifest.xml @@ -62,6 +62,8 @@ @@ -73,6 +75,10 @@ + + @@ -91,11 +97,12 @@ - + + android:theme="@style/AppTheme.Transparent" + tools:ignore="Instantiatable" /> - @@ -171,6 +177,10 @@ + + @@ -220,6 +230,7 @@ + @@ -232,9 +243,11 @@ + + android:exported="false" + tools:ignore="Instantiatable" /> + + Matrix SDK
- Copyright (c) 2016 OpenMarket Ltd -
- Copyright (c) 2016-2017 Vector Creations Ltd -
- Copyright (c) 2018-2019 New Vector Ltd -
Copyright (c) 2019 The Matrix.org Foundation C.I.C
  • diff --git a/vector/src/main/java/im/vector/app/ActiveSessionDataSource.kt b/vector/src/main/java/im/vector/app/ActiveSessionDataSource.kt index 12738e2b0b..f58c685ab9 100644 --- a/vector/src/main/java/im/vector/app/ActiveSessionDataSource.kt +++ b/vector/src/main/java/im/vector/app/ActiveSessionDataSource.kt @@ -18,8 +18,8 @@ package im.vector.app import arrow.core.Option -import org.matrix.android.sdk.api.session.Session import im.vector.app.core.utils.BehaviorDataSource +import org.matrix.android.sdk.api.session.Session import javax.inject.Inject import javax.inject.Singleton diff --git a/vector/src/main/java/im/vector/app/AppStateHandler.kt b/vector/src/main/java/im/vector/app/AppStateHandler.kt index 31bc822a22..1e92f7bc67 100644 --- a/vector/src/main/java/im/vector/app/AppStateHandler.kt +++ b/vector/src/main/java/im/vector/app/AppStateHandler.kt @@ -20,9 +20,6 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.OnLifecycleEvent import arrow.core.Option -import org.matrix.android.sdk.api.session.group.model.GroupSummary -import org.matrix.android.sdk.api.session.room.model.RoomSummary -import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import im.vector.app.features.grouplist.ALL_COMMUNITIES_GROUP_ID import im.vector.app.features.grouplist.SelectedGroupDataSource import im.vector.app.features.home.HomeRoomListDataSource @@ -32,6 +29,9 @@ import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import io.reactivex.functions.BiFunction import io.reactivex.rxkotlin.addTo +import org.matrix.android.sdk.api.session.group.model.GroupSummary +import org.matrix.android.sdk.api.session.room.model.RoomSummary +import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.rx.rx import java.util.concurrent.TimeUnit import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/EmojiCompatFontProvider.kt b/vector/src/main/java/im/vector/app/EmojiCompatFontProvider.kt index 3aad81529c..689c12b6d3 100644 --- a/vector/src/main/java/im/vector/app/EmojiCompatFontProvider.kt +++ b/vector/src/main/java/im/vector/app/EmojiCompatFontProvider.kt @@ -23,7 +23,7 @@ import javax.inject.Inject import javax.inject.Singleton @Singleton -class EmojiCompatFontProvider @Inject constructor(): FontsContractCompat.FontRequestCallback() { +class EmojiCompatFontProvider @Inject constructor() : FontsContractCompat.FontRequestCallback() { var typeface: Typeface? = null set(value) { diff --git a/vector/src/main/java/im/vector/app/EmojiCompatWrapper.kt b/vector/src/main/java/im/vector/app/EmojiCompatWrapper.kt index dbb0848e74..c1ca3dd21d 100644 --- a/vector/src/main/java/im/vector/app/EmojiCompatWrapper.kt +++ b/vector/src/main/java/im/vector/app/EmojiCompatWrapper.kt @@ -24,7 +24,7 @@ import javax.inject.Inject import javax.inject.Singleton @Singleton -class EmojiCompatWrapper @Inject constructor(private val context: Context) { +class EmojiCompatWrapper @Inject constructor(private val context: Context) { private var initialized = false diff --git a/vector/src/main/java/im/vector/app/VectorApplication.kt b/vector/src/main/java/im/vector/app/VectorApplication.kt index 4b0ef66459..4f89763cda 100644 --- a/vector/src/main/java/im/vector/app/VectorApplication.kt +++ b/vector/src/main/java/im/vector/app/VectorApplication.kt @@ -17,7 +17,10 @@ package im.vector.app import android.app.Application +import android.content.BroadcastReceiver import android.content.Context +import android.content.Intent +import android.content.IntentFilter import android.content.res.Configuration import android.os.Handler import android.os.HandlerThread @@ -92,6 +95,15 @@ class VectorApplication : // font thread handler private var fontThreadHandler: Handler? = null + private val powerKeyReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent) { + if (intent.action == Intent.ACTION_SCREEN_OFF + && vectorPreferences.useFlagPinCode()) { + pinLocker.screenIsOff() + } + } + } + override fun onCreate() { enableStrictModeIfNeeded() super.onCreate() @@ -163,6 +175,12 @@ class VectorApplication : ProcessLifecycleOwner.get().lifecycle.addObserver(pinLocker) // This should be done as early as possible // initKnownEmojiHashSet(appContext) + + applicationContext.registerReceiver(powerKeyReceiver, IntentFilter().apply { + // Looks like i cannot receive OFF, if i don't have both ON and OFF + addAction(Intent.ACTION_SCREEN_OFF) + addAction(Intent.ACTION_SCREEN_ON) + }) } private fun enableStrictModeIfNeeded() { diff --git a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt index 75f61a7b01..014a244bf8 100644 --- a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt @@ -27,6 +27,7 @@ import im.vector.app.features.contactsbook.ContactsBookFragment import im.vector.app.features.crypto.keysbackup.settings.KeysBackupSettingsFragment import im.vector.app.features.crypto.quads.SharedSecuredStorageKeyFragment import im.vector.app.features.crypto.quads.SharedSecuredStoragePassphraseFragment +import im.vector.app.features.crypto.quads.SharedSecuredStorageResetAllFragment import im.vector.app.features.crypto.recover.BootstrapAccountPasswordFragment import im.vector.app.features.crypto.recover.BootstrapConclusionFragment import im.vector.app.features.crypto.recover.BootstrapConfirmPassphraseFragment @@ -51,6 +52,7 @@ import im.vector.app.features.home.HomeDrawerFragment import im.vector.app.features.home.LoadingFragment import im.vector.app.features.home.room.breadcrumbs.BreadcrumbsFragment import im.vector.app.features.home.room.detail.RoomDetailFragment +import im.vector.app.features.home.room.detail.search.SearchFragment import im.vector.app.features.home.room.list.RoomListFragment import im.vector.app.features.login.LoginCaptchaFragment import im.vector.app.features.login.LoginFragment @@ -530,6 +532,11 @@ interface FragmentModule { @FragmentKey(SharedSecuredStorageKeyFragment::class) fun bindSharedSecuredStorageKeyFragment(fragment: SharedSecuredStorageKeyFragment): Fragment + @Binds + @IntoMap + @FragmentKey(SharedSecuredStorageResetAllFragment::class) + fun bindSharedSecuredStorageResetAllFragment(fragment: SharedSecuredStorageResetAllFragment): Fragment + @Binds @IntoMap @FragmentKey(SetIdentityServerFragment::class) @@ -564,4 +571,9 @@ interface FragmentModule { @IntoMap @FragmentKey(RoomBannedMemberListFragment::class) fun bindRoomBannedMemberListFragment(fragment: RoomBannedMemberListFragment): Fragment + + @Binds + @IntoMap + @FragmentKey(SearchFragment::class) + fun bindSearchFragment(fragment: SearchFragment): Fragment } diff --git a/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt b/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt index d337ec7977..fde40f9195 100644 --- a/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt +++ b/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt @@ -38,6 +38,7 @@ import im.vector.app.features.home.HomeActivity import im.vector.app.features.home.HomeModule import im.vector.app.features.home.room.detail.RoomDetailActivity import im.vector.app.features.home.room.detail.readreceipts.DisplayReadReceiptsBottomSheet +import im.vector.app.features.home.room.detail.search.SearchActivity import im.vector.app.features.home.room.detail.timeline.action.MessageActionsBottomSheet import im.vector.app.features.home.room.detail.timeline.edithistory.ViewEditHistoryBottomSheet import im.vector.app.features.home.room.detail.timeline.reactions.ViewReactionsBottomSheet @@ -50,9 +51,7 @@ import im.vector.app.features.invite.VectorInviteView import im.vector.app.features.link.LinkHandlerActivity import im.vector.app.features.login.LoginActivity import im.vector.app.features.media.BigImageViewerActivity -import im.vector.app.features.media.ImageMediaViewerActivity import im.vector.app.features.media.VectorAttachmentViewerActivity -import im.vector.app.features.media.VideoMediaViewerActivity import im.vector.app.features.navigation.Navigator import im.vector.app.features.permalink.PermalinkHandlerActivity import im.vector.app.features.pin.PinLocker @@ -124,10 +123,8 @@ interface ScreenComponent { fun inject(activity: MainActivity) fun inject(activity: RoomDirectoryActivity) fun inject(activity: BugReportActivity) - fun inject(activity: ImageMediaViewerActivity) fun inject(activity: FilteredRoomsActivity) fun inject(activity: CreateRoomActivity) - fun inject(activity: VideoMediaViewerActivity) fun inject(activity: CreateDirectRoomActivity) fun inject(activity: IncomingShareActivity) fun inject(activity: SoftLogoutActivity) @@ -142,6 +139,7 @@ interface ScreenComponent { fun inject(activity: VectorCallActivity) fun inject(activity: VectorAttachmentViewerActivity) fun inject(activity: VectorJitsiActivity) + fun inject(activity: SearchActivity) /* ========================================================================================== * BottomSheets diff --git a/vector/src/main/java/im/vector/app/core/di/VectorComponent.kt b/vector/src/main/java/im/vector/app/core/di/VectorComponent.kt index 4ba3d6ba13..28f3a52efa 100644 --- a/vector/src/main/java/im/vector/app/core/di/VectorComponent.kt +++ b/vector/src/main/java/im/vector/app/core/di/VectorComponent.kt @@ -36,6 +36,7 @@ import im.vector.app.features.crypto.verification.IncomingVerificationRequestHan import im.vector.app.features.grouplist.SelectedGroupDataSource import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.HomeRoomListDataSource +import im.vector.app.features.home.room.detail.RoomDetailPendingActionStore import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider import im.vector.app.features.html.EventHtmlRenderer import im.vector.app.features.html.VectorHtmlCompressor @@ -114,6 +115,8 @@ interface VectorComponent { fun selectedGroupStore(): SelectedGroupDataSource + fun roomDetailPendingActionStore(): RoomDetailPendingActionStore + fun activeSessionObservableStore(): ActiveSessionDataSource fun incomingVerificationRequestHandler(): IncomingVerificationRequestHandler diff --git a/vector/src/main/java/im/vector/app/core/dialogs/UnrecognizedCertificateDialog.kt b/vector/src/main/java/im/vector/app/core/dialogs/UnrecognizedCertificateDialog.kt index 753cfd646c..5226a69094 100644 --- a/vector/src/main/java/im/vector/app/core/dialogs/UnrecognizedCertificateDialog.kt +++ b/vector/src/main/java/im/vector/app/core/dialogs/UnrecognizedCertificateDialog.kt @@ -72,12 +72,12 @@ class UnrecognizedCertificateDialog @Inject constructor( * @param callback callback to fire when the user makes a decision */ private fun internalShow(activity: Activity, - unrecognizedFingerprint: Fingerprint, - existing: Boolean, - callback: Callback, - userId: String?, - homeServerUrl: String, - homeServerConnectionConfigHasFingerprints: Boolean) { + unrecognizedFingerprint: Fingerprint, + existing: Boolean, + callback: Callback, + userId: String?, + homeServerUrl: String, + homeServerConnectionConfigHasFingerprints: Boolean) { val dialogId = userId ?: homeServerUrl + unrecognizedFingerprint.displayableHexRepr if (openDialogIds.contains(dialogId)) { diff --git a/vector/src/main/java/im/vector/app/core/epoxy/ExpandableTextItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/ExpandableTextItem.kt new file mode 100644 index 0000000000..3dceec48ef --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/epoxy/ExpandableTextItem.kt @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.core.epoxy + +import android.animation.ObjectAnimator +import android.text.TextUtils +import android.widget.ImageView +import android.widget.TextView +import androidx.core.view.doOnPreDraw +import androidx.core.view.isVisible +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import im.vector.app.R +import im.vector.app.core.extensions.copyOnLongClick + +@EpoxyModelClass(layout = R.layout.item_expandable_textview) +abstract class ExpandableTextItem : VectorEpoxyModel() { + + @EpoxyAttribute + lateinit var content: String + + @EpoxyAttribute + var maxLines: Int = 3 + + private var isExpanded = false + private var expandedLines = 0 + + override fun bind(holder: Holder) { + super.bind(holder) + holder.content.text = content + holder.content.copyOnLongClick() + + holder.content.doOnPreDraw { + if (holder.content.lineCount > maxLines) { + expandedLines = holder.content.lineCount + holder.content.maxLines = maxLines + + holder.view.setOnClickListener { + if (isExpanded) { + collapse(holder) + } else { + expand(holder) + } + } + holder.arrow.isVisible = true + } else { + holder.arrow.isVisible = false + } + } + } + + private fun expand(holder: Holder) { + ObjectAnimator + .ofInt(holder.content, "maxLines", expandedLines) + .setDuration(200) + .start() + + holder.content.ellipsize = null + holder.arrow.setImageResource(R.drawable.ic_expand_less) + holder.arrow.contentDescription = holder.view.context.getString(R.string.merged_events_collapse) + isExpanded = true + } + + private fun collapse(holder: Holder) { + ObjectAnimator + .ofInt(holder.content, "maxLines", maxLines) + .setDuration(200) + .start() + + holder.content.ellipsize = TextUtils.TruncateAt.END + holder.arrow.setImageResource(R.drawable.ic_expand_more) + holder.arrow.contentDescription = holder.view.context.getString(R.string.merged_events_expand) + isExpanded = false + } + + class Holder : VectorEpoxyHolder() { + val content by bind(R.id.expandableContent) + val arrow by bind(R.id.expandableArrow) + } +} diff --git a/vector/src/main/java/im/vector/app/core/epoxy/VectorEpoxyHolder.kt b/vector/src/main/java/im/vector/app/core/epoxy/VectorEpoxyHolder.kt index 8a75a6bddf..f33b4dccbe 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/VectorEpoxyHolder.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/VectorEpoxyHolder.kt @@ -36,7 +36,7 @@ abstract class VectorEpoxyHolder : EpoxyHolder() { protected fun bind(id: Int): ReadOnlyProperty = Lazy { holder: VectorEpoxyHolder, prop -> holder.view.findViewById(id) as V? - ?: throw IllegalStateException("View ID $id for '${prop.name}' not found.") + ?: throw IllegalStateException("View ID $id for '${prop.name}' not found.") } /** diff --git a/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetActionItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetActionItem.kt index 4beefeb737..e28bec6874 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetActionItem.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetActionItem.kt @@ -42,18 +42,25 @@ abstract class BottomSheetActionItem : VectorEpoxyModel Unit)? = null diff --git a/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetQuickReactionsItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetQuickReactionsItem.kt index 0c5919e5f3..ca8ef89c68 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetQuickReactionsItem.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetQuickReactionsItem.kt @@ -33,10 +33,13 @@ abstract class BottomSheetQuickReactionsItem : VectorEpoxyModel + @EpoxyAttribute lateinit var selecteds: List + @EpoxyAttribute var listener: Listener? = null diff --git a/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetSendStateItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetSendStateItem.kt index 119950f1b8..8f5ccba158 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetSendStateItem.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetSendStateItem.kt @@ -34,8 +34,10 @@ abstract class BottomSheetSendStateItem : VectorEpoxyModel : VectorEpoxy @EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer @EpoxyAttribute lateinit var matrixItem: MatrixItem @EpoxyAttribute var editable: Boolean = true + @EpoxyAttribute var userEncryptionTrustLevel: RoomEncryptionTrustLevel? = null @EpoxyAttribute var clickListener: View.OnClickListener? = null diff --git a/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileActionItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileActionItem.kt index c5d5ee52a8..3bef38d4cb 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileActionItem.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileActionItem.kt @@ -38,6 +38,7 @@ abstract class ProfileActionItem : VectorEpoxyModel() @EpoxyAttribute lateinit var title: String + @EpoxyAttribute var subtitle: String? = null diff --git a/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileSectionItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileSectionItem.kt index 6fa1ed23be..b38342c057 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileSectionItem.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileSectionItem.kt @@ -24,7 +24,7 @@ import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyModel @EpoxyModelClass(layout = R.layout.item_profile_section) -abstract class ProfileSectionItem: VectorEpoxyModel() { +abstract class ProfileSectionItem : VectorEpoxyModel() { @EpoxyAttribute lateinit var title: String diff --git a/vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt b/vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt index 43395b97f7..6065c74541 100644 --- a/vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt +++ b/vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt @@ -24,7 +24,6 @@ import org.matrix.android.sdk.api.failure.isInvalidPassword import org.matrix.android.sdk.api.session.identity.IdentityServiceError import java.net.HttpURLConnection import java.net.SocketTimeoutException -import java.net.UnknownHostException import javax.inject.Inject import javax.net.ssl.SSLException import javax.net.ssl.SSLPeerUnverifiedException @@ -45,60 +44,57 @@ class DefaultErrorFormatter @Inject constructor( when (throwable.ioException) { is SocketTimeoutException -> stringProvider.getString(R.string.error_network_timeout) - is UnknownHostException -> - // Invalid homeserver? - // TODO Check network state, airplane mode, etc. - stringProvider.getString(R.string.login_error_unknown_host) is SSLPeerUnverifiedException -> stringProvider.getString(R.string.login_error_ssl_peer_unverified) is SSLException -> stringProvider.getString(R.string.login_error_ssl_other) else -> + // TODO Check network state, airplane mode, etc. stringProvider.getString(R.string.error_no_network) } } is Failure.ServerError -> { when { - throwable.error.code == MatrixError.M_CONSENT_NOT_GIVEN -> { + throwable.error.code == MatrixError.M_CONSENT_NOT_GIVEN -> { // Special case for terms and conditions stringProvider.getString(R.string.error_terms_not_accepted) } - throwable.isInvalidPassword() -> { + throwable.isInvalidPassword() -> { stringProvider.getString(R.string.auth_invalid_login_param) } - throwable.error.code == MatrixError.M_USER_IN_USE -> { + throwable.error.code == MatrixError.M_USER_IN_USE -> { stringProvider.getString(R.string.login_signup_error_user_in_use) } - throwable.error.code == MatrixError.M_BAD_JSON -> { + throwable.error.code == MatrixError.M_BAD_JSON -> { stringProvider.getString(R.string.login_error_bad_json) } - throwable.error.code == MatrixError.M_NOT_JSON -> { + throwable.error.code == MatrixError.M_NOT_JSON -> { stringProvider.getString(R.string.login_error_not_json) } - throwable.error.code == MatrixError.M_THREEPID_DENIED -> { + throwable.error.code == MatrixError.M_THREEPID_DENIED -> { stringProvider.getString(R.string.login_error_threepid_denied) } - throwable.error.code == MatrixError.M_LIMIT_EXCEEDED -> { + throwable.error.code == MatrixError.M_LIMIT_EXCEEDED -> { limitExceededError(throwable.error) } - throwable.error.code == MatrixError.M_THREEPID_NOT_FOUND -> { + throwable.error.code == MatrixError.M_THREEPID_NOT_FOUND -> { stringProvider.getString(R.string.login_reset_password_error_not_found) } - throwable.error.code == MatrixError.M_USER_DEACTIVATED -> { + throwable.error.code == MatrixError.M_USER_DEACTIVATED -> { stringProvider.getString(R.string.auth_invalid_login_deactivated_account) } throwable.error.code == MatrixError.M_THREEPID_IN_USE - && throwable.error.message == "Email is already in use" -> { + && throwable.error.message == "Email is already in use" -> { stringProvider.getString(R.string.account_email_already_used_error) } throwable.error.code == MatrixError.M_THREEPID_IN_USE && throwable.error.message == "MSISDN is already in use" -> { stringProvider.getString(R.string.account_phone_number_already_used_error) } - throwable.error.code == MatrixError.M_THREEPID_AUTH_FAILED -> { + throwable.error.code == MatrixError.M_THREEPID_AUTH_FAILED -> { stringProvider.getString(R.string.error_threepid_auth_failed) } - else -> { + else -> { throwable.error.message.takeIf { it.isNotEmpty() } ?: throwable.error.code.takeIf { it.isNotEmpty() } } diff --git a/vector/src/main/java/im/vector/app/core/error/ResourceLimitErrorFormatter.kt b/vector/src/main/java/im/vector/app/core/error/ResourceLimitErrorFormatter.kt index ce0b2d0703..129491fe15 100644 --- a/vector/src/main/java/im/vector/app/core/error/ResourceLimitErrorFormatter.kt +++ b/vector/src/main/java/im/vector/app/core/error/ResourceLimitErrorFormatter.kt @@ -20,8 +20,8 @@ import android.content.Context import androidx.annotation.StringRes import androidx.core.text.HtmlCompat import im.vector.app.R -import org.matrix.android.sdk.api.failure.MatrixError import me.gujun.android.span.span +import org.matrix.android.sdk.api.failure.MatrixError class ResourceLimitErrorFormatter(private val context: Context) { diff --git a/vector/src/main/java/im/vector/app/core/extensions/Activity.kt b/vector/src/main/java/im/vector/app/core/extensions/Activity.kt index cc67f633eb..53c2b7fc6c 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/Activity.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/Activity.kt @@ -17,11 +17,20 @@ package im.vector.app.core.extensions import android.app.Activity +import android.content.Intent import android.os.Parcelable +import androidx.activity.ComponentActivity +import androidx.activity.result.ActivityResult +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentTransaction import im.vector.app.core.platform.VectorBaseActivity +fun ComponentActivity.registerStartForActivityResult(onResult: (ActivityResult) -> Unit): ActivityResultLauncher { + return registerForActivityResult(ActivityResultContracts.StartActivityForResult(), onResult) +} + fun VectorBaseActivity.addFragment( frameId: Int, fragment: Fragment, diff --git a/vector/src/main/java/im/vector/app/core/extensions/Fragment.kt b/vector/src/main/java/im/vector/app/core/extensions/Fragment.kt index fbcd6900c1..2740d5393a 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/Fragment.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/Fragment.kt @@ -17,7 +17,11 @@ package im.vector.app.core.extensions import android.app.Activity +import android.content.Intent import android.os.Parcelable +import androidx.activity.result.ActivityResult +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts import androidx.fragment.app.Fragment import im.vector.app.R import im.vector.app.core.platform.VectorBaseFragment @@ -26,6 +30,10 @@ import java.text.SimpleDateFormat import java.util.Date import java.util.Locale +fun Fragment.registerStartForActivityResult(onResult: (ActivityResult) -> Unit): ActivityResultLauncher { + return registerForActivityResult(ActivityResultContracts.StartActivityForResult(), onResult) +} + fun VectorBaseFragment.addFragment( frameId: Int, fragment: Fragment, @@ -160,26 +168,24 @@ fun Fragment.getAllChildFragments(): List { // Define a missing constant const val POP_BACK_STACK_EXCLUSIVE = 0 -fun Fragment.queryExportKeys(userId: String, requestCode: Int) { +fun Fragment.queryExportKeys(userId: String, activityResultLauncher: ActivityResultLauncher) { val timestamp = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date()) selectTxtFileToWrite( activity = requireActivity(), - fragment = this, + activityResultLauncher = activityResultLauncher, defaultFileName = "element-megolm-export-$userId-$timestamp.txt", - chooserHint = getString(R.string.keys_backup_setup_step1_manual_export), - requestCode = requestCode + chooserHint = getString(R.string.keys_backup_setup_step1_manual_export) ) } -fun Activity.queryExportKeys(userId: String, requestCode: Int) { +fun Activity.queryExportKeys(userId: String, activityResultLauncher: ActivityResultLauncher) { val timestamp = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date()) selectTxtFileToWrite( activity = this, - fragment = null, + activityResultLauncher = activityResultLauncher, defaultFileName = "element-megolm-export-$userId-$timestamp.txt", - chooserHint = getString(R.string.keys_backup_setup_step1_manual_export), - requestCode = requestCode + chooserHint = getString(R.string.keys_backup_setup_step1_manual_export) ) } diff --git a/vector/src/main/java/im/vector/app/core/files/FileSaver.kt b/vector/src/main/java/im/vector/app/core/files/FileSaver.kt index c4acbb5e65..406fa76520 100644 --- a/vector/src/main/java/im/vector/app/core/files/FileSaver.kt +++ b/vector/src/main/java/im/vector/app/core/files/FileSaver.kt @@ -59,7 +59,7 @@ fun addEntryToDownloadManager(context: Context, file: File, mimeType: String, title: String = file.name, - description: String = file.name) : Uri? { + description: String = file.name): Uri? { try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { val contentValues = ContentValues().apply { @@ -71,16 +71,16 @@ fun addEntryToDownloadManager(context: Context, return context.contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues) ?.let { uri -> Timber.v("## addEntryToDownloadManager(): $uri") - val source = file.inputStream().source().buffer() - context.contentResolver.openOutputStream(uri)?.sink()?.buffer()?.let { sink -> - source.use { input -> - sink.use { output -> - output.writeAll(input) + val source = file.inputStream().source().buffer() + context.contentResolver.openOutputStream(uri)?.sink()?.buffer()?.let { sink -> + source.use { input -> + sink.use { output -> + output.writeAll(input) + } + } } - } - } uri - } ?: run { + } ?: run { Timber.v("## addEntryToDownloadManager(): context.contentResolver.insert failed") null diff --git a/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt b/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt index 0eba78dbf8..71bd3ccc05 100644 --- a/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt +++ b/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt @@ -33,8 +33,6 @@ import timber.log.Timber import java.io.File import java.io.IOException import java.io.InputStream -import java.lang.Exception -import java.lang.IllegalArgumentException class VectorGlideModelLoaderFactory(private val activeSessionHolder: ActiveSessionHolder) : ModelLoaderFactory { @@ -118,7 +116,7 @@ class VectorGlideDataFetcher(private val activeSessionHolder: ActiveSessionHolde url = data.url, fileName = data.filename, elementToDecrypt = data.elementToDecrypt, - callback = object: MatrixCallback { + callback = object : MatrixCallback { override fun onSuccess(data: File) { callback.onDataReady(data.inputStream()) } diff --git a/vector/src/main/java/im/vector/app/core/intent/ExternalIntentAnalyser.kt b/vector/src/main/java/im/vector/app/core/intent/ExternalIntentAnalyser.kt index 24e03b4f40..0d18f808fb 100644 --- a/vector/src/main/java/im/vector/app/core/intent/ExternalIntentAnalyser.kt +++ b/vector/src/main/java/im/vector/app/core/intent/ExternalIntentAnalyser.kt @@ -109,7 +109,7 @@ fun analyseIntent(intent: Intent): List { for (i in 0 until clipData.itemCount) { val item = clipData.getItemAt(i) val mimeType = mimeTypes?.getOrElse(i) { mimeTypes[0] } - // uris list is not a valid mimetype + // uris list is not a valid mimetype .takeUnless { it == ClipDescription.MIMETYPE_TEXT_URILIST } externalIntentDataList.add(ExternalIntentData.IntentDataClipData(item, mimeType)) diff --git a/vector/src/main/java/im/vector/app/core/linkify/VectorLinkify.kt b/vector/src/main/java/im/vector/app/core/linkify/VectorLinkify.kt index d7dea789d3..bc1ed3609b 100644 --- a/vector/src/main/java/im/vector/app/core/linkify/VectorLinkify.kt +++ b/vector/src/main/java/im/vector/app/core/linkify/VectorLinkify.kt @@ -121,7 +121,7 @@ object VectorLinkify { remove = if (a.important) i + 1 else i } else { when { - b.end <= a.end -> + b.end <= a.end -> // b is inside a -> b should be removed remove = i + 1 a.end - a.start > b.end - b.start -> diff --git a/vector/src/main/java/im/vector/app/core/platform/CheckableFrameLayout.kt b/vector/src/main/java/im/vector/app/core/platform/CheckableFrameLayout.kt index a755903468..1111a9651c 100644 --- a/vector/src/main/java/im/vector/app/core/platform/CheckableFrameLayout.kt +++ b/vector/src/main/java/im/vector/app/core/platform/CheckableFrameLayout.kt @@ -26,11 +26,11 @@ class CheckableFrameLayout : FrameLayout, Checkable { private var mChecked = false - constructor(context: Context) : super(context) {} + constructor(context: Context) : super(context) - constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {} + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) - constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {} + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) override fun isChecked(): Boolean { return mChecked diff --git a/vector/src/main/java/im/vector/app/core/platform/SimpleFragmentActivity.kt b/vector/src/main/java/im/vector/app/core/platform/SimpleFragmentActivity.kt index 12f173d0ac..cfb3e3b656 100644 --- a/vector/src/main/java/im/vector/app/core/platform/SimpleFragmentActivity.kt +++ b/vector/src/main/java/im/vector/app/core/platform/SimpleFragmentActivity.kt @@ -25,8 +25,8 @@ import butterknife.BindView import im.vector.app.R import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.hideKeyboard -import org.matrix.android.sdk.api.session.Session import kotlinx.android.synthetic.main.activity.* +import org.matrix.android.sdk.api.session.Session import javax.inject.Inject /** diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt index 81f73556a5..79021902c4 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt @@ -18,7 +18,6 @@ package im.vector.app.core.platform import android.app.Activity import android.content.Context -import android.content.Intent import android.content.res.Configuration import android.os.Bundle import android.os.Parcelable @@ -60,6 +59,7 @@ import im.vector.app.core.dialogs.UnrecognizedCertificateDialog import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.observeEvent import im.vector.app.core.extensions.observeNotNull +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.restart import im.vector.app.core.extensions.vectorComponent import im.vector.app.core.utils.toast @@ -68,7 +68,6 @@ import im.vector.app.features.MainActivityArgs import im.vector.app.features.configuration.VectorConfiguration import im.vector.app.features.consent.ConsentNotGivenHelper import im.vector.app.features.navigation.Navigator -import im.vector.app.features.pin.PinActivity import im.vector.app.features.pin.PinLocker import im.vector.app.features.pin.PinMode import im.vector.app.features.pin.UnlockedActivity @@ -178,7 +177,7 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector { } override fun onCreate(savedInstanceState: Bundle?) { - Timber.i("onCreate Activity ${this.javaClass.simpleName}") + Timber.i("onCreate Activity ${javaClass.simpleName}") val vectorComponent = getVectorComponent() screenComponent = DaggerScreenComponent.factory().create(vectorComponent, this) val timeForInjection = measureTimeMillis { @@ -206,7 +205,7 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector { }) pinLocker.getLiveState().observeNotNull(this) { if (this@VectorBaseActivity !is UnlockedActivity && it == PinLocker.State.LOCKED) { - navigator.openPinCode(this, PinMode.AUTH) + navigator.openPinCode(this, pinStartForActivityResult, PinMode.AUTH) } } sessionListener = vectorComponent.sessionListener() @@ -306,29 +305,27 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector { override fun onDestroy() { super.onDestroy() - Timber.i("onDestroy Activity ${this.javaClass.simpleName}") + Timber.i("onDestroy Activity ${javaClass.simpleName}") unBinder?.unbind() unBinder = null uiDisposables.dispose() } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - if (requestCode == PinActivity.PIN_REQUEST_CODE) { - when (resultCode) { - Activity.RESULT_OK -> { - Timber.v("Pin ok, unlock app") - pinLocker.unlock() + private val pinStartForActivityResult = registerStartForActivityResult { activityResult -> + when (activityResult.resultCode) { + Activity.RESULT_OK -> { + Timber.v("Pin ok, unlock app") + pinLocker.unlock() - // Cancel any new started PinActivity, after a screen rotation for instance - finishActivity(PinActivity.PIN_REQUEST_CODE) - } - else -> { - if (pinLocker.getLiveState().value != PinLocker.State.UNLOCKED) { - // Remove the task, to be sure that PIN code will be requested when resumed - finishAndRemoveTask() - } + // Cancel any new started PinActivity, after a screen rotation for instance + // FIXME I cannot use this anymore :/ + // finishActivity(PinActivity.PIN_REQUEST_CODE) + } + else -> { + if (pinLocker.getLiveState().value != PinLocker.State.UNLOCKED) { + // Remove the task, to be sure that PIN code will be requested when resumed + finishAndRemoveTask() } } } @@ -336,7 +333,7 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector { override fun onResume() { super.onResume() - Timber.i("onResume Activity ${this.javaClass.simpleName}") + Timber.i("onResume Activity ${javaClass.simpleName}") configurationViewModel.onActivityResumed() @@ -376,7 +373,7 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector { override fun onPause() { super.onPause() - Timber.i("onPause Activity ${this.javaClass.simpleName}") + Timber.i("onPause Activity ${javaClass.simpleName}") rageShake.stop() diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt index 9ed5c5c455..e674c277aa 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt @@ -17,6 +17,7 @@ package im.vector.app.core.platform import android.app.Dialog import android.content.Context +import android.content.DialogInterface import android.os.Bundle import android.os.Parcelable import android.view.LayoutInflater @@ -86,6 +87,24 @@ abstract class VectorBaseBottomSheetDialogFragment : BottomSheetDialogFragment() open val showExpanded = false + interface ResultListener { + fun onBottomSheetResult(resultCode: Int, data: Any?) + + companion object { + const val RESULT_OK = 1 + const val RESULT_CANCEL = 0 + } + } + + var resultListener: ResultListener? = null + var bottomSheetResult: Int = ResultListener.RESULT_CANCEL + var bottomSheetResultData: Any? = null + + override fun onDismiss(dialog: DialogInterface) { + super.onDismiss(dialog) + resultListener?.onBottomSheetResult(bottomSheetResult, bottomSheetResultData) + } + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater.inflate(getLayoutResId(), container, false) unBinder = ButterKnife.bind(this, view) @@ -122,7 +141,7 @@ abstract class VectorBaseBottomSheetDialogFragment : BottomSheetDialogFragment() override fun onResume() { super.onResume() - Timber.i("onResume BottomSheet ${this.javaClass.simpleName}") + Timber.i("onResume BottomSheet ${javaClass.simpleName}") } override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt index 4ce4d210b0..179e21a6d8 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt @@ -58,7 +58,7 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector { // Butterknife unbinder private var mUnBinder: Unbinder? = null - val vectorBaseActivity: VectorBaseActivity by lazy { + protected val vectorBaseActivity: VectorBaseActivity by lazy { activity as VectorBaseActivity } @@ -112,7 +112,7 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector { } final override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - Timber.i("onCreateView Fragment ${this.javaClass.simpleName}") + Timber.i("onCreateView Fragment ${javaClass.simpleName}") return inflater.inflate(getLayoutResId(), container, false) } @@ -122,7 +122,7 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector { @CallSuper override fun onResume() { super.onResume() - Timber.i("onResume Fragment ${this.javaClass.simpleName}") + Timber.i("onResume Fragment ${javaClass.simpleName}") } @CallSuper @@ -142,7 +142,7 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector { @CallSuper override fun onDestroyView() { super.onDestroyView() - Timber.i("onDestroyView Fragment ${this.javaClass.simpleName}") + Timber.i("onDestroyView Fragment ${javaClass.simpleName}") mUnBinder?.unbind() mUnBinder = null uiDisposables.clear() diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt b/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt index 6429c9dfe5..002dfcf068 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt @@ -42,9 +42,11 @@ abstract class VectorViewModel Single.toAsync(stateReducer: S.(Async) -> S): Single> { setState { stateReducer(Loading()) } - return this.map { Success(it) as Async } + return map { Success(it) as Async } .onErrorReturn { Fail(it) } .doOnSuccess { setState { stateReducer(it) } } } @@ -53,9 +55,11 @@ abstract class VectorViewModel Observable.toAsync(stateReducer: S.(Async) -> S): Observable> { setState { stateReducer(Loading()) } - return this.map { Success(it) as Async } + return map { Success(it) as Async } .onErrorReturn { Fail(it) } .doOnNext { setState { stateReducer(it) } } } diff --git a/vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt b/vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt index f0d27182e7..5fe30141d9 100644 --- a/vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt +++ b/vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt @@ -22,6 +22,7 @@ import im.vector.app.core.resources.AppNameProvider import im.vector.app.core.resources.LocaleProvider import im.vector.app.core.resources.StringProvider import org.matrix.android.sdk.api.MatrixCallback +import org.matrix.android.sdk.api.util.Cancelable import java.util.UUID import javax.inject.Inject import kotlin.math.abs @@ -34,6 +35,17 @@ class PushersManager @Inject constructor( private val stringProvider: StringProvider, private val appNameProvider: AppNameProvider ) { + fun testPush(pushKey: String, callback: MatrixCallback): Cancelable { + val currentSession = activeSessionHolder.getActiveSession() + + return currentSession.testPush( + stringProvider.getString(R.string.pusher_http_url), + stringProvider.getString(R.string.pusher_app_id), + pushKey, + TEST_EVENT_ID, + callback + ) + } fun registerPusherWithFcmKey(pushKey: String): UUID { val currentSession = activeSessionHolder.getActiveSession() @@ -56,4 +68,8 @@ class PushersManager @Inject constructor( val currentSession = activeSessionHolder.getSafeActiveSession() ?: return currentSession.removeHttpPusher(pushKey, stringProvider.getString(R.string.pusher_app_id), callback) } + + companion object { + const val TEST_EVENT_ID = "\$THIS_IS_A_FAKE_EVENT_ID" + } } diff --git a/vector/src/main/java/im/vector/app/core/resources/DrawableProvider.kt b/vector/src/main/java/im/vector/app/core/resources/DrawableProvider.kt index 2f6b9aa6d2..c184b04bd9 100644 --- a/vector/src/main/java/im/vector/app/core/resources/DrawableProvider.kt +++ b/vector/src/main/java/im/vector/app/core/resources/DrawableProvider.kt @@ -29,6 +29,7 @@ class DrawableProvider @Inject constructor(private val context: Context) { fun getDrawable(@DrawableRes colorRes: Int): Drawable? { return ContextCompat.getDrawable(context, colorRes) } + fun getDrawable(@DrawableRes colorRes: Int, @ColorInt color: Int): Drawable? { return ContextCompat.getDrawable(context, colorRes)?.let { ThemeUtils.tintDrawableWithColor(it, color) diff --git a/vector/src/main/java/im/vector/app/core/ui/list/GenericLoaderItem.kt b/vector/src/main/java/im/vector/app/core/ui/list/GenericLoaderItem.kt index 634e58e943..b458f10680 100644 --- a/vector/src/main/java/im/vector/app/core/ui/list/GenericLoaderItem.kt +++ b/vector/src/main/java/im/vector/app/core/ui/list/GenericLoaderItem.kt @@ -1,3 +1,19 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package im.vector.app.core.ui.list import com.airbnb.epoxy.EpoxyModelClass diff --git a/vector/src/main/java/im/vector/app/core/ui/views/BottomSheetActionButton.kt b/vector/src/main/java/im/vector/app/core/ui/views/BottomSheetActionButton.kt index 0259898ee3..d418822b7f 100644 --- a/vector/src/main/java/im/vector/app/core/ui/views/BottomSheetActionButton.kt +++ b/vector/src/main/java/im/vector/app/core/ui/views/BottomSheetActionButton.kt @@ -24,6 +24,7 @@ import android.view.View import android.widget.FrameLayout import android.widget.ImageView import android.widget.TextView +import androidx.core.content.ContextCompat import androidx.core.content.withStyledAttributes import androidx.core.view.isGone import androidx.core.view.isInvisible @@ -107,6 +108,12 @@ class BottomSheetActionButton @JvmOverloads constructor( leftIconImageView.imageTintList = value?.let { ColorStateList.valueOf(value) } } + var titleTextColor: Int? = null + set(value) { + field = value + value?.let { actionTextView.setTextColor(it) } + } + init { inflate(context, R.layout.item_verification_action, this) ButterKnife.bind(this) @@ -120,6 +127,7 @@ class BottomSheetActionButton @JvmOverloads constructor( rightIcon = getDrawable(R.styleable.BottomSheetActionButton_rightIcon) tint = getColor(R.styleable.BottomSheetActionButton_tint, ThemeUtils.getColor(context, android.R.attr.textColor)) + titleTextColor = getColor(R.styleable.BottomSheetActionButton_titleTextColor, ContextCompat.getColor(context, R.color.riotx_accent)) } } } diff --git a/vector/src/main/java/im/vector/app/core/ui/views/NotificationAreaView.kt b/vector/src/main/java/im/vector/app/core/ui/views/NotificationAreaView.kt index 828e0ab687..4620d7a1fb 100644 --- a/vector/src/main/java/im/vector/app/core/ui/views/NotificationAreaView.kt +++ b/vector/src/main/java/im/vector/app/core/ui/views/NotificationAreaView.kt @@ -28,11 +28,11 @@ import im.vector.app.R import im.vector.app.core.error.ResourceLimitErrorFormatter import im.vector.app.core.utils.DimensionConverter import im.vector.app.features.themes.ThemeUtils -import org.matrix.android.sdk.api.failure.MatrixError -import org.matrix.android.sdk.api.session.events.model.Event import kotlinx.android.synthetic.main.view_notification_area.view.* import me.gujun.android.span.span import me.saket.bettermovementmethod.BetterLinkMovementMethod +import org.matrix.android.sdk.api.failure.MatrixError +import org.matrix.android.sdk.api.session.events.model.Event import timber.log.Timber /** diff --git a/vector/src/main/java/im/vector/app/core/ui/views/PasswordStrengthBar.kt b/vector/src/main/java/im/vector/app/core/ui/views/PasswordStrengthBar.kt index 65577d18f3..64ee71edf8 100644 --- a/vector/src/main/java/im/vector/app/core/ui/views/PasswordStrengthBar.kt +++ b/vector/src/main/java/im/vector/app/core/ui/views/PasswordStrengthBar.kt @@ -56,15 +56,19 @@ class PasswordStrengthBar @JvmOverloads constructor( @BindColor(R.color.password_strength_bar_undefined) @JvmField var colorBackground: Int = 0 + @BindColor(R.color.password_strength_bar_weak) @JvmField var colorWeak: Int = 0 + @BindColor(R.color.password_strength_bar_low) @JvmField var colorLow: Int = 0 + @BindColor(R.color.password_strength_bar_ok) @JvmField var colorOk: Int = 0 + @BindColor(R.color.password_strength_bar_strong) @JvmField var colorStrong: Int = 0 diff --git a/vector/src/main/java/im/vector/app/core/utils/ExternalApplicationsUtil.kt b/vector/src/main/java/im/vector/app/core/utils/ExternalApplicationsUtil.kt index d28f6749a6..ff1055fa44 100644 --- a/vector/src/main/java/im/vector/app/core/utils/ExternalApplicationsUtil.kt +++ b/vector/src/main/java/im/vector/app/core/utils/ExternalApplicationsUtil.kt @@ -31,22 +31,22 @@ import android.provider.Browser import android.provider.MediaStore import android.webkit.MimeTypeMap import android.widget.Toast +import androidx.activity.result.ActivityResultLauncher import androidx.browser.customtabs.CustomTabsIntent import androidx.browser.customtabs.CustomTabsSession import androidx.core.content.ContextCompat import androidx.core.content.FileProvider import androidx.core.content.getSystemService -import androidx.fragment.app.Fragment import im.vector.app.BuildConfig import im.vector.app.R import im.vector.app.features.notifications.NotificationUtils -import org.matrix.android.sdk.api.extensions.tryOrNull import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import okio.buffer import okio.sink import okio.source +import org.matrix.android.sdk.api.extensions.tryOrNull import timber.log.Timber import java.io.File import java.io.FileInputStream @@ -130,7 +130,7 @@ fun openSoundRecorder(activity: Activity, requestCode: Int) { * Open file selection activity */ fun openFileSelection(activity: Activity, - fragment: Fragment?, + activityResultLauncher: ActivityResultLauncher?, allowMultipleSelection: Boolean, requestCode: Int) { val fileIntent = Intent(Intent.ACTION_GET_CONTENT) @@ -140,8 +140,8 @@ fun openFileSelection(activity: Activity, fileIntent.type = "*/*" try { - fragment - ?.startActivityForResult(fileIntent, requestCode) + activityResultLauncher + ?.launch(fileIntent) ?: run { activity.startActivityForResult(fileIntent, requestCode) } @@ -300,11 +300,24 @@ fun shareMedia(context: Context, file: File, mediaMimeType: String?) { sendIntent.type = mediaMimeType sendIntent.putExtra(Intent.EXTRA_STREAM, mediaUri) - try { - context.startActivity(sendIntent) - } catch (activityNotFoundException: ActivityNotFoundException) { - context.toast(R.string.error_no_external_application_found) - } + sendShareIntent(context, sendIntent) + } +} + +fun shareText(context: Context, text: String) { + val sendIntent = Intent() + sendIntent.action = Intent.ACTION_SEND + sendIntent.type = "text/plain" + sendIntent.putExtra(Intent.EXTRA_TEXT, text) + + sendShareIntent(context, sendIntent) +} + +private fun sendShareIntent(context: Context, intent: Intent) { + try { + context.startActivity(Intent.createChooser(intent, context.getString(R.string.share))) + } catch (activityNotFoundException: ActivityNotFoundException) { + context.toast(R.string.error_no_external_application_found) } } @@ -440,10 +453,9 @@ fun openPlayStore(activity: Activity, appId: String = BuildConfig.APPLICATION_ID */ fun selectTxtFileToWrite( activity: Activity, - fragment: Fragment?, + activityResultLauncher: ActivityResultLauncher, defaultFileName: String, - chooserHint: String, - requestCode: Int + chooserHint: String ) { val intent = Intent(Intent.ACTION_CREATE_DOCUMENT) intent.addCategory(Intent.CATEGORY_OPENABLE) @@ -452,11 +464,7 @@ fun selectTxtFileToWrite( try { val chooserIntent = Intent.createChooser(intent, chooserHint) - if (fragment != null) { - fragment.startActivityForResult(chooserIntent, requestCode) - } else { - activity.startActivityForResult(chooserIntent, requestCode) - } + activityResultLauncher.launch(chooserIntent) } catch (activityNotFoundException: ActivityNotFoundException) { activity.toast(R.string.error_no_external_application_found) } diff --git a/vector/src/main/java/im/vector/app/core/utils/PermissionsTools.kt b/vector/src/main/java/im/vector/app/core/utils/PermissionsTools.kt index aff26ae494..44fc6afa4e 100644 --- a/vector/src/main/java/im/vector/app/core/utils/PermissionsTools.kt +++ b/vector/src/main/java/im/vector/app/core/utils/PermissionsTools.kt @@ -22,6 +22,8 @@ import android.content.Context import android.content.pm.PackageManager import android.os.Build import android.widget.Toast +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.StringRes import androidx.appcompat.app.AlertDialog import androidx.core.app.ActivityCompat @@ -94,6 +96,12 @@ fun logPermissionStatuses(context: Context) { } } +fun Fragment.registerForPermissionsResult(allGranted: (Boolean) -> Unit): ActivityResultLauncher> { + return registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { result -> + allGranted.invoke(result.keys.all { result[it] == true }) + } +} + /** * See [.checkPermissions] * @@ -112,14 +120,14 @@ fun checkPermissions(permissionsToBeGrantedBitMap: Int, * See [.checkPermissions] * * @param permissionsToBeGrantedBitMap - * @param fragment + * @param activityResultLauncher from the calling fragment that is requesting the permissions * @return true if the permissions are granted (synchronous flow), false otherwise (asynchronous flow) */ fun checkPermissions(permissionsToBeGrantedBitMap: Int, - fragment: Fragment, - requestCode: Int, + activity: Activity, + activityResultLauncher: ActivityResultLauncher>, @StringRes rationaleMessage: Int = 0): Boolean { - return checkPermissions(permissionsToBeGrantedBitMap, fragment.activity, fragment, requestCode, rationaleMessage) + return checkPermissions(permissionsToBeGrantedBitMap, activity, activityResultLauncher, 0, rationaleMessage) } /** @@ -137,22 +145,19 @@ fun checkPermissions(permissionsToBeGrantedBitMap: Int, * * @param permissionsToBeGrantedBitMap the permissions bit map to be granted * @param activity the calling Activity that is requesting the permissions (or fragment parent) - * @param fragment the calling fragment that is requesting the permissions + * @param activityResultLauncher from the calling fragment that is requesting the permissions * @return true if the permissions are granted (synchronous flow), false otherwise (asynchronous flow) */ private fun checkPermissions(permissionsToBeGrantedBitMap: Int, - activity: Activity?, - fragment: Fragment?, + activity: Activity, + activityResultLauncher: ActivityResultLauncher>?, requestCode: Int, @StringRes rationaleMessage: Int ): Boolean { var isPermissionGranted = false // sanity check - if (null == activity) { - Timber.w("## checkPermissions(): invalid input data") - isPermissionGranted = false - } else if (PERMISSIONS_EMPTY == permissionsToBeGrantedBitMap) { + if (PERMISSIONS_EMPTY == permissionsToBeGrantedBitMap) { isPermissionGranted = true } else if (PERMISSIONS_FOR_AUDIO_IP_CALL != permissionsToBeGrantedBitMap && PERMISSIONS_FOR_VIDEO_IP_CALL != permissionsToBeGrantedBitMap @@ -222,7 +227,8 @@ private fun checkPermissions(permissionsToBeGrantedBitMap: Int, .setOnCancelListener { Toast.makeText(activity, R.string.missing_permissions_warning, Toast.LENGTH_SHORT).show() } .setPositiveButton(R.string.ok) { _, _ -> if (permissionsListToBeGranted.isNotEmpty()) { - fragment?.requestPermissions(permissionsListToBeGranted.toTypedArray(), requestCode) + activityResultLauncher + ?.launch(permissionsListToBeGranted.toTypedArray()) ?: run { ActivityCompat.requestPermissions(activity, permissionsListToBeGranted.toTypedArray(), requestCode) } @@ -262,7 +268,8 @@ private fun checkPermissions(permissionsToBeGrantedBitMap: Int, .show() */ } else { - fragment?.requestPermissions(permissionsArrayToBeGranted, requestCode) + activityResultLauncher + ?.launch(permissionsArrayToBeGranted) ?: run { ActivityCompat.requestPermissions(activity, permissionsArrayToBeGranted, requestCode) } @@ -307,43 +314,6 @@ private fun updatePermissionsToBeGranted(activity: Activity, return isRequestPermissionRequested } -/** - * Helper method to process [.PERMISSIONS_FOR_AUDIO_IP_CALL] - * on onRequestPermissionsResult() methods. - * - * @param context App context - * @param grantResults permissions granted results - * @return true if audio IP call is permitted, false otherwise - */ -fun onPermissionResultAudioIpCall(context: Context, grantResults: IntArray): Boolean { - val arePermissionsGranted = allGranted(grantResults) - - if (!arePermissionsGranted) { - Toast.makeText(context, R.string.permissions_action_not_performed_missing_permissions, Toast.LENGTH_SHORT).show() - } - - return arePermissionsGranted -} - -/** - * Helper method to process [.PERMISSIONS_FOR_VIDEO_IP_CALL] - * on onRequestPermissionsResult() methods. - * For video IP calls, record audio and camera permissions are both mandatory. - * - * @param context App context - * @param grantResults permissions granted results - * @return true if video IP call is permitted, false otherwise - */ -fun onPermissionResultVideoIpCall(context: Context, grantResults: IntArray): Boolean { - val arePermissionsGranted = allGranted(grantResults) - - if (!arePermissionsGranted) { - Toast.makeText(context, R.string.permissions_action_not_performed_missing_permissions, Toast.LENGTH_SHORT).show() - } - - return arePermissionsGranted -} - /** * Return true if all permissions are granted, false if not or if permission request has been cancelled */ diff --git a/vector/src/main/java/im/vector/app/core/utils/SystemUtils.kt b/vector/src/main/java/im/vector/app/core/utils/SystemUtils.kt index 0e722da34a..d228adab12 100644 --- a/vector/src/main/java/im/vector/app/core/utils/SystemUtils.kt +++ b/vector/src/main/java/im/vector/app/core/utils/SystemUtils.kt @@ -28,8 +28,8 @@ import android.os.Build import android.os.PowerManager import android.provider.Settings import android.widget.Toast +import androidx.activity.result.ActivityResultLauncher import androidx.annotation.StringRes -import androidx.appcompat.app.AppCompatActivity import androidx.core.content.getSystemService import androidx.fragment.app.Fragment import im.vector.app.R @@ -55,6 +55,10 @@ fun isAirplaneModeOn(context: Context): Boolean { return Settings.Global.getInt(context.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0) != 0 } +fun isAnimationDisabled(context: Context): Boolean { + return Settings.Global.getFloat(context.contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1f) == 0f +} + /** * display the system dialog for granting this permission. If previously granted, the * system will not show it (so you should call this method). @@ -63,15 +67,11 @@ fun isAirplaneModeOn(context: Context): Boolean { * will return false and the notification privacy will fallback to "LOW_DETAIL". */ @TargetApi(Build.VERSION_CODES.M) -fun requestDisablingBatteryOptimization(activity: Activity, fragment: Fragment?, requestCode: Int) { +fun requestDisablingBatteryOptimization(activity: Activity, activityResultLauncher: ActivityResultLauncher) { val intent = Intent() intent.action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS intent.data = Uri.parse("package:" + activity.packageName) - if (fragment != null) { - fragment.startActivityForResult(intent, requestCode) - } else { - activity.startActivityForResult(intent, requestCode) - } + activityResultLauncher.launch(intent) } // ============================================================================================================== @@ -96,17 +96,17 @@ fun copyToClipboard(context: Context, text: CharSequence, showToast: Boolean = t * Shows notification settings for the current app. * In android O will directly opens the notification settings, in lower version it will show the App settings */ -fun startNotificationSettingsIntent(activity: AppCompatActivity, requestCode: Int) { +fun startNotificationSettingsIntent(context: Context, activityResultLauncher: ActivityResultLauncher) { val intent = Intent() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS - intent.putExtra(Settings.EXTRA_APP_PACKAGE, activity.packageName) + intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName) } else { intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS - intent.putExtra("app_package", activity.packageName) - intent.putExtra("app_uid", activity.applicationInfo?.uid) + intent.putExtra("app_package", context.packageName) + intent.putExtra("app_uid", context.applicationInfo?.uid) } - activity.startActivityForResult(intent, requestCode) + activityResultLauncher.launch(intent) } /** @@ -122,42 +122,47 @@ fun startNotificationChannelSettingsIntent(fragment: Fragment, channelID: String fragment.startActivity(intent) } -fun startAddGoogleAccountIntent(context: AppCompatActivity, requestCode: Int) { +fun startAddGoogleAccountIntent(context: Context, activityResultLauncher: ActivityResultLauncher) { try { val intent = Intent(Settings.ACTION_ADD_ACCOUNT) intent.putExtra(Settings.EXTRA_ACCOUNT_TYPES, arrayOf("com.google")) - context.startActivityForResult(intent, requestCode) + activityResultLauncher.launch(intent) } catch (activityNotFoundException: ActivityNotFoundException) { context.toast(R.string.error_no_external_application_found) } } -fun startSharePlainTextIntent(fragment: Fragment, chooserTitle: String?, text: String, subject: String? = null, requestCode: Int? = null) { +fun startSharePlainTextIntent(fragment: Fragment, + activityResultLauncher: ActivityResultLauncher?, + chooserTitle: String?, + text: String, + subject: String? = null) { val share = Intent(Intent.ACTION_SEND) share.type = "text/plain" share.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT) // Add data to the intent, the receiving app will decide what to do with it. share.putExtra(Intent.EXTRA_SUBJECT, subject) share.putExtra(Intent.EXTRA_TEXT, text) + val intent = Intent.createChooser(share, chooserTitle) try { - if (requestCode != null) { - fragment.startActivityForResult(Intent.createChooser(share, chooserTitle), requestCode) + if (activityResultLauncher != null) { + activityResultLauncher.launch(intent) } else { - fragment.startActivity(Intent.createChooser(share, chooserTitle)) + fragment.startActivity(intent) } } catch (activityNotFoundException: ActivityNotFoundException) { fragment.activity?.toast(R.string.error_no_external_application_found) } } -fun startImportTextFromFileIntent(fragment: Fragment, requestCode: Int) { +fun startImportTextFromFileIntent(context: Context, activityResultLauncher: ActivityResultLauncher) { val intent = Intent(Intent.ACTION_GET_CONTENT).apply { type = "text/plain" } - if (intent.resolveActivity(fragment.requireActivity().packageManager) != null) { - fragment.startActivityForResult(intent, requestCode) + if (intent.resolveActivity(context.packageManager) != null) { + activityResultLauncher.launch(intent) } else { - fragment.activity?.toast(R.string.error_no_external_application_found) + context.toast(R.string.error_no_external_application_found) } } diff --git a/vector/src/main/java/im/vector/app/core/utils/TemporaryStore.kt b/vector/src/main/java/im/vector/app/core/utils/TemporaryStore.kt index 32b995c004..63f80341eb 100644 --- a/vector/src/main/java/im/vector/app/core/utils/TemporaryStore.kt +++ b/vector/src/main/java/im/vector/app/core/utils/TemporaryStore.kt @@ -23,6 +23,7 @@ const val THREE_MINUTES = 3 * 60_000L /** * Store an object T for a specific period of time + * @param delay delay to keep the data, in millis */ open class TemporaryStore(private val delay: Long = THREE_MINUTES) { @@ -30,14 +31,16 @@ open class TemporaryStore(private val delay: Long = THREE_MINUTES) { var data: T? = null set(value) { - field = value timer?.cancel() - timer = Timer().also { - it.schedule(object : TimerTask() { - override fun run() { - field = null - } - }, delay) + field = value + if (value != null) { + timer = Timer().also { + it.schedule(object : TimerTask() { + override fun run() { + field = null + } + }, delay) + } } } } diff --git a/vector/src/main/java/im/vector/app/features/MainActivity.kt b/vector/src/main/java/im/vector/app/features/MainActivity.kt index ce81ba12f1..b5552e4d62 100644 --- a/vector/src/main/java/im/vector/app/features/MainActivity.kt +++ b/vector/src/main/java/im/vector/app/features/MainActivity.kt @@ -22,8 +22,6 @@ import android.os.Bundle import android.os.Parcelable import androidx.appcompat.app.AlertDialog import com.bumptech.glide.Glide -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.failure.GlobalError import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ScreenComponent @@ -38,6 +36,7 @@ import im.vector.app.features.notifications.NotificationDrawerManager import im.vector.app.features.pin.PinCodeStore import im.vector.app.features.pin.PinLocker import im.vector.app.features.pin.UnlockedActivity +import im.vector.app.features.popup.PopupAlertManager import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.signout.hard.SignedOutActivity import im.vector.app.features.signout.soft.SoftLogoutActivity @@ -47,6 +46,8 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import org.matrix.android.sdk.api.MatrixCallback +import org.matrix.android.sdk.api.failure.GlobalError import timber.log.Timber import javax.inject.Inject @@ -89,6 +90,7 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity { @Inject lateinit var shortcutsHandler: ShortcutsHandler @Inject lateinit var pinCodeStore: PinCodeStore @Inject lateinit var pinLocker: PinLocker + @Inject lateinit var popupAlertManager: PopupAlertManager override fun injectWith(injector: ScreenComponent) { injector.inject(this) @@ -115,6 +117,9 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity { // Also clear the dynamic shortcuts shortcutsHandler.clearShortcuts() + + // Also clear the alerts + popupAlertManager.cancelAll() } private fun parseArgs(): MainActivityArgs { diff --git a/vector/src/main/java/im/vector/app/features/attachments/AttachmentsHelper.kt b/vector/src/main/java/im/vector/app/features/attachments/AttachmentsHelper.kt index 1d7c67b046..d4efb22eb8 100644 --- a/vector/src/main/java/im/vector/app/features/attachments/AttachmentsHelper.kt +++ b/vector/src/main/java/im/vector/app/features/attachments/AttachmentsHelper.kt @@ -15,12 +15,11 @@ */ package im.vector.app.features.attachments -import android.app.Activity import android.content.Context import android.content.Intent import android.net.Uri import android.os.Bundle -import androidx.fragment.app.Fragment +import androidx.activity.result.ActivityResultLauncher import im.vector.app.core.platform.Restorable import im.vector.lib.multipicker.MultiPicker import org.matrix.android.sdk.BuildConfig @@ -48,6 +47,7 @@ class AttachmentsHelper(val context: Context, val callback: Callback) : Restorab // Capture path allows to handle camera image picking. It must be restored if the activity gets killed. private var captureUri: Uri? = null + // The pending type is set if we have to handle permission request. It must be restored if the activity gets killed. var pendingType: AttachmentTypeSelectorView.Type? = null @@ -72,99 +72,93 @@ class AttachmentsHelper(val context: Context, val callback: Callback) : Restorab /** * Starts the process for handling file picking */ - fun selectFile(fragment: Fragment) { - MultiPicker.get(MultiPicker.FILE).startWith(fragment) + fun selectFile(activityResultLauncher: ActivityResultLauncher) { + MultiPicker.get(MultiPicker.FILE).startWith(activityResultLauncher) } /** * Starts the process for handling image picking */ - fun selectGallery(fragment: Fragment) { - MultiPicker.get(MultiPicker.IMAGE).startWith(fragment) + fun selectGallery(activityResultLauncher: ActivityResultLauncher) { + MultiPicker.get(MultiPicker.IMAGE).startWith(activityResultLauncher) } /** * Starts the process for handling audio picking */ - fun selectAudio(fragment: Fragment) { - MultiPicker.get(MultiPicker.AUDIO).startWith(fragment) + fun selectAudio(activityResultLauncher: ActivityResultLauncher) { + MultiPicker.get(MultiPicker.AUDIO).startWith(activityResultLauncher) } /** * Starts the process for handling capture image picking */ - fun openCamera(fragment: Fragment) { - captureUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(fragment) + fun openCamera(context: Context, activityResultLauncher: ActivityResultLauncher) { + captureUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(context, activityResultLauncher) } /** * Starts the process for handling contact picking */ - fun selectContact(fragment: Fragment) { - MultiPicker.get(MultiPicker.CONTACT).startWith(fragment) + fun selectContact(activityResultLauncher: ActivityResultLauncher) { + MultiPicker.get(MultiPicker.CONTACT).startWith(activityResultLauncher) } /** - * This methods aims to handle on activity result data. - * - * @return true if it can handle the data, false otherwise + * This methods aims to handle the result data. */ - fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean { - if (resultCode == Activity.RESULT_OK) { - when (requestCode) { - MultiPicker.REQUEST_CODE_PICK_FILE -> { - callback.onContentAttachmentsReady( - MultiPicker.get(MultiPicker.FILE) - .getSelectedFiles(context, requestCode, resultCode, data) - .map { it.toContentAttachmentData() } - ) + fun onFileResult(data: Intent?) { + callback.onContentAttachmentsReady( + MultiPicker.get(MultiPicker.FILE) + .getSelectedFiles(context, data) + .map { it.toContentAttachmentData() } + ) + } + + fun onAudioResult(data: Intent?) { + callback.onContentAttachmentsReady( + MultiPicker.get(MultiPicker.AUDIO) + .getSelectedFiles(context, data) + .map { it.toContentAttachmentData() } + ) + } + + fun onContactResult(data: Intent?) { + MultiPicker.get(MultiPicker.CONTACT) + .getSelectedFiles(context, data) + .firstOrNull() + ?.toContactAttachment() + ?.let { + callback.onContactAttachmentReady(it) } - MultiPicker.REQUEST_CODE_PICK_AUDIO -> { - callback.onContentAttachmentsReady( - MultiPicker.get(MultiPicker.AUDIO) - .getSelectedFiles(context, requestCode, resultCode, data) - .map { it.toContentAttachmentData() } - ) - } - MultiPicker.REQUEST_CODE_PICK_CONTACT -> { - MultiPicker.get(MultiPicker.CONTACT) - .getSelectedFiles(context, requestCode, resultCode, data) - .firstOrNull() - ?.toContactAttachment() - ?.let { - callback.onContactAttachmentReady(it) - } - } - MultiPicker.REQUEST_CODE_PICK_IMAGE -> { - callback.onContentAttachmentsReady( - MultiPicker.get(MultiPicker.IMAGE) - .getSelectedFiles(context, requestCode, resultCode, data) - .map { it.toContentAttachmentData() } - ) - } - MultiPicker.REQUEST_CODE_TAKE_PHOTO -> { - captureUri?.let { captureUri -> - MultiPicker.get(MultiPicker.CAMERA) - .getTakenPhoto(context, requestCode, resultCode, captureUri) - ?.let { - callback.onContentAttachmentsReady( - listOf(it).map { it.toContentAttachmentData() } - ) - } + } + + fun onImageResult(data: Intent?) { + callback.onContentAttachmentsReady( + MultiPicker.get(MultiPicker.IMAGE) + .getSelectedFiles(context, data) + .map { it.toContentAttachmentData() } + ) + } + + fun onPhotoResult() { + captureUri?.let { captureUri -> + MultiPicker.get(MultiPicker.CAMERA) + .getTakenPhoto(context, captureUri) + ?.let { + callback.onContentAttachmentsReady( + listOf(it).map { it.toContentAttachmentData() } + ) } - } - MultiPicker.REQUEST_CODE_PICK_VIDEO -> { - callback.onContentAttachmentsReady( - MultiPicker.get(MultiPicker.VIDEO) - .getSelectedFiles(context, requestCode, resultCode, data) - .map { it.toContentAttachmentData() } - ) - } - else -> return false - } - return true } - return false + } + + fun onVideoResult(data: Intent?) { + callback.onContentAttachmentsReady( + MultiPicker.get(MultiPicker.VIDEO) + .getSelectedFiles(context, data) + .map { it.toContentAttachmentData() } + ) } /** diff --git a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentPreviewItems.kt b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentPreviewItems.kt index 066f69b555..f715d0cb3f 100644 --- a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentPreviewItems.kt +++ b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentPreviewItems.kt @@ -55,8 +55,10 @@ abstract class AttachmentPreviewItem : VectorE abstract class AttachmentMiniaturePreviewItem : AttachmentPreviewItem() { @EpoxyAttribute override lateinit var attachment: ContentAttachmentData + @EpoxyAttribute var clickListener: View.OnClickListener? = null + @EpoxyAttribute var checked: Boolean = false diff --git a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewAction.kt b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewAction.kt index 16f6fe2b97..4364300c3a 100644 --- a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewAction.kt +++ b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewAction.kt @@ -22,6 +22,6 @@ import im.vector.app.core.platform.VectorViewModelAction sealed class AttachmentsPreviewAction : VectorViewModelAction { object RemoveCurrentAttachment : AttachmentsPreviewAction() - data class SetCurrentAttachment(val index: Int): AttachmentsPreviewAction() - data class UpdatePathOfCurrentAttachment(val newUri: Uri): AttachmentsPreviewAction() + data class SetCurrentAttachment(val index: Int) : AttachmentsPreviewAction() + data class UpdatePathOfCurrentAttachment(val newUri: Uri) : AttachmentsPreviewAction() } diff --git a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewActivity.kt b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewActivity.kt index bfa44a7a70..8e830d00a4 100644 --- a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewActivity.kt +++ b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewActivity.kt @@ -30,8 +30,6 @@ import org.matrix.android.sdk.api.session.content.ContentAttachmentData class AttachmentsPreviewActivity : VectorBaseActivity(), ToolbarConfigurable { companion object { - const val REQUEST_CODE = 55 - private const val EXTRA_FRAGMENT_ARGS = "EXTRA_FRAGMENT_ARGS" private const val ATTACHMENTS_PREVIEW_RESULT = "ATTACHMENTS_PREVIEW_RESULT" private const val KEEP_ORIGINAL_IMAGES_SIZE = "KEEP_ORIGINAL_IMAGES_SIZE" diff --git a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt index 387cfb2261..b040101c84 100644 --- a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt @@ -81,6 +81,10 @@ class AttachmentsPreviewFragment @Inject constructor( } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + // TODO handle this one (Ucrop lib) + @Suppress("DEPRECATION") + super.onActivityResult(requestCode, resultCode, data) + if (resultCode == RESULT_OK) { if (requestCode == UCrop.REQUEST_CROP && data != null) { Timber.v("Crop success") @@ -98,7 +102,7 @@ class AttachmentsPreviewFragment @Inject constructor( handleRemoveAction() true } - R.id.attachmentsPreviewEditAction -> { + R.id.attachmentsPreviewEditAction -> { handleEditAction() true } diff --git a/vector/src/main/java/im/vector/app/features/autocomplete/RecyclerViewPresenter.kt b/vector/src/main/java/im/vector/app/features/autocomplete/RecyclerViewPresenter.kt index e7ece3bb9e..c4631300f1 100644 --- a/vector/src/main/java/im/vector/app/features/autocomplete/RecyclerViewPresenter.kt +++ b/vector/src/main/java/im/vector/app/features/autocomplete/RecyclerViewPresenter.kt @@ -51,6 +51,7 @@ abstract class RecyclerViewPresenter(context: Context?) : AutocompletePresent } override fun onViewShown() {} + @CallSuper override fun onViewHidden() { observer?.also { diff --git a/vector/src/main/java/im/vector/app/features/autocomplete/command/AutocompleteCommandItem.kt b/vector/src/main/java/im/vector/app/features/autocomplete/command/AutocompleteCommandItem.kt index 257a2abd6c..0f24b866bf 100644 --- a/vector/src/main/java/im/vector/app/features/autocomplete/command/AutocompleteCommandItem.kt +++ b/vector/src/main/java/im/vector/app/features/autocomplete/command/AutocompleteCommandItem.kt @@ -29,10 +29,13 @@ abstract class AutocompleteCommandItem : VectorEpoxyModel, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == CAPTURE_PERMISSION_REQUEST_CODE && allGranted(grantResults)) { start() } else { diff --git a/vector/src/main/java/im/vector/app/features/call/WebRtcPeerConnectionManager.kt b/vector/src/main/java/im/vector/app/features/call/WebRtcPeerConnectionManager.kt index 6d3f8b5885..c70b52b09b 100644 --- a/vector/src/main/java/im/vector/app/features/call/WebRtcPeerConnectionManager.kt +++ b/vector/src/main/java/im/vector/app/features/call/WebRtcPeerConnectionManager.kt @@ -183,7 +183,7 @@ class WebRtcPeerConnectionManager @Inject constructor( fun addIfNeeded(renderer: SurfaceViewRenderer?, list: MutableList>) { if (renderer == null) return - val exists = list.firstOrNull() { + val exists = list.firstOrNull { it.get() == renderer } != null if (!exists) { diff --git a/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt b/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt index 5a4c25a8ad..1ab6fb6363 100644 --- a/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt @@ -150,6 +150,7 @@ class VectorJitsiActivity : VectorBaseActivity(), JitsiMeetActivityInterface, Ji } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) JitsiMeetActivityDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults) } diff --git a/vector/src/main/java/im/vector/app/features/command/ParsedCommand.kt b/vector/src/main/java/im/vector/app/features/command/ParsedCommand.kt index bdfa7779fb..2f8531929a 100644 --- a/vector/src/main/java/im/vector/app/features/command/ParsedCommand.kt +++ b/vector/src/main/java/im/vector/app/features/command/ParsedCommand.kt @@ -54,5 +54,5 @@ sealed class ParsedCommand { class SendSpoiler(val message: String) : ParsedCommand() class SendShrug(val message: CharSequence) : ParsedCommand() class SendPoll(val question: String, val options: List) : ParsedCommand() - object DiscardSession: ParsedCommand() + object DiscardSession : ParsedCommand() } diff --git a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt index dd449de989..c4cf9eab39 100644 --- a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt +++ b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt @@ -33,9 +33,9 @@ import im.vector.app.features.userdirectory.UserDirectoryAction import im.vector.app.features.userdirectory.UserDirectorySharedAction import im.vector.app.features.userdirectory.UserDirectorySharedActionViewModel import im.vector.app.features.userdirectory.UserDirectoryViewModel +import kotlinx.android.synthetic.main.fragment_contacts_book.* import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.user.model.User -import kotlinx.android.synthetic.main.fragment_contacts_book.* import java.util.concurrent.TimeUnit import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt index d9b136691e..167660d11e 100644 --- a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt @@ -33,18 +33,18 @@ import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.createdirect.CreateDirectRoomActivity import im.vector.app.features.invite.InviteUsersToRoomActivity +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.identity.FoundThreePid import org.matrix.android.sdk.api.session.identity.ThreePid -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import timber.log.Timber private typealias PhoneBookSearch = String class ContactsBookViewModel @AssistedInject constructor(@Assisted - initialState: ContactsBookViewState, + initialState: ContactsBookViewState, private val contactsDataSource: ContactsDataSource, private val session: Session) : VectorViewModel(initialState) { diff --git a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt index 27ca636142..a1bb12a84b 100644 --- a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt +++ b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt @@ -111,9 +111,10 @@ class CreateDirectRoomActivity : SimpleFragmentActivity() { } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (allGranted(grantResults)) { if (requestCode == PERMISSION_REQUEST_CODE_READ_CONTACTS) { - doOnPostResume { addFragmentToBackstack(R.id.container, ContactsBookFragment::class.java) } + doOnPostResume { addFragmentToBackstack(R.id.container, ContactsBookFragment::class.java) } } } } @@ -139,7 +140,7 @@ class CreateDirectRoomActivity : SimpleFragmentActivity() { private fun renderCreationFailure(error: Throwable) { hideWaitingView() when (error) { - is CreateRoomFailure.CreatedWithTimeout -> { + is CreateRoomFailure.CreatedWithTimeout -> { finish() } is CreateRoomFailure.CreatedWithFederationFailure -> { diff --git a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt index 56114cdb4b..c42e10686f 100644 --- a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt @@ -70,7 +70,7 @@ class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted .apply { invitees.forEach { when (it) { - is PendingInvitee.UserPendingInvitee -> invitedUserIds.add(it.user.userId) + is PendingInvitee.UserPendingInvitee -> invitedUserIds.add(it.user.userId) is PendingInvitee.ThreePidPendingInvitee -> invite3pids.add(it.threePid) }.exhaustive } diff --git a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewState.kt b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewState.kt index 953d415140..27a8cc0a97 100644 --- a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewState.kt +++ b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewState.kt @@ -21,5 +21,5 @@ import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized data class CreateDirectRoomViewState( - val createAndInviteState: Async = Uninitialized + val createAndInviteState: Async = Uninitialized ) : MvRxState diff --git a/vector/src/main/java/im/vector/app/features/crypto/keys/KeysExporter.kt b/vector/src/main/java/im/vector/app/features/crypto/keys/KeysExporter.kt index d35d031286..282f7b1a71 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keys/KeysExporter.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keys/KeysExporter.kt @@ -18,14 +18,14 @@ package im.vector.app.features.crypto.keys import android.content.Context import android.net.Uri -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.internal.extensions.foldToCallback -import org.matrix.android.sdk.internal.util.awaitCallback import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import org.matrix.android.sdk.api.MatrixCallback +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.internal.extensions.foldToCallback +import org.matrix.android.sdk.internal.util.awaitCallback class KeysExporter(private val session: Session) { diff --git a/vector/src/main/java/im/vector/app/features/crypto/keys/KeysImporter.kt b/vector/src/main/java/im/vector/app/features/crypto/keys/KeysImporter.kt index d2227841b5..8932bb9489 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keys/KeysImporter.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keys/KeysImporter.kt @@ -20,15 +20,15 @@ import android.content.Context import android.net.Uri import im.vector.app.core.intent.getMimeTypeFromUri import im.vector.app.core.resources.openResource +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult import org.matrix.android.sdk.internal.extensions.foldToCallback import org.matrix.android.sdk.internal.util.awaitCallback -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import timber.log.Timber class KeysImporter(private val session: Session) { diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt index 40953cb5f6..80ae46262d 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt @@ -23,6 +23,7 @@ import androidx.lifecycle.Observer import im.vector.app.R import im.vector.app.core.extensions.addFragmentToBackstack import im.vector.app.core.extensions.observeEvent +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.replaceFragment import im.vector.app.core.platform.SimpleFragmentActivity import im.vector.app.core.ui.views.KeysBackupBanner @@ -32,8 +33,6 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_S class KeysBackupRestoreActivity : SimpleFragmentActivity() { companion object { - - private const val REQUEST_4S_SECRET = 100 const val SECRET_ALIAS = SharedSecureStorageActivity.DEFAULT_RESULT_KEYSTORE_ALIAS fun intent(context: Context): Intent { @@ -130,22 +129,19 @@ class KeysBackupRestoreActivity : SimpleFragmentActivity() { requestedSecrets = listOf(KEYBACKUP_SECRET_SSSS_NAME), resultKeyStoreAlias = SECRET_ALIAS ).let { - startActivityForResult(it, REQUEST_4S_SECRET) + secretStartForActivityResult.launch(it) } } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (requestCode == REQUEST_4S_SECRET) { - val extraResult = data?.getStringExtra(SharedSecureStorageActivity.EXTRA_DATA_RESULT) - if (resultCode == Activity.RESULT_OK && extraResult != null) { - viewModel.handleGotSecretFromSSSS( - extraResult, - SECRET_ALIAS - ) - } else { - finish() - } + private val secretStartForActivityResult = registerStartForActivityResult { activityResult -> + val extraResult = activityResult.data?.getStringExtra(SharedSecureStorageActivity.EXTRA_DATA_RESULT) + if (activityResult.resultCode == Activity.RESULT_OK && extraResult != null) { + viewModel.handleGotSecretFromSSSS( + extraResult, + SECRET_ALIAS + ) + } else { + finish() } - super.onActivityResult(requestCode, resultCode, data) } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt index cc10256fc4..580a3443e7 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt @@ -16,9 +16,9 @@ package im.vector.app.features.crypto.keysbackup.restore import android.app.Activity -import android.content.Intent import android.os.Bundle import android.text.Editable +import android.view.View import android.view.inputmethod.EditorInfo import android.widget.EditText import androidx.lifecycle.Observer @@ -27,19 +27,15 @@ import butterknife.OnClick import butterknife.OnTextChanged import com.google.android.material.textfield.TextInputLayout import im.vector.app.R +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.startImportTextFromFileIntent -import timber.log.Timber +import org.matrix.android.sdk.api.extensions.tryOrNull import javax.inject.Inject class KeysBackupRestoreFromKeyFragment @Inject constructor() : VectorBaseFragment() { - companion object { - - private const val REQUEST_TEXT_FILE_GET = 1 - } - override fun getLayoutResId() = R.layout.fragment_keys_backup_restore_from_key private lateinit var viewModel: KeysBackupRestoreFromKeyViewModel @@ -47,11 +43,12 @@ class KeysBackupRestoreFromKeyFragment @Inject constructor() @BindView(R.id.keys_backup_key_enter_til) lateinit var mKeyInputLayout: TextInputLayout + @BindView(R.id.keys_restore_key_enter_edittext) lateinit var mKeyTextEdit: EditText - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) viewModel = fragmentViewModelProvider.get(KeysBackupRestoreFromKeyViewModel::class.java) sharedViewModel = activityViewModelProvider.get(KeysBackupRestoreSharedViewModel::class.java) mKeyTextEdit.setText(viewModel.recoveryCode.value) @@ -88,29 +85,23 @@ class KeysBackupRestoreFromKeyFragment @Inject constructor() @OnClick(R.id.keys_backup_import) fun onImport() { - startImportTextFromFileIntent(this, REQUEST_TEXT_FILE_GET) + startImportTextFromFileIntent(requireContext(), textFileStartForActivityResult) } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (requestCode == REQUEST_TEXT_FILE_GET && resultCode == Activity.RESULT_OK) { - val dataURI = data?.data - if (dataURI != null) { - try { - activity - ?.contentResolver - ?.openInputStream(dataURI) - ?.bufferedReader() - ?.use { it.readText() } - ?.let { - mKeyTextEdit.setText(it) - mKeyTextEdit.setSelection(it.length) - } - } catch (e: Exception) { - Timber.e(e, "Failed to read recovery kay from text") - } + private val textFileStartForActivityResult = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + val dataURI = activityResult.data?.data ?: return@registerStartForActivityResult + tryOrNull(message = "Failed to read recovery kay from text") { + activity + ?.contentResolver + ?.openInputStream(dataURI) + ?.bufferedReader() + ?.use { it.readText() } + ?.let { + mKeyTextEdit.setText(it) + mKeyTextEdit.setSelection(it.length) + } } - return } - super.onActivityResult(requestCode, resultCode, data) } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt index 7941d95add..79aa728da1 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt @@ -59,8 +59,8 @@ class KeysBackupRestoreFromPassphraseFragment @Inject constructor() : VectorBase viewModel.showPasswordMode.value = !(viewModel.showPasswordMode.value ?: false) } - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) viewModel = fragmentViewModelProvider.get(KeysBackupRestoreFromPassphraseViewModel::class.java) sharedViewModel = activityViewModelProvider.get(KeysBackupRestoreSharedViewModel::class.java) diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSharedViewModel.kt index 7d2d74db2f..d2cf871701 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSharedViewModel.kt @@ -23,6 +23,8 @@ import im.vector.app.R import im.vector.app.core.platform.WaitingViewData import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.LiveEvent +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.listeners.StepProgressListener import org.matrix.android.sdk.api.session.Session @@ -34,8 +36,6 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionR import org.matrix.android.sdk.internal.crypto.keysbackup.util.computeRecoveryKey import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult import org.matrix.android.sdk.internal.util.awaitCallback -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt index fa571b86c1..b4969d28b7 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt @@ -16,6 +16,7 @@ package im.vector.app.features.crypto.keysbackup.restore import android.os.Bundle +import android.view.View import android.widget.TextView import androidx.core.view.isVisible import butterknife.BindView @@ -31,13 +32,14 @@ class KeysBackupRestoreSuccessFragment @Inject constructor() : VectorBaseFragmen @BindView(R.id.keys_backup_restore_success) lateinit var mSuccessText: TextView + @BindView(R.id.keys_backup_restore_success_info) lateinit var mSuccessDetailsText: TextView private lateinit var sharedViewModel: KeysBackupRestoreSharedViewModel - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) sharedViewModel = activityViewModelProvider.get(KeysBackupRestoreSharedViewModel::class.java) if (compareValues(sharedViewModel.importKeyResult?.totalNumberOfKeys, 0) > 0) { diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt index 1bba2c3a39..ab8e725959 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt @@ -26,6 +26,7 @@ import im.vector.app.R import im.vector.app.core.dialogs.ExportKeysDialog import im.vector.app.core.extensions.observeEvent import im.vector.app.core.extensions.queryExportKeys +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.replaceFragment import im.vector.app.core.platform.SimpleFragmentActivity import im.vector.app.core.utils.toast @@ -93,7 +94,7 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() { .show() } KeysBackupSetupSharedViewModel.NAVIGATE_MANUAL_EXPORT -> { - queryExportKeys(session.myUserId, REQUEST_CODE_SAVE_MEGOLM_EXPORT) + queryExportKeys(session.myUserId, saveStartForActivityResult) } } } @@ -125,10 +126,10 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() { }) } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (requestCode == REQUEST_CODE_SAVE_MEGOLM_EXPORT) { - val uri = data?.data - if (resultCode == Activity.RESULT_OK && uri != null) { + private val saveStartForActivityResult = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + val uri = activityResult.data?.data + if (uri != null) { ExportKeysDialog().show(this, object : ExportKeysDialog.ExportKeyDialogListener { override fun onPassphrase(passphrase: String) { showWaitingView() @@ -163,7 +164,6 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() { hideWaitingView() } } - super.onActivityResult(requestCode, resultCode, data) } override fun onBackPressed() { @@ -198,7 +198,6 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() { const val KEYS_VERSION = "KEYS_VERSION" const val MANUAL_EXPORT = "MANUAL_EXPORT" const val EXTRA_SHOW_MANUAL_EXPORT = "SHOW_MANUAL_EXPORT" - const val REQUEST_CODE_SAVE_MEGOLM_EXPORT = 101 fun intent(context: Context, showManualExport: Boolean): Intent { val intent = Intent(context, KeysBackupSetupActivity::class.java) diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt index 0a82edd150..f855b86b6c 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt @@ -40,8 +40,8 @@ class KeysBackupSetupStep1Fragment @Inject constructor() : VectorBaseFragment() @BindView(R.id.keys_backup_setup_step1_manualExport) lateinit var manualExportButton: Button - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) viewModel = activityViewModelProvider.get(KeysBackupSetupSharedViewModel::class.java) diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep2Fragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep2Fragment.kt index 0520098866..af279c9140 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep2Fragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep2Fragment.kt @@ -16,6 +16,7 @@ package im.vector.app.features.crypto.keysbackup.setup import android.os.Bundle +import android.view.View import android.view.ViewGroup import android.view.inputmethod.EditorInfo import android.widget.EditText @@ -77,8 +78,8 @@ class KeysBackupSetupStep2Fragment @Inject constructor() : VectorBaseFragment() private lateinit var viewModel: KeysBackupSetupSharedViewModel - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) viewModel = activityViewModelProvider.get(KeysBackupSetupSharedViewModel::class.java) diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt index 41b7cbddfe..78471c6848 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt @@ -16,7 +16,6 @@ package im.vector.app.features.crypto.keysbackup.setup import android.app.Activity -import android.content.Intent import android.net.Uri import android.os.Bundle import android.view.View @@ -31,6 +30,7 @@ import butterknife.BindView import butterknife.OnClick import com.google.android.material.bottomsheet.BottomSheetDialog import im.vector.app.R +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.LiveEvent import im.vector.app.core.utils.copyToClipboard @@ -48,10 +48,6 @@ import javax.inject.Inject class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment() { - companion object { - private const val SAVE_RECOVERY_KEY_REQUEST_CODE = 2754 - } - override fun getLayoutResId() = R.layout.fragment_keys_backup_setup_step3 @BindView(R.id.keys_backup_setup_step3_button) @@ -65,8 +61,8 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment() private lateinit var viewModel: KeysBackupSetupSharedViewModel - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) viewModel = activityViewModelProvider.get(KeysBackupSetupSharedViewModel::class.java) viewModel.shouldPromptOnBack = false @@ -138,19 +134,20 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment() val timestamp = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date()) selectTxtFileToWrite( activity = requireActivity(), - fragment = this, + activityResultLauncher = saveRecoveryActivityResultLauncher, defaultFileName = "recovery-key-$userId-$timestamp.txt", - chooserHint = getString(R.string.save_recovery_key_chooser_hint), - requestCode = SAVE_RECOVERY_KEY_REQUEST_CODE + chooserHint = getString(R.string.save_recovery_key_chooser_hint) ) dialog.dismiss() } dialog.findViewById(R.id.keys_backup_setup_share)?.setOnClickListener { - startSharePlainTextIntent(this, - context?.getString(R.string.keys_backup_setup_step3_share_intent_chooser_title), - recoveryKey, - context?.getString(R.string.recovery_key)) + startSharePlainTextIntent( + fragment = this, + activityResultLauncher = null, + chooserTitle = context?.getString(R.string.keys_backup_setup_step3_share_intent_chooser_title), + text = recoveryKey, + subject = context?.getString(R.string.recovery_key)) viewModel.copyHasBeenMade = true dialog.dismiss() } @@ -202,15 +199,11 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment() } } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - when (requestCode) { - SAVE_RECOVERY_KEY_REQUEST_CODE -> { - val uri = data?.data - if (resultCode == Activity.RESULT_OK && uri != null) { - viewModel.recoveryKey.value?.let { - exportRecoveryKeyToFile(uri, it) - } - } + private val saveRecoveryActivityResultLauncher = registerStartForActivityResult { activityRessult -> + val uri = activityRessult.data?.data ?: return@registerStartForActivityResult + if (activityRessult.resultCode == Activity.RESULT_OK) { + viewModel.recoveryKey.value?.let { + exportRecoveryKeyToFile(uri, it) } } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysrequest/KeyRequestHandler.kt b/vector/src/main/java/im/vector/app/features/crypto/keysrequest/KeyRequestHandler.kt index 4ed0e037d4..8a02b8deac 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysrequest/KeyRequestHandler.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysrequest/KeyRequestHandler.kt @@ -1,7 +1,4 @@ /* - * Copyright 2016 OpenMarket Ltd - * Copyright 2017 Vector Creations Ltd - * Copyright 2018 New Vector Ltd * Copyright 2019 New Vector Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -126,7 +123,7 @@ class KeyRequestHandler @Inject constructor( // can we get more info on this device? session?.cryptoService()?.getMyDevicesInfo()?.firstOrNull { it.deviceId == deviceId }?.let { postAlert(context, userId, deviceId, true, deviceInfo, it) - } ?: kotlin.run { + } ?: run { postAlert(context, userId, deviceId, true, deviceInfo) } } else { diff --git a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageAction.kt b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageAction.kt index b47b7dc3a9..30a7ab3cc0 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageAction.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageAction.kt @@ -28,6 +28,8 @@ sealed class SharedSecureStorageAction : VectorViewModelAction { object Cancel : SharedSecureStorageAction() data class SubmitPassphrase(val passphrase: String) : SharedSecureStorageAction() data class SubmitKey(val recoveryKey: String) : SharedSecureStorageAction() + object ForgotResetAll : SharedSecureStorageAction() + object DoResetAll : SharedSecureStorageAction() } sealed class SharedSecureStorageViewEvent : VectorViewEvents { @@ -40,4 +42,5 @@ sealed class SharedSecureStorageViewEvent : VectorViewEvents { object ShowModalLoading : SharedSecureStorageViewEvent() object HideModalLoading : SharedSecureStorageViewEvent() data class UpdateLoadingState(val waitingData: WaitingViewData) : SharedSecureStorageViewEvent() + object ShowResetBottomSheet : SharedSecureStorageViewEvent() } diff --git a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageActivity.kt b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageActivity.kt index bca7a63470..2abad9dd38 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageActivity.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageActivity.kt @@ -24,6 +24,8 @@ import android.os.Parcelable import android.view.View import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import androidx.fragment.app.FragmentOnAttachListener import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.viewModel import im.vector.app.R @@ -31,12 +33,17 @@ import im.vector.app.core.di.ScreenComponent import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.extensions.commitTransaction import im.vector.app.core.platform.SimpleFragmentActivity +import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment +import im.vector.app.features.crypto.recover.SetupMode import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.activity.* import javax.inject.Inject import kotlin.reflect.KClass -class SharedSecureStorageActivity : SimpleFragmentActivity() { +class SharedSecureStorageActivity : + SimpleFragmentActivity(), + VectorBaseBottomSheetDialogFragment.ResultListener, + FragmentOnAttachListener { @Parcelize data class Args( @@ -56,6 +63,8 @@ class SharedSecureStorageActivity : SimpleFragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + supportFragmentManager.addFragmentOnAttachListener(this) + toolbar.visibility = View.GONE viewModel.observeViewEvents { observeViewEvents(it) } @@ -63,24 +72,33 @@ class SharedSecureStorageActivity : SimpleFragmentActivity() { viewModel.subscribe(this) { renderState(it) } } + override fun onDestroy() { + super.onDestroy() + supportFragmentManager.removeFragmentOnAttachListener(this) + } + override fun onBackPressed() { viewModel.handle(SharedSecureStorageAction.Back) } private fun renderState(state: SharedSecureStorageViewState) { if (!state.ready) return - val fragment = if (state.hasPassphrase) { - if (state.useKey) SharedSecuredStorageKeyFragment::class else SharedSecuredStoragePassphraseFragment::class - } else SharedSecuredStorageKeyFragment::class + val fragment = + when (state.step) { + SharedSecureStorageViewState.Step.EnterPassphrase -> SharedSecuredStoragePassphraseFragment::class + SharedSecureStorageViewState.Step.EnterKey -> SharedSecuredStorageKeyFragment::class + SharedSecureStorageViewState.Step.ResetAll -> SharedSecuredStorageResetAllFragment::class + } + showFragment(fragment, Bundle()) } private fun observeViewEvents(it: SharedSecureStorageViewEvent?) { when (it) { - is SharedSecureStorageViewEvent.Dismiss -> { + is SharedSecureStorageViewEvent.Dismiss -> { finish() } - is SharedSecureStorageViewEvent.Error -> { + is SharedSecureStorageViewEvent.Error -> { AlertDialog.Builder(this) .setTitle(getString(R.string.dialog_title_error)) .setMessage(it.message) @@ -92,21 +110,30 @@ class SharedSecureStorageActivity : SimpleFragmentActivity() { } .show() } - is SharedSecureStorageViewEvent.ShowModalLoading -> { + is SharedSecureStorageViewEvent.ShowModalLoading -> { showWaitingView() } - is SharedSecureStorageViewEvent.HideModalLoading -> { + is SharedSecureStorageViewEvent.HideModalLoading -> { hideWaitingView() } - is SharedSecureStorageViewEvent.UpdateLoadingState -> { + is SharedSecureStorageViewEvent.UpdateLoadingState -> { updateWaitingView(it.waitingData) } - is SharedSecureStorageViewEvent.FinishSuccess -> { + is SharedSecureStorageViewEvent.FinishSuccess -> { val dataResult = Intent() dataResult.putExtra(EXTRA_DATA_RESULT, it.cypherResult) setResult(Activity.RESULT_OK, dataResult) finish() } + is SharedSecureStorageViewEvent.ShowResetBottomSheet -> { + navigator.open4SSetup(this, SetupMode.HARD_RESET) + } + } + } + + override fun onAttachFragment(fragmentManager: FragmentManager, fragment: Fragment) { + if (fragment is VectorBaseBottomSheetDialogFragment) { + fragment.resultListener = this } } @@ -124,6 +151,7 @@ class SharedSecureStorageActivity : SimpleFragmentActivity() { companion object { const val EXTRA_DATA_RESULT = "EXTRA_DATA_RESULT" + const val EXTRA_DATA_RESET = "EXTRA_DATA_RESET" const val DEFAULT_RESULT_KEYSTORE_ALIAS = "SharedSecureStorageActivity" fun newIntent(context: Context, @@ -140,4 +168,12 @@ class SharedSecureStorageActivity : SimpleFragmentActivity() { } } } + + override fun onBottomSheetResult(resultCode: Int, data: Any?) { + if (resultCode == VectorBaseBottomSheetDialogFragment.ResultListener.RESULT_OK) { + // the 4S has been reset + setResult(Activity.RESULT_OK, Intent().apply { putExtra(EXTRA_DATA_RESET, true) }) + finish() + } + } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt index 5bc87dfce7..951ff4ede6 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt @@ -33,6 +33,9 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.WaitingViewData import im.vector.app.core.resources.StringProvider +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.listeners.ProgressListener import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.securestorage.IntegrityResult @@ -40,19 +43,26 @@ import org.matrix.android.sdk.api.session.securestorage.KeyInfoResult import org.matrix.android.sdk.api.session.securestorage.RawBytesKeySpec import org.matrix.android.sdk.internal.crypto.crosssigning.toBase64NoPadding import org.matrix.android.sdk.internal.util.awaitCallback -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext +import org.matrix.android.sdk.rx.rx import timber.log.Timber import java.io.ByteArrayOutputStream data class SharedSecureStorageViewState( val ready: Boolean = false, val hasPassphrase: Boolean = true, - val useKey: Boolean = false, val passphraseVisible: Boolean = false, - val checkingSSSSAction: Async = Uninitialized -) : MvRxState + val checkingSSSSAction: Async = Uninitialized, + val step: Step = Step.EnterPassphrase, + val activeDeviceCount: Int = 0, + val showResetAllAction: Boolean = false, + val userId: String = "" +) : MvRxState { + enum class Step { + EnterPassphrase, + EnterKey, + ResetAll + } +} class SharedSecureStorageViewModel @AssistedInject constructor( @Assisted initialState: SharedSecureStorageViewState, @@ -67,6 +77,10 @@ class SharedSecureStorageViewModel @AssistedInject constructor( } init { + + setState { + copy(userId = session.myUserId) + } val isValid = session.sharedSecretStorageService.checkShouldBeAbleToAccessSecrets(args.requestedSecrets, args.keyId) is IntegrityResult.Success if (!isValid) { _viewEvents.post( @@ -86,20 +100,30 @@ class SharedSecureStorageViewModel @AssistedInject constructor( if (info.content.passphrase != null) { setState { copy( - ready = true, hasPassphrase = true, - useKey = false + ready = true, + step = SharedSecureStorageViewState.Step.EnterPassphrase ) } } else { setState { copy( + hasPassphrase = false, ready = true, - hasPassphrase = false + step = SharedSecureStorageViewState.Step.EnterKey ) } } } + + session.rx() + .liveUserCryptoDevices(session.myUserId) + .distinctUntilChanged() + .execute { + copy( + activeDeviceCount = it.invoke()?.size ?: 0 + ) + } } override fun handle(action: SharedSecureStorageAction) = withState { @@ -110,27 +134,52 @@ class SharedSecureStorageViewModel @AssistedInject constructor( SharedSecureStorageAction.UseKey -> handleUseKey() is SharedSecureStorageAction.SubmitKey -> handleSubmitKey(action) SharedSecureStorageAction.Back -> handleBack() + SharedSecureStorageAction.ForgotResetAll -> handleResetAll() + SharedSecureStorageAction.DoResetAll -> handleDoResetAll() }.exhaustive } + private fun handleDoResetAll() { + _viewEvents.post(SharedSecureStorageViewEvent.ShowResetBottomSheet) + } + + private fun handleResetAll() { + setState { + copy( + step = SharedSecureStorageViewState.Step.ResetAll + ) + } + } + private fun handleUseKey() { setState { copy( - useKey = true + step = SharedSecureStorageViewState.Step.EnterKey ) } } private fun handleBack() = withState { state -> if (state.checkingSSSSAction is Loading) return@withState // ignore - if (state.hasPassphrase && state.useKey) { - setState { - copy( - useKey = false - ) + when (state.step) { + SharedSecureStorageViewState.Step.EnterKey -> { + setState { + copy( + step = SharedSecureStorageViewState.Step.EnterPassphrase + ) + } + } + SharedSecureStorageViewState.Step.ResetAll -> { + setState { + copy( + step = if (state.hasPassphrase) SharedSecureStorageViewState.Step.EnterPassphrase + else SharedSecureStorageViewState.Step.EnterKey + ) + } + } + else -> { + _viewEvents.post(SharedSecureStorageViewEvent.Dismiss) } - } else { - _viewEvents.post(SharedSecureStorageViewEvent.Dismiss) } } @@ -158,6 +207,7 @@ class SharedSecureStorageViewModel @AssistedInject constructor( val keySpec = RawBytesKeySpec.fromRecoveryKey(recoveryKey) ?: return@launch Unit.also { _viewEvents.post(SharedSecureStorageViewEvent.KeyInlineError(stringProvider.getString(R.string.bootstrap_invalid_recovery_key))) _viewEvents.post(SharedSecureStorageViewEvent.HideModalLoading) + setState { copy(checkingSSSSAction = Fail(IllegalArgumentException(stringProvider.getString(R.string.bootstrap_invalid_recovery_key)))) } } withContext(Dispatchers.IO) { diff --git a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecuredStorageKeyFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecuredStorageKeyFragment.kt index ee47a4d8e9..9fb3f637e1 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecuredStorageKeyFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecuredStorageKeyFragment.kt @@ -17,7 +17,6 @@ package im.vector.app.features.crypto.quads import android.app.Activity -import android.content.Intent import android.os.Bundle import android.view.View import android.view.inputmethod.EditorInfo @@ -25,11 +24,12 @@ import com.airbnb.mvrx.activityViewModel import com.jakewharton.rxbinding3.widget.editorActionEvents import com.jakewharton.rxbinding3.widget.textChanges import im.vector.app.R +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.startImportTextFromFileIntent -import org.matrix.android.sdk.api.extensions.tryOrNull import io.reactivex.android.schedulers.AndroidSchedulers import kotlinx.android.synthetic.main.fragment_ssss_access_from_key.* +import org.matrix.android.sdk.api.extensions.tryOrNull import java.util.concurrent.TimeUnit import javax.inject.Inject @@ -61,7 +61,11 @@ class SharedSecuredStorageKeyFragment @Inject constructor() : VectorBaseFragment } .disposeOnDestroyView() - ssss_key_use_file.debouncedClicks { startImportTextFromFileIntent(this, IMPORT_FILE_REQ) } + ssss_key_use_file.debouncedClicks { startImportTextFromFileIntent(requireContext(), importFileStartForActivityResult) } + + ssss_key_reset.clickableView.debouncedClicks { + sharedViewModel.handle(SharedSecureStorageAction.ForgotResetAll) + } sharedViewModel.observeViewEvents { when (it) { @@ -81,9 +85,9 @@ class SharedSecuredStorageKeyFragment @Inject constructor() : VectorBaseFragment sharedViewModel.handle(SharedSecureStorageAction.SubmitKey(text)) } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (requestCode == IMPORT_FILE_REQ && resultCode == Activity.RESULT_OK) { - data?.data?.let { dataURI -> + private val importFileStartForActivityResult = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + activityResult.data?.data?.let { dataURI -> tryOrNull { activity?.contentResolver?.openInputStream(dataURI) ?.bufferedReader() @@ -93,12 +97,6 @@ class SharedSecuredStorageKeyFragment @Inject constructor() : VectorBaseFragment } } } - return } - super.onActivityResult(requestCode, resultCode, data) - } - - companion object { - private const val IMPORT_FILE_REQ = 0 } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecuredStoragePassphraseFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecuredStoragePassphraseFragment.kt index 09e67948d0..97047fbc65 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecuredStoragePassphraseFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecuredStoragePassphraseFragment.kt @@ -74,6 +74,10 @@ class SharedSecuredStoragePassphraseFragment @Inject constructor( } .disposeOnDestroyView() + ssss_passphrase_reset.clickableView.debouncedClicks { + sharedViewModel.handle(SharedSecureStorageAction.ForgotResetAll) + } + sharedViewModel.observeViewEvents { when (it) { is SharedSecureStorageViewEvent.InlineError -> { diff --git a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecuredStorageResetAllFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecuredStorageResetAllFragment.kt new file mode 100644 index 0000000000..d7db779230 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecuredStorageResetAllFragment.kt @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.crypto.quads + +import android.os.Bundle +import android.view.View +import com.airbnb.mvrx.activityViewModel +import com.airbnb.mvrx.withState +import im.vector.app.R +import im.vector.app.core.extensions.setTextOrHide +import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.features.roommemberprofile.devices.DeviceListBottomSheet +import kotlinx.android.synthetic.main.fragment_ssss_reset_all.* +import javax.inject.Inject + +class SharedSecuredStorageResetAllFragment @Inject constructor() : VectorBaseFragment() { + + override fun getLayoutResId() = R.layout.fragment_ssss_reset_all + + val sharedViewModel: SharedSecureStorageViewModel by activityViewModel() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + ssss_reset_button_reset.debouncedClicks { + sharedViewModel.handle(SharedSecureStorageAction.DoResetAll) + } + + ssss_reset_button_cancel.debouncedClicks { + sharedViewModel.handle(SharedSecureStorageAction.Back) + } + + ssss_reset_other_devices.debouncedClicks { + withState(sharedViewModel) { + DeviceListBottomSheet.newInstance(it.userId, false).show(childFragmentManager, "DEV_LIST") + } + } + + sharedViewModel.subscribe(this) { state -> + ssss_reset_other_devices.setTextOrHide( + state.activeDeviceCount + .takeIf { it > 0 } + ?.let { resources.getQuantityString(R.plurals.secure_backup_reset_devices_you_can_verify, it, it) } + ) + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapBottomSheet.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapBottomSheet.kt index 1b9beabe9c..e6260b6e7e 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapBottomSheet.kt @@ -45,8 +45,7 @@ class BootstrapBottomSheet : VectorBaseBottomSheetDialogFragment() { @Parcelize data class Args( - val initCrossSigningOnly: Boolean, - val forceReset4S: Boolean + val setUpMode: SetupMode = SetupMode.NORMAL ) : Parcelable override val showExpanded = true @@ -66,7 +65,10 @@ class BootstrapBottomSheet : VectorBaseBottomSheetDialogFragment() { super.onViewCreated(view, savedInstanceState) viewModel.observeViewEvents { event -> when (event) { - is BootstrapViewEvents.Dismiss -> dismiss() + is BootstrapViewEvents.Dismiss -> { + bottomSheetResult = if (event.success) ResultListener.RESULT_OK else ResultListener.RESULT_CANCEL + dismiss() + } is BootstrapViewEvents.ModalError -> { AlertDialog.Builder(requireActivity()) .setTitle(R.string.dialog_title_error) @@ -90,6 +92,7 @@ class BootstrapBottomSheet : VectorBaseBottomSheetDialogFragment() { .setMessage(R.string.bootstrap_cancel_text) .setPositiveButton(R.string._continue, null) .setNegativeButton(R.string.skip) { _, _ -> + bottomSheetResult = ResultListener.RESULT_CANCEL dismiss() } .show() @@ -181,16 +184,15 @@ class BootstrapBottomSheet : VectorBaseBottomSheetDialogFragment() { const val EXTRA_ARGS = "EXTRA_ARGS" - fun show(fragmentManager: FragmentManager, initCrossSigningOnly: Boolean, forceReset4S: Boolean) { - BootstrapBottomSheet().apply { + fun show(fragmentManager: FragmentManager, mode: SetupMode): BootstrapBottomSheet { + return BootstrapBottomSheet().apply { isCancelable = false arguments = Bundle().apply { - this.putParcelable(EXTRA_ARGS, Args( - initCrossSigningOnly, - forceReset4S - )) + this.putParcelable(EXTRA_ARGS, Args(setUpMode = mode)) } - }.show(fragmentManager, "BootstrapBottomSheet") + }.also { + it.show(fragmentManager, "BootstrapBottomSheet") + } } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapCrossSigningTask.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapCrossSigningTask.kt index 5da788583e..b7c689f41f 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapCrossSigningTask.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapCrossSigningTask.kt @@ -69,10 +69,10 @@ interface BootstrapProgressListener { data class Params( val userPasswordAuth: UserPasswordAuth? = null, - val initOnlyCrossSigning: Boolean = false, val progressListener: BootstrapProgressListener? = null, val passphrase: String?, - val keySpec: SsssKeySpec? = null + val keySpec: SsssKeySpec? = null, + val setupMode: SetupMode ) // TODO Rename to CreateServerRecovery @@ -84,9 +84,13 @@ class BootstrapCrossSigningTask @Inject constructor( override suspend fun execute(params: Params): BootstrapResult { val crossSigningService = session.cryptoService().crossSigningService() - Timber.d("## BootstrapCrossSigningTask: initXSOnly:${params.initOnlyCrossSigning} Starting...") + Timber.d("## BootstrapCrossSigningTask: mode:${params.setupMode} Starting...") // Ensure cross-signing is initialized. Due to migration it is maybe not always correctly initialized - if (!crossSigningService.isCrossSigningInitialized()) { + + val shouldSetCrossSigning = !crossSigningService.isCrossSigningInitialized() + || (params.setupMode == SetupMode.PASSPHRASE_AND_NEEDED_SECRETS_RESET && !crossSigningService.allPrivateKeysKnown()) + || (params.setupMode == SetupMode.HARD_RESET) + if (shouldSetCrossSigning) { Timber.d("## BootstrapCrossSigningTask: Cross signing not enabled, so initialize") params.progressListener?.onProgress( WaitingViewData( @@ -99,7 +103,7 @@ class BootstrapCrossSigningTask @Inject constructor( awaitCallback { crossSigningService.initializeCrossSigning(params.userPasswordAuth, it) } - if (params.initOnlyCrossSigning) { + if (params.setupMode == SetupMode.CROSS_SIGNING_ONLY) { return BootstrapResult.SuccessCrossSigningOnly } } catch (failure: Throwable) { @@ -107,7 +111,7 @@ class BootstrapCrossSigningTask @Inject constructor( } } else { Timber.d("## BootstrapCrossSigningTask: Cross signing already setup, go to 4S setup") - if (params.initOnlyCrossSigning) { + if (params.setupMode == SetupMode.CROSS_SIGNING_ONLY) { // not sure how this can happen?? return handleInitializeXSigningError(IllegalArgumentException("Cross signing already setup")) } @@ -135,7 +139,7 @@ class BootstrapCrossSigningTask @Inject constructor( null, it ) - } ?: kotlin.run { + } ?: run { ssssService.generateKey( UUID.randomUUID().toString(), params.keySpec, @@ -236,7 +240,13 @@ class BootstrapCrossSigningTask @Inject constructor( val serverVersion = awaitCallback { session.cryptoService().keysBackupService().getCurrentVersion(it) } - if (serverVersion == null) { + + val knownMegolmSecret = session.cryptoService().keysBackupService().getKeyBackupRecoveryKeyInfo() + val isMegolmBackupSecretKnown = knownMegolmSecret != null && knownMegolmSecret.version == serverVersion?.version + val shouldCreateKeyBackup = serverVersion == null + || (params.setupMode == SetupMode.PASSPHRASE_AND_NEEDED_SECRETS_RESET && !isMegolmBackupSecretKnown) + || (params.setupMode == SetupMode.HARD_RESET) + if (shouldCreateKeyBackup) { Timber.d("## BootstrapCrossSigningTask: Creating 4S - Create megolm backup") val creationInfo = awaitCallback { session.cryptoService().keysBackupService().prepareKeysBackupVersion(null, null, it) @@ -260,16 +270,15 @@ class BootstrapCrossSigningTask @Inject constructor( } else { Timber.d("## BootstrapCrossSigningTask: Creating 4S - Existing megolm backup found") // ensure we store existing backup secret if we have it! - val knownSecret = session.cryptoService().keysBackupService().getKeyBackupRecoveryKeyInfo() - if (knownSecret != null && knownSecret.version == serverVersion.version) { + if (isMegolmBackupSecretKnown) { // check it matches val isValid = awaitCallback { - session.cryptoService().keysBackupService().isValidRecoveryKeyForCurrentVersion(knownSecret.recoveryKey, it) + session.cryptoService().keysBackupService().isValidRecoveryKeyForCurrentVersion(knownMegolmSecret!!.recoveryKey, it) } if (isValid) { Timber.d("## BootstrapCrossSigningTask: Creating 4S - Megolm key valid and known") awaitCallback { - extractCurveKeyFromRecoveryKey(knownSecret.recoveryKey)?.toBase64NoPadding()?.let { secret -> + extractCurveKeyFromRecoveryKey(knownMegolmSecret!!.recoveryKey)?.toBase64NoPadding()?.let { secret -> ssssService.storeSecret( KEYBACKUP_SECRET_SSSS_NAME, secret, @@ -286,7 +295,7 @@ class BootstrapCrossSigningTask @Inject constructor( Timber.e("## BootstrapCrossSigningTask: Failed to init keybackup") } - Timber.d("## BootstrapCrossSigningTask: initXSOnly:${params.initOnlyCrossSigning} Finished") + Timber.d("## BootstrapCrossSigningTask: mode:${params.setupMode} Finished") return BootstrapResult.Success(keyInfo) } diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapMigrateBackupFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapMigrateBackupFragment.kt index a89e08988c..0e3ba4c526 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapMigrateBackupFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapMigrateBackupFragment.kt @@ -17,7 +17,6 @@ package im.vector.app.features.crypto.recover import android.app.Activity -import android.content.Intent import android.os.Bundle import android.text.InputType.TYPE_CLASS_TEXT import android.text.InputType.TYPE_TEXT_FLAG_MULTI_LINE @@ -32,16 +31,17 @@ import com.jakewharton.rxbinding3.widget.editorActionEvents import com.jakewharton.rxbinding3.widget.textChanges import im.vector.app.R import im.vector.app.core.extensions.hideKeyboard +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.showPassword import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.resources.ColorProvider import im.vector.app.core.utils.colorizeMatchingText import im.vector.app.core.utils.startImportTextFromFileIntent -import org.matrix.android.sdk.api.extensions.tryOrNull -import org.matrix.android.sdk.internal.crypto.keysbackup.util.isValidRecoveryKey import io.reactivex.android.schedulers.AndroidSchedulers import kotlinx.android.synthetic.main.fragment_bootstrap_enter_passphrase.bootstrapDescriptionText import kotlinx.android.synthetic.main.fragment_bootstrap_migrate_backup.* +import org.matrix.android.sdk.api.extensions.tryOrNull +import org.matrix.android.sdk.internal.crypto.keysbackup.util.isValidRecoveryKey import java.util.concurrent.TimeUnit import javax.inject.Inject @@ -82,7 +82,7 @@ class BootstrapMigrateBackupFragment @Inject constructor( bootstrapMigrateContinueButton.debouncedClicks { submit() } bootstrapMigrateShowPassword.debouncedClicks { sharedViewModel.handle(BootstrapActions.TogglePasswordVisibility) } bootstrapMigrateForgotPassphrase.debouncedClicks { sharedViewModel.handle(BootstrapActions.HandleForgotBackupPassphrase) } - bootstrapMigrateUseFile.debouncedClicks { startImportTextFromFileIntent(this, IMPORT_FILE_REQ) } + bootstrapMigrateUseFile.debouncedClicks { startImportTextFromFileIntent(requireContext(), importFileStartForActivityResult) } } private fun submit() = withState(sharedViewModel) { state -> @@ -147,9 +147,9 @@ class BootstrapMigrateBackupFragment @Inject constructor( } } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (requestCode == IMPORT_FILE_REQ && resultCode == Activity.RESULT_OK) { - data?.data?.let { dataURI -> + private val importFileStartForActivityResult = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + activityResult.data?.data?.let { dataURI -> tryOrNull { activity?.contentResolver?.openInputStream(dataURI) ?.bufferedReader() @@ -159,12 +159,6 @@ class BootstrapMigrateBackupFragment @Inject constructor( } } } - return } - super.onActivityResult(requestCode, resultCode, data) - } - - companion object { - private const val IMPORT_FILE_REQ = 0 } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSaveRecoveryKeyFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSaveRecoveryKeyFragment.kt index 750dadbc9f..e426394d77 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSaveRecoveryKeyFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSaveRecoveryKeyFragment.kt @@ -16,7 +16,7 @@ package im.vector.app.features.crypto.recover -import android.app.Activity.RESULT_OK +import android.app.Activity import android.content.ActivityNotFoundException import android.content.Intent import android.os.Bundle @@ -25,6 +25,7 @@ import androidx.core.view.isVisible import com.airbnb.mvrx.parentFragmentViewModel import com.airbnb.mvrx.withState import im.vector.app.R +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.resources.ColorProvider import im.vector.app.core.utils.startSharePlainTextIntent @@ -65,43 +66,46 @@ class BootstrapSaveRecoveryKeyFragment @Inject constructor( try { sharedViewModel.handle(BootstrapActions.SaveReqQueryStarted) - startActivityForResult(Intent.createChooser(intent, getString(R.string.keys_backup_setup_step3_please_make_copy)), REQUEST_CODE_SAVE) + saveStartForActivityResult.launch(Intent.createChooser(intent, getString(R.string.keys_backup_setup_step3_please_make_copy))) } catch (activityNotFoundException: ActivityNotFoundException) { requireActivity().toast(R.string.error_no_external_application_found) sharedViewModel.handle(BootstrapActions.SaveReqFailed) } } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (requestCode == REQUEST_CODE_SAVE) { - val uri = data?.data - if (resultCode == RESULT_OK && uri != null) { - GlobalScope.launch(Dispatchers.IO) { - try { - sharedViewModel.handle(BootstrapActions.SaveKeyToUri(requireContext().contentResolver!!.openOutputStream(uri)!!)) - } catch (failure: Throwable) { - sharedViewModel.handle(BootstrapActions.SaveReqFailed) - } + private val saveStartForActivityResult = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + val uri = activityResult.data?.data ?: return@registerStartForActivityResult + GlobalScope.launch(Dispatchers.IO) { + try { + sharedViewModel.handle(BootstrapActions.SaveKeyToUri(requireContext().contentResolver!!.openOutputStream(uri)!!)) + } catch (failure: Throwable) { + sharedViewModel.handle(BootstrapActions.SaveReqFailed) } - } else { - // result code seems to be always cancelled here.. so act as if it was saved - sharedViewModel.handle(BootstrapActions.SaveReqFailed) } - return - } else if (requestCode == REQUEST_CODE_COPY) { + } else { + // result code seems to be always cancelled here.. so act as if it was saved + sharedViewModel.handle(BootstrapActions.SaveReqFailed) + } + } + + private val copyStartForActivityResult = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { sharedViewModel.handle(BootstrapActions.RecoveryKeySaved) } - super.onActivityResult(requestCode, resultCode, data) } private fun shareRecoveryKey() = withState(sharedViewModel) { state -> val recoveryKey = state.recoveryKeyCreationInfo?.recoveryKey?.formatRecoveryKey() ?: return@withState - startSharePlainTextIntent(this, + startSharePlainTextIntent( + this, + copyStartForActivityResult, context?.getString(R.string.keys_backup_setup_step3_share_intent_chooser_title), recoveryKey, - context?.getString(R.string.recovery_key), REQUEST_CODE_COPY) + context?.getString(R.string.recovery_key) + ) } override fun invalidate() = withState(sharedViewModel) { state -> @@ -111,9 +115,4 @@ class BootstrapSaveRecoveryKeyFragment @Inject constructor( recoveryContinue.isVisible = step.isSaved bootstrapRecoveryKeyText.text = state.recoveryKeyCreationInfo?.recoveryKey?.formatRecoveryKey() } - - companion object { - const val REQUEST_CODE_SAVE = 123 - const val REQUEST_CODE_COPY = 124 - } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt index 32b4771286..72b767e12f 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt @@ -34,6 +34,8 @@ import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.WaitingViewData import im.vector.app.core.resources.StringProvider import im.vector.app.features.login.ReAuthHelper +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.securestorage.RawBytesKeySpec @@ -41,8 +43,6 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionR import org.matrix.android.sdk.internal.crypto.keysbackup.util.extractCurveKeyFromRecoveryKey import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth import org.matrix.android.sdk.internal.util.awaitCallback -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import java.io.OutputStream class BootstrapSharedViewModel @AssistedInject constructor( @@ -69,46 +69,52 @@ class BootstrapSharedViewModel @AssistedInject constructor( init { - if (args.forceReset4S) { - setState { - copy(step = BootstrapStep.FirstForm(keyBackUpExist = false, reset = true)) - } - } else if (args.initCrossSigningOnly) { - // Go straight to account password - setState { - copy(step = BootstrapStep.AccountPassword(false)) - } - } else { - // need to check if user have an existing keybackup - setState { - copy(step = BootstrapStep.CheckingMigration) - } - - // We need to check if there is an existing backup - viewModelScope.launch(Dispatchers.IO) { - val version = awaitCallback { - session.cryptoService().keysBackupService().getCurrentVersion(it) + when (args.setUpMode) { + SetupMode.PASSPHRASE_RESET, + SetupMode.PASSPHRASE_AND_NEEDED_SECRETS_RESET, + SetupMode.HARD_RESET -> { + setState { + copy(step = BootstrapStep.FirstForm(keyBackUpExist = false, reset = true)) } - if (version == null) { - // we just resume plain bootstrap - doesKeyBackupExist = false - setState { - copy(step = BootstrapStep.FirstForm(keyBackUpExist = doesKeyBackupExist)) + } + SetupMode.CROSS_SIGNING_ONLY -> { + // Go straight to account password + setState { + copy(step = BootstrapStep.AccountPassword(false)) + } + } + SetupMode.NORMAL -> { + // need to check if user have an existing keybackup + setState { + copy(step = BootstrapStep.CheckingMigration) + } + + // We need to check if there is an existing backup + viewModelScope.launch(Dispatchers.IO) { + val version = awaitCallback { + session.cryptoService().keysBackupService().getCurrentVersion(it) } - } else { - // we need to get existing backup passphrase/key and convert to SSSS - val keyVersion = awaitCallback { - session.cryptoService().keysBackupService().getVersion(version.version ?: "", it) - } - if (keyVersion == null) { - // strange case... just finish? - _viewEvents.post(BootstrapViewEvents.Dismiss) - } else { - doesKeyBackupExist = true - isBackupCreatedFromPassphrase = keyVersion.getAuthDataAsMegolmBackupAuthData()?.privateKeySalt != null + if (version == null) { + // we just resume plain bootstrap + doesKeyBackupExist = false setState { copy(step = BootstrapStep.FirstForm(keyBackUpExist = doesKeyBackupExist)) } + } else { + // we need to get existing backup passphrase/key and convert to SSSS + val keyVersion = awaitCallback { + session.cryptoService().keysBackupService().getVersion(version.version ?: "", it) + } + if (keyVersion == null) { + // strange case... just finish? + _viewEvents.post(BootstrapViewEvents.Dismiss(false)) + } else { + doesKeyBackupExist = true + isBackupCreatedFromPassphrase = keyVersion.getAuthDataAsMegolmBackupAuthData()?.privateKeySalt != null + setState { + copy(step = BootstrapStep.FirstForm(keyBackUpExist = doesKeyBackupExist)) + } + } } } } @@ -234,7 +240,7 @@ class BootstrapSharedViewModel @AssistedInject constructor( } } BootstrapActions.Completed -> { - _viewEvents.post(BootstrapViewEvents.Dismiss) + _viewEvents.post(BootstrapViewEvents.Dismiss(true)) } BootstrapActions.GoToCompleted -> { setState { @@ -295,7 +301,7 @@ class BootstrapSharedViewModel @AssistedInject constructor( // ======================================= private fun saveRecoveryKeyToUri(os: OutputStream) = withState { state -> viewModelScope.launch(Dispatchers.IO) { - kotlin.runCatching { + runCatching { os.use { os.write((state.recoveryKeyCreationInfo?.recoveryKey?.formatRecoveryKey() ?: "").toByteArray()) } @@ -395,16 +401,16 @@ class BootstrapSharedViewModel @AssistedInject constructor( bootstrapTask.invoke(this, Params( userPasswordAuth = userPasswordAuth, - initOnlyCrossSigning = args.initCrossSigningOnly, progressListener = progressListener, passphrase = state.passphrase, - keySpec = state.migrationRecoveryKey?.let { extractCurveKeyFromRecoveryKey(it)?.let { RawBytesKeySpec(it) } } + keySpec = state.migrationRecoveryKey?.let { extractCurveKeyFromRecoveryKey(it)?.let { RawBytesKeySpec(it) } }, + setupMode = args.setUpMode ) ) { bootstrapResult -> when (bootstrapResult) { - is BootstrapResult.SuccessCrossSigningOnly -> { + is BootstrapResult.SuccessCrossSigningOnly -> { // TPD - _viewEvents.post(BootstrapViewEvents.Dismiss) + _viewEvents.post(BootstrapViewEvents.Dismiss(true)) } is BootstrapResult.Success -> { setState { @@ -428,7 +434,7 @@ class BootstrapSharedViewModel @AssistedInject constructor( } is BootstrapResult.UnsupportedAuthFlow -> { _viewEvents.post(BootstrapViewEvents.ModalError(stringProvider.getString(R.string.auth_flow_not_supported))) - _viewEvents.post(BootstrapViewEvents.Dismiss) + _viewEvents.post(BootstrapViewEvents.Dismiss(false)) } is BootstrapResult.InvalidPasswordError -> { // it's a bad password @@ -522,7 +528,13 @@ class BootstrapSharedViewModel @AssistedInject constructor( } BootstrapStep.CheckingMigration -> Unit is BootstrapStep.FirstForm -> { - _viewEvents.post(BootstrapViewEvents.SkipBootstrap()) + _viewEvents.post( + when (args.setUpMode) { + SetupMode.CROSS_SIGNING_ONLY, + SetupMode.NORMAL -> BootstrapViewEvents.SkipBootstrap() + else -> BootstrapViewEvents.Dismiss(success = false) + } + ) } is BootstrapStep.GetBackupSecretForMigration -> { setState { @@ -558,7 +570,7 @@ class BootstrapSharedViewModel @AssistedInject constructor( override fun create(viewModelContext: ViewModelContext, state: BootstrapViewState): BootstrapSharedViewModel? { val fragment: BootstrapBottomSheet = (viewModelContext as FragmentViewModelContext).fragment() val args: BootstrapBottomSheet.Args = fragment.arguments?.getParcelable(BootstrapBottomSheet.EXTRA_ARGS) - ?: BootstrapBottomSheet.Args(initCrossSigningOnly = true, forceReset4S = false) + ?: BootstrapBottomSheet.Args(SetupMode.CROSS_SIGNING_ONLY) return fragment.bootstrapViewModelFactory.create(state, args) } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapViewEvents.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapViewEvents.kt index 58bc64a9ad..10a092ccbb 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapViewEvents.kt @@ -19,8 +19,8 @@ package im.vector.app.features.crypto.recover import im.vector.app.core.platform.VectorViewEvents sealed class BootstrapViewEvents : VectorViewEvents { - object Dismiss : BootstrapViewEvents() + data class Dismiss(val success: Boolean) : BootstrapViewEvents() data class ModalError(val error: String) : BootstrapViewEvents() - object RecoveryKeySaved: BootstrapViewEvents() - data class SkipBootstrap(val genKeyOption: Boolean = true): BootstrapViewEvents() + object RecoveryKeySaved : BootstrapViewEvents() + data class SkipBootstrap(val genKeyOption: Boolean = true) : BootstrapViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/SetupMode.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/SetupMode.kt new file mode 100644 index 0000000000..0879490e79 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/SetupMode.kt @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.crypto.recover + +enum class SetupMode { + + /** + * Only setup cross signing, no 4S or megolm backup + */ + CROSS_SIGNING_ONLY, + + /** + * Normal setup mode. + */ + NORMAL, + + /** + * Only reset the 4S passphrase/key, but do not touch + * to existing cross-signing or megolm backup + * It take the local known secrets and put them in 4S + */ + PASSPHRASE_RESET, + + /** + * Resets the passphrase/key, and all missing secrets + * are re-created. Meaning that if cross signing is setup and the secrets + * keys are not known, cross signing will be reset (if secret is known we just keep same cross signing) + * Same apply to megolm + */ + PASSPHRASE_AND_NEEDED_SECRETS_RESET, + + /** + * Resets the passphrase/key, cross signing and megolm backup + */ + HARD_RESET +} diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationAction.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationAction.kt index 1c6ea413cb..a32a9de97f 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationAction.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationAction.kt @@ -31,4 +31,5 @@ sealed class VerificationAction : VectorViewModelAction { object SkipVerification : VerificationAction() object VerifyFromPassphrase : VerificationAction() data class GotResultFromSsss(val cypherData: String, val alias: String) : VerificationAction() + object SecuredStorageHasBeenReset : VerificationAction() } diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt index f979539f2e..35ea96de6f 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt @@ -17,7 +17,6 @@ package im.vector.app.features.crypto.verification import android.app.Activity import android.app.Dialog -import android.content.Intent import android.os.Bundle import android.os.Parcelable import android.view.KeyEvent @@ -35,6 +34,7 @@ import im.vector.app.R import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.commitTransaction import im.vector.app.core.extensions.exhaustive +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.features.crypto.quads.SharedSecureStorageActivity @@ -48,6 +48,7 @@ import im.vector.app.features.crypto.verification.qrconfirmation.VerificationQrS import im.vector.app.features.crypto.verification.request.VerificationRequestFragment import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.settings.VectorSettingsActivity +import kotlinx.android.parcel.Parcelize import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME @@ -55,7 +56,6 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_S import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.verification.CancelCode import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState -import kotlinx.android.parcel.Parcelize import timber.log.Timber import javax.inject.Inject import kotlin.reflect.KClass @@ -76,6 +76,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { @Inject lateinit var verificationViewModelFactory: VerificationBottomSheetViewModel.Factory + @Inject lateinit var avatarRenderer: AvatarRenderer @@ -107,12 +108,12 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { when (it) { is VerificationBottomSheetViewEvents.Dismiss -> dismiss() is VerificationBottomSheetViewEvents.AccessSecretStore -> { - startActivityForResult(SharedSecureStorageActivity.newIntent( + secretStartForActivityResult.launch(SharedSecureStorageActivity.newIntent( requireContext(), null, // use default key listOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME, KEYBACKUP_SECRET_SSSS_NAME), SharedSecureStorageActivity.DEFAULT_RESULT_KEYSTORE_ALIAS - ), SECRET_REQUEST_CODE) + )) } is VerificationBottomSheetViewEvents.ModalError -> { AlertDialog.Builder(requireContext()) @@ -144,17 +145,20 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { } } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (resultCode == Activity.RESULT_OK && requestCode == SECRET_REQUEST_CODE) { - data?.getStringExtra(SharedSecureStorageActivity.EXTRA_DATA_RESULT)?.let { - viewModel.handle(VerificationAction.GotResultFromSsss(it, SharedSecureStorageActivity.DEFAULT_RESULT_KEYSTORE_ALIAS)) + private val secretStartForActivityResult = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + val result = activityResult.data?.getStringExtra(SharedSecureStorageActivity.EXTRA_DATA_RESULT) + val reset = activityResult.data?.getBooleanExtra(SharedSecureStorageActivity.EXTRA_DATA_RESET, false) ?: false + if (result != null) { + viewModel.handle(VerificationAction.GotResultFromSsss(result, SharedSecureStorageActivity.DEFAULT_RESULT_KEYSTORE_ALIAS)) + } else if (reset) { + // all have been reset, so we are verified? + viewModel.handle(VerificationAction.SecuredStorageHasBeenReset) } } - super.onActivityResult(requestCode, resultCode, data) } override fun invalidate() = withState(viewModel) { state -> - state.otherUserMxItem?.let { matrixItem -> if (state.isMe) { avatarRenderer.render(matrixItem, otherUserAvatarImageView) @@ -182,6 +186,17 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { } } + if (state.quadSHasBeenReset) { + showFragment(VerificationConclusionFragment::class, Bundle().apply { + putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args( + isSuccessFull = true, + isMe = true, + cancelReason = null + )) + }) + return@withState + } + if (state.userThinkItsNotHim) { otherUserNameText.text = getString(R.string.dialog_title_warning) showFragment(VerificationNotMeFragment::class, Bundle()) @@ -330,9 +345,6 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { } companion object { - - const val SECRET_REQUEST_CODE = 101 - fun withArgs(roomId: String?, otherUserId: String, transactionId: String? = null): VerificationBottomSheet { return VerificationBottomSheet().apply { arguments = Bundle().apply { @@ -356,6 +368,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { } } } + fun forSelfVerification(session: Session, outgoingRequest: String): VerificationBottomSheet { return VerificationBottomSheet().apply { arguments = Bundle().apply { diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt index a4ce9bd38d..2720c20fb0 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt @@ -31,6 +31,8 @@ import im.vector.app.R import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME @@ -56,7 +58,6 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionR import org.matrix.android.sdk.internal.crypto.keysbackup.util.computeRecoveryKey import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult import org.matrix.android.sdk.internal.util.awaitCallback -import kotlinx.coroutines.launch import timber.log.Timber data class VerificationBottomSheetViewState( @@ -74,7 +75,9 @@ data class VerificationBottomSheetViewState( val currentDeviceCanCrossSign: Boolean = false, val userWantsToCancel: Boolean = false, val userThinkItsNotHim: Boolean = false, - val quadSContainsSecrets: Boolean = true + val quadSContainsSecrets: Boolean = true, + val quadSHasBeenReset: Boolean = false, + val hasAnyOtherSession: Boolean = false ) : MvRxState class VerificationBottomSheetViewModel @AssistedInject constructor( @@ -117,6 +120,12 @@ class VerificationBottomSheetViewModel @AssistedInject constructor( session.cryptoService().verificationService().getExistingTransaction(args.otherUserId, it) as? QrCodeVerificationTransaction } + val hasAnyOtherSession = session.cryptoService() + .getCryptoDeviceInfo(session.myUserId) + .any { + it.deviceId != session.sessionParams.deviceId + } + setState { copy( otherUserMxItem = userItem?.toMatrixItem(), @@ -128,7 +137,8 @@ class VerificationBottomSheetViewModel @AssistedInject constructor( roomId = args.roomId, isMe = args.otherUserId == session.myUserId, currentDeviceCanCrossSign = session.cryptoService().crossSigningService().canCrossSign(), - quadSContainsSecrets = session.sharedSecretStorageService.isRecoverySetup() + quadSContainsSecrets = session.sharedSecretStorageService.isRecoverySetup(), + hasAnyOtherSession = hasAnyOtherSession ) } @@ -349,6 +359,14 @@ class VerificationBottomSheetViewModel @AssistedInject constructor( is VerificationAction.GotResultFromSsss -> { handleSecretBackFromSSSS(action) } + VerificationAction.SecuredStorageHasBeenReset -> { + if (session.cryptoService().crossSigningService().allPrivateKeysKnown()) { + setState { + copy(quadSHasBeenReset = true) + } + } + Unit + } }.exhaustive } @@ -393,7 +411,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor( } private fun tentativeRestoreBackup(res: Map?) { - viewModelScope.launch { + viewModelScope.launch(Dispatchers.IO) { try { val secret = res?.get(KEYBACKUP_SECRET_SSSS_NAME) ?: return@launch Unit.also { Timber.v("## Keybackup secret not restored from SSSS") diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodFragment.kt index fc715301f2..72cd063bbd 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodFragment.kt @@ -16,7 +16,6 @@ package im.vector.app.features.crypto.verification.choose import android.app.Activity -import android.content.Intent import android.os.Bundle import android.view.View import com.airbnb.mvrx.fragmentViewModel @@ -25,11 +24,11 @@ import com.airbnb.mvrx.withState import im.vector.app.R import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO -import im.vector.app.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_CAMERA -import im.vector.app.core.utils.allGranted import im.vector.app.core.utils.checkPermissions +import im.vector.app.core.utils.registerForPermissionsResult import im.vector.app.features.crypto.verification.VerificationAction import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel import im.vector.app.features.qrcode.QrCodeScannerActivity @@ -75,16 +74,14 @@ class VerificationChooseMethodFragment @Inject constructor( state.pendingRequest.invoke()?.transactionId ?: "")) } - override fun openCamera() { - if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, this, PERMISSION_REQUEST_CODE_LAUNCH_CAMERA)) { + private val openCameraActivityResultLauncher = registerForPermissionsResult { allGranted -> + if (allGranted) { doOpenQRCodeScanner() } } - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - - if (requestCode == PERMISSION_REQUEST_CODE_LAUNCH_CAMERA && allGranted(grantResults)) { + override fun openCamera() { + if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), openCameraActivityResultLauncher)) { doOpenQRCodeScanner() } } @@ -94,24 +91,18 @@ class VerificationChooseMethodFragment @Inject constructor( } private fun doOpenQRCodeScanner() { - QrCodeScannerActivity.startForResult(this) + QrCodeScannerActivity.startForResult(requireActivity(), scanActivityResultLauncher) } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) + private val scanActivityResultLauncher = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + val scannedQrCode = QrCodeScannerActivity.getResultText(activityResult.data) + val wasQrCode = QrCodeScannerActivity.getResultIsQrCode(activityResult.data) - if (resultCode == Activity.RESULT_OK) { - when (requestCode) { - QrCodeScannerActivity.QR_CODE_SCANNER_REQUEST_CODE -> { - val scannedQrCode = QrCodeScannerActivity.getResultText(data) - val wasQrCode = QrCodeScannerActivity.getResultIsQrCode(data) - - if (wasQrCode && !scannedQrCode.isNullOrBlank()) { - onRemoteQrCodeScanned(scannedQrCode) - } else { - Timber.w("It was not a QR code, or empty result") - } - } + if (wasQrCode && !scannedQrCode.isNullOrBlank()) { + onRemoteQrCodeScanned(scannedQrCode) + } else { + Timber.w("It was not a QR code, or empty result") } } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/conclusion/VerificationConclusionController.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/conclusion/VerificationConclusionController.kt index 3c7c6d5745..d12b2c088f 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/conclusion/VerificationConclusionController.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/conclusion/VerificationConclusionController.kt @@ -46,7 +46,7 @@ class VerificationConclusionController @Inject constructor( val state = viewState ?: return when (state.conclusionState) { - ConclusionState.SUCCESS -> { + ConclusionState.SUCCESS -> { bottomSheetVerificationNoticeItem { id("notice") notice(stringProvider.getString( @@ -61,7 +61,7 @@ class VerificationConclusionController @Inject constructor( bottomDone() } - ConclusionState.WARNING -> { + ConclusionState.WARNING -> { bottomSheetVerificationNoticeItem { id("notice") notice(stringProvider.getString(R.string.verification_conclusion_not_secure)) diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/epoxy/BottomSheetVerificationActionItem.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/epoxy/BottomSheetVerificationActionItem.kt index e87be702db..d738efe3ba 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/epoxy/BottomSheetVerificationActionItem.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/epoxy/BottomSheetVerificationActionItem.kt @@ -38,12 +38,16 @@ abstract class BottomSheetVerificationActionItem : VectorEpoxyModel(R.id.item_emoji_tv).isVisible = false view.findViewById(R.id.item_emoji_image).isVisible = true view.findViewById(R.id.item_emoji_image).setImageDrawable(ContextCompat.getDrawable(view.context, it)) - } ?: kotlin.run { + } ?: run { view.findViewById(R.id.item_emoji_tv).isVisible = true view.findViewById(R.id.item_emoji_image).isVisible = false view.findViewById(R.id.item_emoji_tv).text = rep.emoji diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/request/VerificationRequestController.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/request/VerificationRequestController.kt index 3f4c3120e7..c7740e2ac5 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/request/VerificationRequestController.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/request/VerificationRequestController.kt @@ -52,25 +52,32 @@ class VerificationRequestController @Inject constructor( val matrixItem = viewState?.otherUserMxItem ?: return if (state.selfVerificationMode) { - bottomSheetVerificationNoticeItem { - id("notice") - notice(stringProvider.getString(R.string.verification_open_other_to_verify)) - } + if (state.hasAnyOtherSession) { + bottomSheetVerificationNoticeItem { + id("notice") + notice(stringProvider.getString(R.string.verification_open_other_to_verify)) + } - bottomSheetSelfWaitItem { - id("waiting") - } + bottomSheetSelfWaitItem { + id("waiting") + } - dividerItem { - id("sep") + dividerItem { + id("sep") + } } if (state.quadSContainsSecrets) { + val subtitle = if (state.hasAnyOtherSession) { + stringProvider.getString(R.string.verification_use_passphrase) + } else { + null + } bottomSheetVerificationActionItem { id("passphrase") title(stringProvider.getString(R.string.verification_cannot_access_other_session)) titleColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary)) - subTitle(stringProvider.getString(R.string.verification_use_passphrase)) + subTitle(subtitle) iconRes(R.drawable.ic_arrow_right) iconColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary)) listener { listener?.onClickRecoverFromPassphrase() } diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt index 21f65ec9ef..bfbc00b15a 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt @@ -16,7 +16,6 @@ package im.vector.app.features.discovery import android.app.Activity -import android.content.Intent import android.os.Bundle import android.view.View import androidx.appcompat.app.AlertDialog @@ -27,16 +26,16 @@ import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.observeEvent +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.ensureProtocol import im.vector.app.features.discovery.change.SetIdentityServerFragment import im.vector.app.features.settings.VectorSettingsActivity -import im.vector.app.features.terms.ReviewTermsActivity +import kotlinx.android.synthetic.main.fragment_generic_recycler.* import org.matrix.android.sdk.api.session.identity.SharedState import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.terms.TermsService -import kotlinx.android.synthetic.main.fragment_generic_recycler.* import javax.inject.Inject class DiscoverySettingsFragment @Inject constructor( @@ -92,22 +91,19 @@ class DiscoverySettingsFragment @Inject constructor( viewModel.handle(DiscoverySettingsAction.Refresh) } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (requestCode == ReviewTermsActivity.TERMS_REQUEST_CODE) { - if (Activity.RESULT_OK == resultCode) { - viewModel.handle(DiscoverySettingsAction.RetrieveBinding) - } else { - // add some error? - } + private val termsActivityResultLauncher = registerStartForActivityResult { + if (it.resultCode == Activity.RESULT_OK) { + viewModel.handle(DiscoverySettingsAction.RetrieveBinding) + } else { + // add some error? } - - super.onActivityResult(requestCode, resultCode, data) } override fun openIdentityServerTerms() = withState(viewModel) { state -> if (state.termsNotSigned) { navigator.openTerms( - this, + requireContext(), + termsActivityResultLauncher, TermsService.ServiceType.IdentityService, state.identityServer()?.ensureProtocol() ?: "", null) diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt index 7ec6082111..0bfcdd9984 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt @@ -28,13 +28,13 @@ import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.identity.IdentityServiceError import org.matrix.android.sdk.api.session.identity.IdentityServiceListener import org.matrix.android.sdk.api.session.identity.SharedState import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.internal.util.awaitCallback -import kotlinx.coroutines.launch import org.matrix.android.sdk.rx.rx class DiscoverySettingsViewModel @AssistedInject constructor( diff --git a/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerFragment.kt b/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerFragment.kt index 863270b762..8fb4fc4156 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerFragment.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerFragment.kt @@ -16,7 +16,6 @@ package im.vector.app.features.discovery.change import android.app.Activity -import android.content.Intent import android.os.Bundle import android.view.View import android.view.inputmethod.EditorInfo @@ -28,15 +27,15 @@ import com.airbnb.mvrx.withState import com.jakewharton.rxbinding3.widget.textChanges import im.vector.app.R import im.vector.app.core.extensions.exhaustive +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.toReducedUrl import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.resources.ColorProvider import im.vector.app.core.utils.colorizeMatchingText import im.vector.app.features.discovery.DiscoverySharedViewModel -import im.vector.app.features.terms.ReviewTermsActivity -import org.matrix.android.sdk.api.session.terms.TermsService import kotlinx.android.synthetic.main.fragment_set_identity_server.* +import org.matrix.android.sdk.api.session.terms.TermsService import javax.inject.Inject class SetIdentityServerFragment @Inject constructor( @@ -121,7 +120,8 @@ class SetIdentityServerFragment @Inject constructor( is SetIdentityServerViewEvents.TermsAccepted -> processIdentityServerChange() is SetIdentityServerViewEvents.ShowTerms -> { navigator.openTerms( - this, + requireContext(), + termsActivityResultLauncher, TermsService.ServiceType.IdentityService, it.identityServerUrl, null) @@ -150,15 +150,12 @@ class SetIdentityServerFragment @Inject constructor( (activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.identity_server) } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (requestCode == ReviewTermsActivity.TERMS_REQUEST_CODE) { - if (Activity.RESULT_OK == resultCode) { - processIdentityServerChange() - } else { - // add some error? - } + private val termsActivityResultLauncher = registerStartForActivityResult { + if (it.resultCode == Activity.RESULT_OK) { + processIdentityServerChange() + } else { + // add some error? } - super.onActivityResult(requestCode, resultCode, data) } private fun processIdentityServerChange() { diff --git a/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerViewModel.kt b/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerViewModel.kt index e7faf79f9d..9331f67812 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerViewModel.kt @@ -27,13 +27,13 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.ensureProtocol +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.identity.IdentityServiceError import org.matrix.android.sdk.api.session.terms.GetTermsResponse import org.matrix.android.sdk.api.session.terms.TermsService import org.matrix.android.sdk.internal.util.awaitCallback -import kotlinx.coroutines.launch import java.net.UnknownHostException class SetIdentityServerViewModel @AssistedInject constructor( diff --git a/vector/src/main/java/im/vector/app/features/grouplist/GroupListFragment.kt b/vector/src/main/java/im/vector/app/features/grouplist/GroupListFragment.kt index 434ab7f1ec..d4ba4f3150 100644 --- a/vector/src/main/java/im/vector/app/features/grouplist/GroupListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/grouplist/GroupListFragment.kt @@ -31,8 +31,8 @@ import im.vector.app.core.platform.StateView import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.features.home.HomeActivitySharedAction import im.vector.app.features.home.HomeSharedActionViewModel -import org.matrix.android.sdk.api.session.group.model.GroupSummary import kotlinx.android.synthetic.main.fragment_group_list.* +import org.matrix.android.sdk.api.session.group.model.GroupSummary import javax.inject.Inject class GroupListFragment @Inject constructor( diff --git a/vector/src/main/java/im/vector/app/features/grouplist/GroupListViewModel.kt b/vector/src/main/java/im/vector/app/features/grouplist/GroupListViewModel.kt index 51831e8fb9..588d939635 100644 --- a/vector/src/main/java/im/vector/app/features/grouplist/GroupListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/grouplist/GroupListViewModel.kt @@ -26,14 +26,14 @@ import com.squareup.inject.assisted.AssistedInject import im.vector.app.R import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider +import io.reactivex.Observable +import io.reactivex.functions.BiFunction import org.matrix.android.sdk.api.NoOpMatrixCallback import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.group.groupSummaryQueryParams import org.matrix.android.sdk.api.session.group.model.GroupSummary import org.matrix.android.sdk.api.session.room.model.Membership -import io.reactivex.Observable -import io.reactivex.functions.BiFunction import org.matrix.android.sdk.rx.rx const val ALL_COMMUNITIES_GROUP_ID = "+ALL_COMMUNITIES_GROUP_ID" diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt index ae3a9f9680..c074d66f7c 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt @@ -139,6 +139,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet is HomeActivityViewEvents.AskPasswordToInitCrossSigning -> handleAskPasswordToInitCrossSigning(it) is HomeActivityViewEvents.OnNewSession -> handleOnNewSession(it) HomeActivityViewEvents.PromptToEnableSessionPush -> handlePromptToEnablePush() + is HomeActivityViewEvents.OnCrossSignedInvalidated -> handleCrossSigningInvalidated(it) }.exhaustive } homeActivityViewModel.subscribe(this) { renderState(it) } @@ -183,6 +184,17 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet } } + private fun handleCrossSigningInvalidated(event: HomeActivityViewEvents.OnCrossSignedInvalidated) { + // We need to ask + promptSecurityEvent( + event.userItem, + R.string.crosssigning_verify_this_session, + R.string.confirm_your_identity + ) { + it.navigator.waitSessionVerification(it) + } + } + private fun handleOnNewSession(event: HomeActivityViewEvents.OnNewSession) { // We need to ask promptSecurityEvent( @@ -310,6 +322,10 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet navigator.openRoomsFiltering(this) return true } + R.id.menu_home_setting -> { + navigator.openSettings(this) + return true + } } return super.onOptionsItemSelected(item) diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt index be62997e0c..2a29e13572 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt @@ -22,5 +22,6 @@ import org.matrix.android.sdk.api.util.MatrixItem sealed class HomeActivityViewEvents : VectorViewEvents { data class AskPasswordToInitCrossSigning(val userItem: MatrixItem.UserItem?) : HomeActivityViewEvents() data class OnNewSession(val userItem: MatrixItem.UserItem?, val waitForIncomingRequest: Boolean = true) : HomeActivityViewEvents() + data class OnCrossSignedInvalidated(val userItem: MatrixItem.UserItem?) : HomeActivityViewEvents() object PromptToEnableSessionPush : HomeActivityViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt index 46ae65ec02..48a71db35c 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt @@ -27,6 +27,9 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.login.ReAuthHelper import im.vector.app.features.settings.VectorPreferences +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.NoOpMatrixCallback import org.matrix.android.sdk.api.pushrules.RuleIds @@ -38,9 +41,7 @@ import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth import org.matrix.android.sdk.rx.asObservable -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch +import org.matrix.android.sdk.rx.rx import timber.log.Timber class HomeActivityViewModel @AssistedInject constructor( @@ -67,11 +68,39 @@ class HomeActivityViewModel @AssistedInject constructor( } private var checkBootstrap = false + private var onceTrusted = false init { observeInitialSync() mayBeInitializeCrossSigning() checkSessionPushIsOn() + observeCrossSigningReset() + } + + private fun observeCrossSigningReset() { + val safeActiveSession = activeSessionHolder.getSafeActiveSession() + val crossSigningService = safeActiveSession + ?.cryptoService() + ?.crossSigningService() + onceTrusted = crossSigningService + ?.allPrivateKeysKnown() ?: false + + safeActiveSession + ?.rx() + ?.liveCrossSigningInfo(safeActiveSession.myUserId) + ?.subscribe { + val isVerified = it.getOrNull()?.isTrusted() ?: false + if (!isVerified && onceTrusted) { + // cross signing keys have been reset + // Tigger a popup to re-verify + _viewEvents.post( + HomeActivityViewEvents.OnCrossSignedInvalidated( + safeActiveSession.getUser(safeActiveSession.myUserId)?.toMatrixItem() + ) + ) + } + onceTrusted = isVerified + }?.disposeOnClear() } private fun observeInitialSync() { diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt index c381f998c7..88c310fde8 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt @@ -27,9 +27,9 @@ import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.features.grouplist.SelectedGroupDataSource import im.vector.app.features.ui.UiStateRepository +import io.reactivex.schedulers.Schedulers import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.model.Membership -import io.reactivex.schedulers.Schedulers import org.matrix.android.sdk.rx.rx /** diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDrawerFragment.kt b/vector/src/main/java/im/vector/app/features/home/HomeDrawerFragment.kt index 3344989b0a..12689cd983 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDrawerFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDrawerFragment.kt @@ -18,18 +18,23 @@ package im.vector.app.features.home import android.os.Bundle import android.view.View +import androidx.core.view.isVisible import im.vector.app.R import im.vector.app.core.extensions.observeK import im.vector.app.core.extensions.replaceChildFragment import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.features.grouplist.GroupListFragment +import im.vector.app.features.settings.VectorPreferences +import im.vector.app.features.settings.VectorSettingsActivity +import im.vector.app.features.workers.signout.SignOutUiWorker +import kotlinx.android.synthetic.main.fragment_home_drawer.* import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.util.toMatrixItem -import kotlinx.android.synthetic.main.fragment_home_drawer.* import javax.inject.Inject class HomeDrawerFragment @Inject constructor( private val session: Session, + private val vectorPreferences: VectorPreferences, private val avatarRenderer: AvatarRenderer ) : VectorBaseFragment() { @@ -53,12 +58,24 @@ class HomeDrawerFragment @Inject constructor( homeDrawerUserIdView.text = user.userId } } + // Profile + homeDrawerHeader.debouncedClicks { + sharedActionViewModel.post(HomeActivitySharedAction.CloseDrawer) + navigator.openSettings(requireActivity(), directAccess = VectorSettingsActivity.EXTRA_DIRECT_ACCESS_GENERAL) + } + // Settings homeDrawerHeaderSettingsView.debouncedClicks { sharedActionViewModel.post(HomeActivitySharedAction.CloseDrawer) navigator.openSettings(requireActivity()) } + // Sign out + homeDrawerHeaderSignoutView.debouncedClicks { + sharedActionViewModel.post(HomeActivitySharedAction.CloseDrawer) + SignOutUiWorker(requireActivity()).perform() + } // Debug menu + homeDrawerHeaderDebugView.isVisible = vectorPreferences.developerMode() homeDrawerHeaderDebugView.debouncedClicks { sharedActionViewModel.post(HomeActivitySharedAction.CloseDrawer) navigator.openDebug(requireActivity()) diff --git a/vector/src/main/java/im/vector/app/features/home/LoadingFragment.kt b/vector/src/main/java/im/vector/app/features/home/LoadingFragment.kt index b3c80b3642..e56887b85f 100644 --- a/vector/src/main/java/im/vector/app/features/home/LoadingFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/LoadingFragment.kt @@ -24,7 +24,7 @@ import im.vector.app.core.platform.VectorBaseFragment import kotlinx.android.synthetic.main.fragment_loading.* import javax.inject.Inject -class LoadingFragment @Inject constructor(): VectorBaseFragment() { +class LoadingFragment @Inject constructor() : VectorBaseFragment() { override fun getLayoutResId() = R.layout.fragment_loading diff --git a/vector/src/main/java/im/vector/app/features/home/RoomListDisplayMode.kt b/vector/src/main/java/im/vector/app/features/home/RoomListDisplayMode.kt index 91f08a4d21..ad99d8e1c4 100644 --- a/vector/src/main/java/im/vector/app/features/home/RoomListDisplayMode.kt +++ b/vector/src/main/java/im/vector/app/features/home/RoomListDisplayMode.kt @@ -20,9 +20,9 @@ import androidx.annotation.StringRes import im.vector.app.R enum class RoomListDisplayMode(@StringRes val titleRes: Int) { - ALL(R.string.bottom_action_all), - NOTIFICATIONS(R.string.bottom_action_notification), - PEOPLE(R.string.bottom_action_people_x), - ROOMS(R.string.bottom_action_rooms), - FILTERED(/* Not used */ 0) - } + ALL(R.string.bottom_action_all), + NOTIFICATIONS(R.string.bottom_action_notification), + PEOPLE(R.string.bottom_action_people_x), + ROOMS(R.string.bottom_action_rooms), + FILTERED(/* Not used */ 0) +} diff --git a/vector/src/main/java/im/vector/app/features/home/ShortcutCreator.kt b/vector/src/main/java/im/vector/app/features/home/ShortcutCreator.kt new file mode 100644 index 0000000000..db396cf990 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/ShortcutCreator.kt @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home + +import android.content.Context +import android.graphics.Bitmap +import android.os.Build +import androidx.annotation.WorkerThread +import androidx.core.content.pm.ShortcutInfoCompat +import androidx.core.content.pm.ShortcutManagerCompat +import androidx.core.graphics.drawable.IconCompat +import im.vector.app.BuildConfig +import im.vector.app.core.glide.GlideApp +import im.vector.app.core.utils.DimensionConverter +import im.vector.app.features.home.room.detail.RoomDetailActivity +import org.matrix.android.sdk.api.session.room.model.RoomSummary +import org.matrix.android.sdk.api.util.toMatrixItem +import javax.inject.Inject + +private val useAdaptiveIcon = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O +private const val adaptiveIconSizeDp = 108 +private const val adaptiveIconOuterSidesDp = 18 +private const val directShareCategory = BuildConfig.APPLICATION_ID + ".SHORTCUT_SHARE" + +class ShortcutCreator @Inject constructor( + private val context: Context, + private val avatarRenderer: AvatarRenderer, + private val dimensionConverter: DimensionConverter +) { + private val adaptiveIconSize = dimensionConverter.dpToPx(adaptiveIconSizeDp) + private val adaptiveIconOuterSides = dimensionConverter.dpToPx(adaptiveIconOuterSidesDp) + private val iconSize by lazy { + if (useAdaptiveIcon) { + adaptiveIconSize - adaptiveIconOuterSides + } else { + dimensionConverter.dpToPx(72) + } + } + + fun canCreateShortcut(): Boolean { + return ShortcutManagerCompat.isRequestPinShortcutSupported(context) + } + + @WorkerThread + fun create(roomSummary: RoomSummary): ShortcutInfoCompat { + val intent = RoomDetailActivity.shortcutIntent(context, roomSummary.roomId) + val bitmap = try { + avatarRenderer.shortcutDrawable(GlideApp.with(context), roomSummary.toMatrixItem(), iconSize) + } catch (failure: Throwable) { + null + } + return ShortcutInfoCompat.Builder(context, roomSummary.roomId) + .setShortLabel(roomSummary.displayName) + .setIcon(bitmap?.toProfileImageIcon()) + .setIntent(intent) + + // Make it show up in the direct share menu + .setCategories(setOf(directShareCategory)) + + .build() + } + + private fun Bitmap.toProfileImageIcon(): IconCompat { + return if (useAdaptiveIcon) { + IconCompat.createWithAdaptiveBitmap(this) + } else { + IconCompat.createWithBitmap(this) + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/ShortcutsHandler.kt b/vector/src/main/java/im/vector/app/features/home/ShortcutsHandler.kt index 1a476913f3..3684a8b3f8 100644 --- a/vector/src/main/java/im/vector/app/features/home/ShortcutsHandler.kt +++ b/vector/src/main/java/im/vector/app/features/home/ShortcutsHandler.kt @@ -18,40 +18,19 @@ package im.vector.app.features.home import android.content.Context import android.content.pm.ShortcutManager -import android.graphics.Bitmap import android.os.Build import androidx.core.content.getSystemService -import androidx.core.content.pm.ShortcutInfoCompat import androidx.core.content.pm.ShortcutManagerCompat -import androidx.core.graphics.drawable.IconCompat -import im.vector.app.core.glide.GlideApp -import im.vector.app.core.utils.DimensionConverter -import im.vector.app.features.home.room.detail.RoomDetailActivity -import org.matrix.android.sdk.api.util.toMatrixItem import io.reactivex.Observable import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import javax.inject.Inject -private val useAdaptiveIcon = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -private const val adaptiveIconSizeDp = 108 -private const val adaptiveIconOuterSidesDp = 18 - class ShortcutsHandler @Inject constructor( private val context: Context, private val homeRoomListStore: HomeRoomListDataSource, - private val avatarRenderer: AvatarRenderer, - private val dimensionConverter: DimensionConverter + private val shortcutCreator: ShortcutCreator ) { - private val adaptiveIconSize = dimensionConverter.dpToPx(adaptiveIconSizeDp) - private val adaptiveIconOuterSides = dimensionConverter.dpToPx(adaptiveIconOuterSidesDp) - private val iconSize by lazy { - if (useAdaptiveIcon) { - adaptiveIconSize - adaptiveIconOuterSides - } else { - dimensionConverter.dpToPx(72) - } - } fun observeRoomsAndBuildShortcuts(): Disposable { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) { @@ -67,19 +46,7 @@ class ShortcutsHandler @Inject constructor( val shortcuts = rooms .filter { room -> room.isFavorite } .take(n = 4) // Android only allows us to create 4 shortcuts - .map { room -> - val intent = RoomDetailActivity.shortcutIntent(context, room.roomId) - val bitmap = try { - avatarRenderer.shortcutDrawable(GlideApp.with(context), room.toMatrixItem(), iconSize) - } catch (failure: Throwable) { - null - } - ShortcutInfoCompat.Builder(context, room.roomId) - .setShortLabel(room.displayName) - .setIcon(bitmap?.toProfileImageIcon()) - .setIntent(intent) - .build() - } + .map { shortcutCreator.create(it) } ShortcutManagerCompat.removeAllDynamicShortcuts(context) ShortcutManagerCompat.addDynamicShortcuts(context, shortcuts) @@ -104,14 +71,4 @@ class ShortcutsHandler @Inject constructor( } } } - - // PRIVATE API ********************************************************************************* - - private fun Bitmap.toProfileImageIcon(): IconCompat { - return if (useAdaptiveIcon) { - IconCompat.createWithAdaptiveBitmap(this) - } else { - IconCompat.createWithBitmap(this) - } - } } diff --git a/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt index eeb1639045..3bdcfc4018 100644 --- a/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt @@ -30,6 +30,8 @@ import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModelAction import im.vector.app.features.settings.VectorPreferences +import io.reactivex.Observable +import io.reactivex.functions.Function3 import org.matrix.android.sdk.api.NoOpMatrixCallback import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.Session @@ -39,8 +41,6 @@ import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo import org.matrix.android.sdk.internal.crypto.store.PrivateKeysInfo -import io.reactivex.Observable -import io.reactivex.functions.Function3 import org.matrix.android.sdk.rx.rx import timber.log.Timber import java.util.concurrent.TimeUnit diff --git a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt index 4ddc4bf972..8a32157097 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt @@ -24,11 +24,11 @@ import com.squareup.inject.assisted.AssistedInject import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel +import io.reactivex.schedulers.Schedulers import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams -import io.reactivex.schedulers.Schedulers import org.matrix.android.sdk.rx.rx class BreadcrumbsViewModel @AssistedInject constructor(@Assisted initialState: BreadcrumbsViewState, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt index 4bdf2e7e57..88eb1b5109 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt @@ -51,7 +51,7 @@ sealed class RoomDetailAction : VectorViewModelAction { data class EnterEditMode(val eventId: String, val text: String) : RoomDetailAction() data class EnterQuoteMode(val eventId: String, val text: String) : RoomDetailAction() data class EnterReplyMode(val eventId: String, val text: String) : RoomDetailAction() - data class ExitSpecialMode(val text: String) : RoomDetailAction() + data class EnterRegularMode(val text: String, val fromSharing: Boolean) : RoomDetailAction() data class ResendMessage(val eventId: String) : RoomDetailAction() data class RemoveFailedEcho(val eventId: String) : RoomDetailAction() @@ -81,11 +81,13 @@ sealed class RoomDetailAction : VectorViewModelAction { data class ReRequestKeys(val eventId: String) : RoomDetailAction() object SelectStickerAttachment : RoomDetailAction() - object OpenIntegrationManager: RoomDetailAction() - object ManageIntegrations: RoomDetailAction() - data class AddJitsiWidget(val withVideo: Boolean): RoomDetailAction() - data class RemoveWidget(val widgetId: String): RoomDetailAction() + object OpenIntegrationManager : RoomDetailAction() + object ManageIntegrations : RoomDetailAction() + data class AddJitsiWidget(val withVideo: Boolean) : RoomDetailAction() + data class RemoveWidget(val widgetId: String) : RoomDetailAction() data class EnsureNativeWidgetAllowed(val widget: Widget, val userJustAccepted: Boolean, val grantedEvents: RoomDetailViewEvents) : RoomDetailAction() + + data class JumpToReadReceipt(val userId: String) : RoomDetailAction() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index b0e43e3396..af806c729c 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -17,7 +17,7 @@ package im.vector.app.features.home.room.detail import android.annotation.SuppressLint -import android.app.Activity.RESULT_OK +import android.app.Activity import android.content.DialogInterface import android.content.Intent import android.graphics.Typeface @@ -53,6 +53,8 @@ import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import com.airbnb.epoxy.EpoxyModel import com.airbnb.epoxy.OnModelBuildFinishedListener +import com.airbnb.epoxy.addGlidePreloader +import com.airbnb.epoxy.glidePreloader import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading @@ -71,10 +73,12 @@ import im.vector.app.core.epoxy.LayoutManagerStateRestorer import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.hideKeyboard +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.extensions.showKeyboard import im.vector.app.core.extensions.trackItemsVisibilityChange import im.vector.app.core.glide.GlideApp +import im.vector.app.core.glide.GlideRequests import im.vector.app.core.intent.getMimeTypeFromUri import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.resources.ColorProvider @@ -88,21 +92,18 @@ import im.vector.app.core.utils.KeyboardStateUtils import im.vector.app.core.utils.PERMISSIONS_FOR_AUDIO_IP_CALL import im.vector.app.core.utils.PERMISSIONS_FOR_VIDEO_IP_CALL import im.vector.app.core.utils.PERMISSIONS_FOR_WRITING_FILES -import im.vector.app.core.utils.PERMISSION_REQUEST_CODE_INCOMING_URI -import im.vector.app.core.utils.PERMISSION_REQUEST_CODE_PICK_ATTACHMENT import im.vector.app.core.utils.TextUtils -import im.vector.app.core.utils.allGranted import im.vector.app.core.utils.checkPermissions import im.vector.app.core.utils.colorizeMatchingText import im.vector.app.core.utils.copyToClipboard import im.vector.app.core.utils.createJSonViewerStyleProvider import im.vector.app.core.utils.createUIHandler import im.vector.app.core.utils.isValidUrl -import im.vector.app.core.utils.onPermissionResultAudioIpCall -import im.vector.app.core.utils.onPermissionResultVideoIpCall import im.vector.app.core.utils.openUrlInExternalBrowser +import im.vector.app.core.utils.registerForPermissionsResult import im.vector.app.core.utils.saveMedia import im.vector.app.core.utils.shareMedia +import im.vector.app.core.utils.shareText import im.vector.app.core.utils.toast import im.vector.app.features.attachments.AttachmentTypeSelectorView import im.vector.app.features.attachments.AttachmentsHelper @@ -136,7 +137,6 @@ import im.vector.app.features.home.room.detail.timeline.item.MessageTextItem import im.vector.app.features.home.room.detail.timeline.item.ReadReceiptData import im.vector.app.features.home.room.detail.timeline.reactions.ViewReactionsBottomSheet import im.vector.app.features.home.room.detail.widget.RoomWidgetsBottomSheet -import im.vector.app.features.home.room.detail.widget.WidgetRequestCodes import im.vector.app.features.html.EventHtmlRenderer import im.vector.app.features.html.PillImageSpan import im.vector.app.features.invite.VectorInviteView @@ -205,8 +205,6 @@ data class RoomDetailArgs( val sharedData: SharedData? = null ) : Parcelable -private const val REACTION_SELECT_REQUEST_CODE = 0 - class RoomDetailFragment @Inject constructor( private val session: Session, private val avatarRenderer: AvatarRenderer, @@ -220,7 +218,9 @@ class RoomDetailFragment @Inject constructor( private val colorProvider: ColorProvider, private val notificationUtils: NotificationUtils, private val webRtcPeerConnectionManager: WebRtcPeerConnectionManager, - private val matrixItemColorProvider: MatrixItemColorProvider + private val matrixItemColorProvider: MatrixItemColorProvider, + private val imageContentRenderer: ImageContentRenderer, + private val roomDetailPendingActionStore: RoomDetailPendingActionStore ) : VectorBaseFragment(), TimelineEventController.Callback, @@ -232,11 +232,6 @@ class RoomDetailFragment @Inject constructor( ActiveCallView.Callback { companion object { - - private const val AUDIO_CALL_PERMISSION_REQUEST_CODE = 1 - private const val VIDEO_CALL_PERMISSION_REQUEST_CODE = 2 - private const val SAVE_ATTACHEMENT_REQUEST_CODE = 3 - /** * Sanitize the display name. * @@ -369,6 +364,10 @@ class RoomDetailFragment @Inject constructor( is RoomDetailViewEvents.RequestNativeWidgetPermission -> requestNativeWidgetPermission(it) }.exhaustive } + + if (savedInstanceState == null) { + handleShareData() + } } private fun requestNativeWidgetPermission(it: RoomDetailViewEvents.RequestNativeWidgetPermission) { @@ -399,9 +398,14 @@ class RoomDetailFragment @Inject constructor( } } + private val integrationManagerActivityResultLauncher = registerStartForActivityResult { + // Noop + } + private fun openIntegrationManager(screen: String? = null) { navigator.openIntegrationManager( - fragment = this, + context = requireContext(), + activityResultLauncher = integrationManagerActivityResultLauncher, roomId = roomDetailArgs.roomId, integId = null, screen = screen @@ -438,7 +442,7 @@ class RoomDetailFragment @Inject constructor( } private fun openStickerPicker(event: RoomDetailViewEvents.OpenStickerPicker) { - navigator.openStickerPicker(this, roomDetailArgs.roomId, event.widget) + navigator.openStickerPicker(requireContext(), stickerActivityResultLauncher, roomDetailArgs.roomId, event.widget) } private fun startOpenFileIntent(action: RoomDetailViewEvents.OpenFile) { @@ -478,21 +482,17 @@ class RoomDetailFragment @Inject constructor( navigator.openRoom(vectorBaseActivity, action.roomId) } - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - if (savedInstanceState == null) { - when (val sharedData = roomDetailArgs.sharedData) { - is SharedData.Text -> { - // Save a draft to set the shared text to the composer - roomDetailViewModel.handle(RoomDetailAction.SaveDraft(sharedData.text)) - } - is SharedData.Attachments -> { - // open share edition - onContentAttachmentsReady(sharedData.attachmentData) - } - null -> Timber.v("No share data to process") - }.exhaustive - } + private fun handleShareData() { + when (val sharedData = roomDetailArgs.sharedData) { + is SharedData.Text -> { + roomDetailViewModel.handle(RoomDetailAction.EnterRegularMode(sharedData.text, fromSharing = true)) + } + is SharedData.Attachments -> { + // open share edition + onContentAttachmentsReady(sharedData.attachmentData) + } + null -> Timber.v("No share data to process") + }.exhaustive } override fun onDestroyView() { @@ -616,8 +616,8 @@ class RoomDetailFragment @Inject constructor( withState(roomDetailViewModel) { state -> // Set the visual state of the call buttons (voice/video) to enabled/disabled according to user permissions val callButtonsEnabled = when (state.asyncRoomSummary.invoke()?.joinedMembersCount) { - 1 -> false - 2 -> state.isAllowedToStartWebRTCCall + 1 -> false + 2 -> state.isAllowedToStartWebRTCCall else -> state.isAllowedToManageWidgets } setOf(R.id.voice_call, R.id.video_call).forEach { @@ -654,6 +654,14 @@ class RoomDetailFragment @Inject constructor( roomDetailViewModel.handle(RoomDetailAction.ClearSendQueue) true } + R.id.invite -> { + navigator.openInviteUsersToRoom(requireActivity(), roomDetailArgs.roomId) + true + } + R.id.timeline_setting -> { + navigator.openRoomProfile(requireActivity(), roomDetailArgs.roomId) + true + } R.id.resend_all -> { roomDetailViewModel.handle(RoomDetailAction.ResendAll) true @@ -679,10 +687,22 @@ class RoomDetailFragment @Inject constructor( navigator.openRoomProfile(requireActivity(), roomDetailArgs.roomId) true } + R.id.search -> { + handleSearchAction() + true + } else -> super.onOptionsItemSelected(item) } } + private fun handleSearchAction() { + if (session.getRoom(roomDetailArgs.roomId)?.isEncrypted() == false) { + navigator.openSearch(requireContext(), roomDetailArgs.roomId) + } else { + showDialogWithMessage(getString(R.string.search_is_not_supported_in_e2e_room)) + } + } + private fun handleCallRequest(item: MenuItem) = withState(roomDetailViewModel) { state -> val roomSummary = state.asyncRoomSummary.invoke() ?: return@withState val isVideoCall = item.itemId == R.id.video_call @@ -710,7 +730,13 @@ class RoomDetailFragment @Inject constructor( // safeStartCall(it, isVideoCall) // } } else if (!state.isAllowedToStartWebRTCCall) { - showDialogWithMessage(getString(R.string.no_permissions_to_start_webrtc_call)) + showDialogWithMessage(getString( + if (state.isDm()) { + R.string.no_permissions_to_start_webrtc_call_in_direct_room + } else { + R.string.no_permissions_to_start_webrtc_call + }) + ) } else { safeStartCall(isVideoCall) } @@ -720,7 +746,13 @@ class RoomDetailFragment @Inject constructor( // can you add widgets?? if (!state.isAllowedToManageWidgets) { // You do not have permission to start a conference call in this room - showDialogWithMessage(getString(R.string.no_permissions_to_start_conf_call)) + showDialogWithMessage(getString( + if (state.isDm()) { + R.string.no_permissions_to_start_conf_call_in_direct_room + } else { + R.string.no_permissions_to_start_conf_call + } + )) } else { if (state.activeRoomWidgets()?.filter { it.type == WidgetType.Jitsi }?.any() == true) { // A conference is already in progress! @@ -766,19 +798,33 @@ class RoomDetailFragment @Inject constructor( } } + private val startCallActivityResultLauncher = registerForPermissionsResult { allGranted -> + if (allGranted) { + (roomDetailViewModel.pendingAction as? RoomDetailAction.StartCall)?.let { + roomDetailViewModel.pendingAction = null + roomDetailViewModel.handle(it) + } + } else { + context?.toast(R.string.permissions_action_not_performed_missing_permissions) + cleanUpAfterPermissionNotGranted() + } + } + private fun safeStartCall2(isVideoCall: Boolean) { val startCallAction = RoomDetailAction.StartCall(isVideoCall) roomDetailViewModel.pendingAction = startCallAction if (isVideoCall) { if (checkPermissions(PERMISSIONS_FOR_VIDEO_IP_CALL, - this, VIDEO_CALL_PERMISSION_REQUEST_CODE, + requireActivity(), + startCallActivityResultLauncher, R.string.permissions_rationale_msg_camera_and_audio)) { roomDetailViewModel.pendingAction = null roomDetailViewModel.handle(startCallAction) } } else { if (checkPermissions(PERMISSIONS_FOR_AUDIO_IP_CALL, - this, AUDIO_CALL_PERMISSION_REQUEST_CODE, + requireActivity(), + startCallActivityResultLauncher, R.string.permissions_rationale_msg_record_audio)) { roomDetailViewModel.pendingAction = null roomDetailViewModel.handle(startCallAction) @@ -844,6 +890,17 @@ class RoomDetailFragment @Inject constructor( override fun onResume() { super.onResume() notificationDrawerManager.setCurrentRoom(roomDetailArgs.roomId) + roomDetailPendingActionStore.data?.let { handlePendingAction(it) } + roomDetailPendingActionStore.data = null + } + + private fun handlePendingAction(roomDetailPendingAction: RoomDetailPendingAction) { + when (roomDetailPendingAction) { + is RoomDetailPendingAction.JumpToReadReceipt -> + roomDetailViewModel.handle(RoomDetailAction.JumpToReadReceipt(roomDetailPendingAction.userId)) + is RoomDetailPendingAction.MentionUser -> + insertUserDisplayNameInTextEditor(roomDetailPendingAction.userId) + }.exhaustive } override fun onPause() { @@ -854,27 +911,63 @@ class RoomDetailFragment @Inject constructor( roomDetailViewModel.handle(RoomDetailAction.SaveDraft(composerLayout.composerEditText.text.toString())) } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - val hasBeenHandled = attachmentsHelper.onActivityResult(requestCode, resultCode, data) - if (!hasBeenHandled && resultCode == RESULT_OK && data != null) { - when (requestCode) { - AttachmentsPreviewActivity.REQUEST_CODE -> { - val sendData = AttachmentsPreviewActivity.getOutput(data) - val keepOriginalSize = AttachmentsPreviewActivity.getKeepOriginalSize(data) - roomDetailViewModel.handle(RoomDetailAction.SendMedia(sendData, !keepOriginalSize)) - } - REACTION_SELECT_REQUEST_CODE -> { - val (eventId, reaction) = EmojiReactionPickerActivity.getOutput(data) ?: return - roomDetailViewModel.handle(RoomDetailAction.SendReaction(eventId, reaction)) - } - WidgetRequestCodes.STICKER_PICKER_REQUEST_CODE -> { - val content = WidgetActivity.getOutput(data).toModel() ?: return - roomDetailViewModel.handle(RoomDetailAction.SendSticker(content)) - } + private val attachmentFileActivityResultLauncher = registerStartForActivityResult { + if (it.resultCode == Activity.RESULT_OK) { + attachmentsHelper.onImageResult(it.data) + } + } + + private val attachmentAudioActivityResultLauncher = registerStartForActivityResult { + if (it.resultCode == Activity.RESULT_OK) { + attachmentsHelper.onAudioResult(it.data) + } + } + + private val attachmentContactActivityResultLauncher = registerStartForActivityResult { + if (it.resultCode == Activity.RESULT_OK) { + attachmentsHelper.onContactResult(it.data) + } + } + + private val attachmentImageActivityResultLauncher = registerStartForActivityResult { + if (it.resultCode == Activity.RESULT_OK) { + attachmentsHelper.onImageResult(it.data) + } + } + + private val attachmentPhotoActivityResultLauncher = registerStartForActivityResult { + if (it.resultCode == Activity.RESULT_OK) { + attachmentsHelper.onPhotoResult() + } + } + + private val contentAttachmentActivityResultLauncher = registerStartForActivityResult { activityResult -> + val data = activityResult.data ?: return@registerStartForActivityResult + if (activityResult.resultCode == Activity.RESULT_OK) { + val sendData = AttachmentsPreviewActivity.getOutput(data) + val keepOriginalSize = AttachmentsPreviewActivity.getKeepOriginalSize(data) + roomDetailViewModel.handle(RoomDetailAction.SendMedia(sendData, !keepOriginalSize)) + } + } + + private val emojiActivityResultLauncher = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + val eventId = EmojiReactionPickerActivity.getOutputEventId(activityResult.data) + val reaction = EmojiReactionPickerActivity.getOutputReaction(activityResult.data) + if (eventId != null && reaction != null) { + roomDetailViewModel.handle(RoomDetailAction.SendReaction(eventId, reaction)) } } - // TODO why don't we call super here? - // super.onActivityResult(requestCode, resultCode, data) + } + + private val stickerActivityResultLauncher = registerStartForActivityResult { activityResult -> + val data = activityResult.data ?: return@registerStartForActivityResult + if (activityResult.resultCode == Activity.RESULT_OK) { + WidgetActivity.getOutput(data).toModel() + ?.let { content -> + roomDetailViewModel.handle(RoomDetailAction.SendSticker(content)) + } + } } // PRIVATE METHODS ***************************************************************************** @@ -931,6 +1024,16 @@ class RoomDetailFragment @Inject constructor( val touchHelper = ItemTouchHelper(swipeCallback) touchHelper.attachToRecyclerView(recyclerView) } + recyclerView.addGlidePreloader( + epoxyController = timelineEventController, + requestManager = GlideApp.with(this), + preloader = glidePreloader { requestManager, epoxyModel: MessageImageVideoItem, _ -> + imageContentRenderer.createGlideRequest( + epoxyModel.mediaData, + ImageContentRenderer.Mode.THUMBNAIL, + requestManager as GlideRequests + ) + }) } private fun updateJumpToReadMarkerViewVisibility() { @@ -959,6 +1062,18 @@ class RoomDetailFragment @Inject constructor( } } + private val writingFileActivityResultLauncher = registerForPermissionsResult { allGranted -> + if (allGranted) { + val pendingUri = roomDetailViewModel.pendingUri + if (pendingUri != null) { + roomDetailViewModel.pendingUri = null + sendUri(pendingUri) + } + } else { + cleanUpAfterPermissionNotGranted() + } + } + private fun setupComposer() { autoCompleter.setup(composerLayout.composerEditText) @@ -986,12 +1101,12 @@ class RoomDetailFragment @Inject constructor( } override fun onCloseRelatedMessage() { - roomDetailViewModel.handle(RoomDetailAction.ExitSpecialMode(composerLayout.text.toString())) + roomDetailViewModel.handle(RoomDetailAction.EnterRegularMode(composerLayout.text.toString(), false)) } override fun onRichContentSelected(contentUri: Uri): Boolean { // We need WRITE_EXTERNAL permission - return if (checkPermissions(PERMISSIONS_FOR_WRITING_FILES, this@RoomDetailFragment, PERMISSION_REQUEST_CODE_INCOMING_URI)) { + return if (checkPermissions(PERMISSIONS_FOR_WRITING_FILES, requireActivity(), writingFileActivityResultLauncher)) { sendUri(contentUri) } else { roomDetailViewModel.pendingUri = contentUri @@ -1119,12 +1234,8 @@ class RoomDetailFragment @Inject constructor( private fun renderSendMessageResult(sendMessageResult: RoomDetailViewEvents.SendMessageResult) { when (sendMessageResult) { - is RoomDetailViewEvents.MessageSent -> { - updateComposerText("") - } is RoomDetailViewEvents.SlashCommandHandled -> { sendMessageResult.messageRes?.let { showSnackWithMessage(getString(it)) } - updateComposerText("") } is RoomDetailViewEvents.SlashCommandError -> { displayCommandError(getString(R.string.command_problem_with_parameters, sendMessageResult.command.command)) @@ -1385,52 +1496,11 @@ class RoomDetailFragment @Inject constructor( // // } // } - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { - if (allGranted(grantResults)) { - when (requestCode) { - SAVE_ATTACHEMENT_REQUEST_CODE -> { - sharedActionViewModel.pendingAction?.let { - handleActions(it) - sharedActionViewModel.pendingAction = null - } - } - PERMISSION_REQUEST_CODE_INCOMING_URI -> { - val pendingUri = roomDetailViewModel.pendingUri - if (pendingUri != null) { - roomDetailViewModel.pendingUri = null - sendUri(pendingUri) - } - } - PERMISSION_REQUEST_CODE_PICK_ATTACHMENT -> { - val pendingType = attachmentsHelper.pendingType - if (pendingType != null) { - attachmentsHelper.pendingType = null - launchAttachmentProcess(pendingType) - } - } - AUDIO_CALL_PERMISSION_REQUEST_CODE -> { - if (onPermissionResultAudioIpCall(requireContext(), grantResults)) { - (roomDetailViewModel.pendingAction as? RoomDetailAction.StartCall)?.let { - roomDetailViewModel.pendingAction = null - roomDetailViewModel.handle(it) - } - } - } - VIDEO_CALL_PERMISSION_REQUEST_CODE -> { - if (onPermissionResultVideoIpCall(requireContext(), grantResults)) { - (roomDetailViewModel.pendingAction as? RoomDetailAction.StartCall)?.let { - roomDetailViewModel.pendingAction = null - roomDetailViewModel.handle(it) - } - } - } - } - } else { - // Reset all pending data - roomDetailViewModel.pendingAction = null - roomDetailViewModel.pendingUri = null - attachmentsHelper.pendingType = null - } + private fun cleanUpAfterPermissionNotGranted() { + // Reset all pending data + roomDetailViewModel.pendingAction = null + roomDetailViewModel.pendingUri = null + attachmentsHelper.pendingType = null } // override fun onAudioMessageClicked(messageAudioContent: MessageAudioContent) { @@ -1528,26 +1598,41 @@ class RoomDetailFragment @Inject constructor( } private fun onShareActionClicked(action: EventSharedAction.Share) { - session.fileService().downloadFile( - downloadMode = FileService.DownloadMode.FOR_EXTERNAL_SHARE, - id = action.eventId, - fileName = action.messageContent.body, - mimeType = action.messageContent.mimeType, - url = action.messageContent.getFileUrl(), - elementToDecrypt = action.messageContent.encryptedFileInfo?.toElementToDecrypt(), - callback = object : MatrixCallback { - override fun onSuccess(data: File) { - if (isAdded) { - shareMedia(requireContext(), data, getMimeTypeFromUri(requireContext(), data.toUri())) + if (action.messageContent is MessageTextContent) { + shareText(requireContext(), action.messageContent.body) + } else if (action.messageContent is MessageWithAttachmentContent) { + session.fileService().downloadFile( + downloadMode = FileService.DownloadMode.FOR_EXTERNAL_SHARE, + id = action.eventId, + fileName = action.messageContent.body, + mimeType = action.messageContent.mimeType, + url = action.messageContent.getFileUrl(), + elementToDecrypt = action.messageContent.encryptedFileInfo?.toElementToDecrypt(), + callback = object : MatrixCallback { + override fun onSuccess(data: File) { + if (isAdded) { + shareMedia(requireContext(), data, getMimeTypeFromUri(requireContext(), data.toUri())) + } } } - } - ) + ) + } + } + + private val saveActionActivityResultLauncher = registerForPermissionsResult { allGranted -> + if (allGranted) { + sharedActionViewModel.pendingAction?.let { + handleActions(it) + sharedActionViewModel.pendingAction = null + } + } else { + cleanUpAfterPermissionNotGranted() + } } private fun onSaveActionClicked(action: EventSharedAction.Save) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q - && !checkPermissions(PERMISSIONS_FOR_WRITING_FILES, this, SAVE_ATTACHEMENT_REQUEST_CODE)) { + && !checkPermissions(PERMISSIONS_FOR_WRITING_FILES, requireActivity(), saveActionActivityResultLauncher)) { sharedActionViewModel.pendingAction = action return } @@ -1580,7 +1665,7 @@ class RoomDetailFragment @Inject constructor( openRoomMemberProfile(action.userId) } is EventSharedAction.AddReaction -> { - startActivityForResult(EmojiReactionPickerActivity.intent(requireContext(), action.eventId), REACTION_SELECT_REQUEST_CODE) + emojiActivityResultLauncher.launch(EmojiReactionPickerActivity.intent(requireContext(), action.eventId)) } is EventSharedAction.ViewReactions -> { ViewReactionsBottomSheet.newInstance(roomDetailArgs.roomId, action.messageInformationData) @@ -1787,8 +1872,20 @@ class RoomDetailFragment @Inject constructor( // AttachmentTypeSelectorView.Callback + private val typeSelectedActivityResultLauncher = registerForPermissionsResult { allGranted -> + if (allGranted) { + val pendingType = attachmentsHelper.pendingType + if (pendingType != null) { + attachmentsHelper.pendingType = null + launchAttachmentProcess(pendingType) + } + } else { + cleanUpAfterPermissionNotGranted() + } + } + override fun onTypeSelected(type: AttachmentTypeSelectorView.Type) { - if (checkPermissions(type.permissionsBit, this, PERMISSION_REQUEST_CODE_PICK_ATTACHMENT)) { + if (checkPermissions(type.permissionsBit, requireActivity(), typeSelectedActivityResultLauncher)) { launchAttachmentProcess(type) } else { attachmentsHelper.pendingType = type @@ -1797,11 +1894,11 @@ class RoomDetailFragment @Inject constructor( private fun launchAttachmentProcess(type: AttachmentTypeSelectorView.Type) { when (type) { - AttachmentTypeSelectorView.Type.CAMERA -> attachmentsHelper.openCamera(this) - AttachmentTypeSelectorView.Type.FILE -> attachmentsHelper.selectFile(this) - AttachmentTypeSelectorView.Type.GALLERY -> attachmentsHelper.selectGallery(this) - AttachmentTypeSelectorView.Type.AUDIO -> attachmentsHelper.selectAudio(this) - AttachmentTypeSelectorView.Type.CONTACT -> attachmentsHelper.selectContact(this) + AttachmentTypeSelectorView.Type.CAMERA -> attachmentsHelper.openCamera(requireContext(), attachmentPhotoActivityResultLauncher) + AttachmentTypeSelectorView.Type.FILE -> attachmentsHelper.selectFile(attachmentFileActivityResultLauncher) + AttachmentTypeSelectorView.Type.GALLERY -> attachmentsHelper.selectGallery(attachmentImageActivityResultLauncher) + AttachmentTypeSelectorView.Type.AUDIO -> attachmentsHelper.selectAudio(attachmentAudioActivityResultLauncher) + AttachmentTypeSelectorView.Type.CONTACT -> attachmentsHelper.selectContact(attachmentContactActivityResultLauncher) AttachmentTypeSelectorView.Type.STICKER -> roomDetailViewModel.handle(RoomDetailAction.SelectStickerAttachment) }.exhaustive } @@ -1820,7 +1917,7 @@ class RoomDetailFragment @Inject constructor( } if (grouped.previewables.isNotEmpty()) { val intent = AttachmentsPreviewActivity.newIntent(requireContext(), AttachmentsPreviewArgs(grouped.previewables)) - startActivityForResult(intent, AttachmentsPreviewActivity.REQUEST_CODE) + contentAttachmentActivityResultLauncher.launch(intent) } } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/widget/WidgetRequestCodes.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailPendingAction.kt similarity index 71% rename from vector/src/main/java/im/vector/app/features/home/room/detail/widget/WidgetRequestCodes.kt rename to vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailPendingAction.kt index 5f0e6866db..394d46ef8d 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/widget/WidgetRequestCodes.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailPendingAction.kt @@ -14,9 +14,9 @@ * limitations under the License. */ -package im.vector.app.features.home.room.detail.widget +package im.vector.app.features.home.room.detail -object WidgetRequestCodes { - const val STICKER_PICKER_REQUEST_CODE = 16000 - const val INTEGRATION_MANAGER_REQUEST_CODE = 16001 +sealed class RoomDetailPendingAction { + data class JumpToReadReceipt(val userId: String) : RoomDetailPendingAction() + data class MentionUser(val userId: String) : RoomDetailPendingAction() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailPendingActionStore.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailPendingActionStore.kt new file mode 100644 index 0000000000..9ffbb83a47 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailPendingActionStore.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.detail + +import im.vector.app.core.utils.TemporaryStore +import javax.inject.Inject +import javax.inject.Singleton + +// Store to keep a pending action from sub screen of a room detail +@Singleton +class RoomDetailPendingActionStore @Inject constructor() : TemporaryStore(10_000) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt index 29ed43f17d..ee2d193473 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt @@ -41,8 +41,8 @@ sealed class RoomDetailViewEvents : VectorViewEvents { data class NavigateToEvent(val eventId: String) : RoomDetailViewEvents() data class JoinJitsiConference(val widget: Widget, val withVideo: Boolean) : RoomDetailViewEvents() - object ShowWaitingView: RoomDetailViewEvents() - object HideWaitingView: RoomDetailViewEvents() + object ShowWaitingView : RoomDetailViewEvents() + object HideWaitingView : RoomDetailViewEvents() data class FileTooBigError( val filename: String, @@ -64,14 +64,14 @@ sealed class RoomDetailViewEvents : VectorViewEvents { abstract class SendMessageResult : RoomDetailViewEvents() - object DisplayPromptForIntegrationManager: RoomDetailViewEvents() + object DisplayPromptForIntegrationManager : RoomDetailViewEvents() - object DisplayEnableIntegrationsWarning: RoomDetailViewEvents() + object DisplayEnableIntegrationsWarning : RoomDetailViewEvents() - data class OpenStickerPicker(val widget: Widget): RoomDetailViewEvents() + data class OpenStickerPicker(val widget: Widget) : RoomDetailViewEvents() - object OpenIntegrationManager: RoomDetailViewEvents() - object OpenActiveWidgetBottomSheet: RoomDetailViewEvents() + object OpenIntegrationManager : RoomDetailViewEvents() + object OpenActiveWidgetBottomSheet : RoomDetailViewEvents() data class RequestNativeWidgetPermission(val widget: Widget, val domain: String, val grantedEvents: RoomDetailViewEvents) : RoomDetailViewEvents() @@ -83,6 +83,7 @@ sealed class RoomDetailViewEvents : VectorViewEvents { data class SlashCommandHandled(@StringRes val messageRes: Int? = null) : SendMessageResult() object SlashCommandResultOk : SendMessageResult() class SlashCommandResultError(val throwable: Throwable) : SendMessageResult() + // TODO Remove object SlashCommandNotImplemented : SendMessageResult() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index c459090245..f9a8669744 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -164,7 +164,7 @@ class RoomDetailViewModel @AssistedInject constructor( getUnreadState() observeSyncState() observeEventDisplayedActions() - observeDrafts() + getDraftIfAny() observeUnreadState() observeMyRoomMember() observeActiveRoomWidgets() @@ -180,11 +180,13 @@ class RoomDetailViewModel @AssistedInject constructor( PowerLevelsObservableFactory(room).createObservable() .subscribe { val canSendMessage = PowerLevelsHelper(it).isUserAllowedToSend(session.myUserId, false, EventType.MESSAGE) + val canInvite = PowerLevelsHelper(it).isUserAbleToInvite(session.myUserId) val isAllowedToManageWidgets = session.widgetService().hasPermissionsToHandleWidgets(room.roomId) val isAllowedToStartWebRTCCall = PowerLevelsHelper(it).isUserAllowedToSend(session.myUserId, false, EventType.CALL_INVITE) setState { copy( canSendMessage = canSendMessage, + canInvite = canInvite, isAllowedToManageWidgets = isAllowedToManageWidgets, isAllowedToStartWebRTCCall = isAllowedToStartWebRTCCall ) @@ -240,7 +242,7 @@ class RoomDetailViewModel @AssistedInject constructor( is RoomDetailAction.RedactAction -> handleRedactEvent(action) is RoomDetailAction.UndoReaction -> handleUndoReact(action) is RoomDetailAction.UpdateQuickReactAction -> handleUpdateQuickReaction(action) - is RoomDetailAction.ExitSpecialMode -> handleExitSpecialMode(action) + is RoomDetailAction.EnterRegularMode -> handleEnterRegularMode(action) is RoomDetailAction.EnterEditMode -> handleEditAction(action) is RoomDetailAction.EnterQuoteMode -> handleQuoteAction(action) is RoomDetailAction.EnterReplyMode -> handleReplyAction(action) @@ -272,9 +274,15 @@ class RoomDetailViewModel @AssistedInject constructor( is RoomDetailAction.RemoveWidget -> handleDeleteWidget(action.widgetId) is RoomDetailAction.EnsureNativeWidgetAllowed -> handleCheckWidgetAllowed(action) is RoomDetailAction.CancelSend -> handleCancel(action) + is RoomDetailAction.JumpToReadReceipt -> handleJumpToReadReceipt(action) }.exhaustive } + private fun handleJumpToReadReceipt(action: RoomDetailAction.JumpToReadReceipt) { + room.getUserReadReceipt(action.userId) + ?.let { handleNavigateToEvent(RoomDetailAction.NavigateToEvent(it, true)) } + } + private fun handleSendSticker(action: RoomDetailAction.SendSticker) { room.sendEvent(EventType.STICKER, action.stickerContent.toContent()) } @@ -449,47 +457,52 @@ class RoomDetailViewModel @AssistedInject constructor( /** * Convert a send mode to a draft and save the draft */ - private fun handleSaveDraft(action: RoomDetailAction.SaveDraft) { - withState { - when (it.sendMode) { - is SendMode.REGULAR -> room.saveDraft(UserDraft.REGULAR(action.draft), NoOpMatrixCallback()) - is SendMode.REPLY -> room.saveDraft(UserDraft.REPLY(it.sendMode.timelineEvent.root.eventId!!, action.draft), NoOpMatrixCallback()) - is SendMode.QUOTE -> room.saveDraft(UserDraft.QUOTE(it.sendMode.timelineEvent.root.eventId!!, action.draft), NoOpMatrixCallback()) - is SendMode.EDIT -> room.saveDraft(UserDraft.EDIT(it.sendMode.timelineEvent.root.eventId!!, action.draft), NoOpMatrixCallback()) - }.exhaustive + private fun handleSaveDraft(action: RoomDetailAction.SaveDraft) = withState { + when { + it.sendMode is SendMode.REGULAR && !it.sendMode.fromSharing -> { + setState { copy(sendMode = it.sendMode.copy(action.draft)) } + room.saveDraft(UserDraft.REGULAR(action.draft), NoOpMatrixCallback()) + } + it.sendMode is SendMode.REPLY -> { + setState { copy(sendMode = it.sendMode.copy(text = action.draft)) } + room.saveDraft(UserDraft.REPLY(it.sendMode.timelineEvent.root.eventId!!, action.draft), NoOpMatrixCallback()) + } + it.sendMode is SendMode.QUOTE -> { + setState { copy(sendMode = it.sendMode.copy(text = action.draft)) } + room.saveDraft(UserDraft.QUOTE(it.sendMode.timelineEvent.root.eventId!!, action.draft), NoOpMatrixCallback()) + } + it.sendMode is SendMode.EDIT -> { + setState { copy(sendMode = it.sendMode.copy(text = action.draft)) } + room.saveDraft(UserDraft.EDIT(it.sendMode.timelineEvent.root.eventId!!, action.draft), NoOpMatrixCallback()) + } } } - private fun observeDrafts() { - room.rx().liveDrafts() - .subscribe { - Timber.d("Draft update --> SetState") - setState { - val draft = it.lastOrNull() ?: UserDraft.REGULAR("") - copy( - // Create a sendMode from a draft and retrieve the TimelineEvent - sendMode = when (draft) { - is UserDraft.REGULAR -> SendMode.REGULAR(draft.text) - is UserDraft.QUOTE -> { - room.getTimeLineEvent(draft.linkedEventId)?.let { timelineEvent -> - SendMode.QUOTE(timelineEvent, draft.text) - } - } - is UserDraft.REPLY -> { - room.getTimeLineEvent(draft.linkedEventId)?.let { timelineEvent -> - SendMode.REPLY(timelineEvent, draft.text) - } - } - is UserDraft.EDIT -> { - room.getTimeLineEvent(draft.linkedEventId)?.let { timelineEvent -> - SendMode.EDIT(timelineEvent, draft.text) - } - } - } ?: SendMode.REGULAR("") - ) - } - } - .disposeOnClear() + private fun getDraftIfAny() { + val currentDraft = room.getDraft() ?: return + setState { + copy( + // Create a sendMode from a draft and retrieve the TimelineEvent + sendMode = when (currentDraft) { + is UserDraft.REGULAR -> SendMode.REGULAR(currentDraft.text, false) + is UserDraft.QUOTE -> { + room.getTimeLineEvent(currentDraft.linkedEventId)?.let { timelineEvent -> + SendMode.QUOTE(timelineEvent, currentDraft.text) + } + } + is UserDraft.REPLY -> { + room.getTimeLineEvent(currentDraft.linkedEventId)?.let { timelineEvent -> + SendMode.REPLY(timelineEvent, currentDraft.text) + } + } + is UserDraft.EDIT -> { + room.getTimeLineEvent(currentDraft.linkedEventId)?.let { timelineEvent -> + SendMode.EDIT(timelineEvent, currentDraft.text) + } + } + } ?: SendMode.REGULAR("", fromSharing = false) + ) + } } private fun handleUserIsTyping(action: RoomDetailAction.UserIsTyping) { @@ -533,6 +546,8 @@ class RoomDetailViewModel @AssistedInject constructor( // For now always disable when not in developer mode, worker cancellation is not working properly timeline.pendingEventCount() > 0 && vectorPreferences.developerMode() R.id.resend_all -> state.asyncRoomSummary()?.hasFailedSending == true + R.id.timeline_setting -> true + R.id.invite -> state.canInvite R.id.clear_all -> state.asyncRoomSummary()?.hasFailedSending == true R.id.open_matrix_apps -> session.integrationManagerService().isIntegrationEnabled() R.id.voice_call, @@ -540,6 +555,7 @@ class RoomDetailViewModel @AssistedInject constructor( R.id.hangup_call -> webRtcPeerConnectionManager.currentCall != null R.id.show_room_info -> true R.id.show_participants -> true + R.id.search -> true else -> false } } @@ -741,8 +757,15 @@ class RoomDetailViewModel @AssistedInject constructor( } } - private fun popDraft() { - room.deleteDraft(NoOpMatrixCallback()) + private fun popDraft() = withState { + if (it.sendMode is SendMode.REGULAR && it.sendMode.fromSharing) { + // If we were sharing, we want to get back our last value from draft + getDraftIfAny() + } else { + // Otherwise we clear the composer and remove the draft from db + setState { copy(sendMode = SendMode.REGULAR("", false)) } + room.deleteDraft(NoOpMatrixCallback()) + } } private fun handleJoinToAnotherRoomSlashCommand(command: ParsedCommand.JoinRoom) { @@ -916,74 +939,25 @@ class RoomDetailViewModel @AssistedInject constructor( } private fun handleEditAction(action: RoomDetailAction.EnterEditMode) { - saveCurrentDraft(action.text) - room.getTimeLineEvent(action.eventId)?.let { timelineEvent -> - setState { copy(sendMode = SendMode.EDIT(timelineEvent, action.text)) } - timelineEvent.root.eventId?.let { - room.saveDraft(UserDraft.EDIT(it, timelineEvent.getTextEditableContent() ?: ""), NoOpMatrixCallback()) - } + setState { copy(sendMode = SendMode.EDIT(timelineEvent, timelineEvent.getTextEditableContent() ?: "")) } } } private fun handleQuoteAction(action: RoomDetailAction.EnterQuoteMode) { - saveCurrentDraft(action.text) - room.getTimeLineEvent(action.eventId)?.let { timelineEvent -> setState { copy(sendMode = SendMode.QUOTE(timelineEvent, action.text)) } - withState { state -> - // Save a new draft and keep the previously entered text, if it was not an edit - timelineEvent.root.eventId?.let { - if (state.sendMode is SendMode.EDIT) { - room.saveDraft(UserDraft.QUOTE(it, ""), NoOpMatrixCallback()) - } else { - room.saveDraft(UserDraft.QUOTE(it, action.text), NoOpMatrixCallback()) - } - } - } } } private fun handleReplyAction(action: RoomDetailAction.EnterReplyMode) { - saveCurrentDraft(action.text) - room.getTimeLineEvent(action.eventId)?.let { timelineEvent -> setState { copy(sendMode = SendMode.REPLY(timelineEvent, action.text)) } - withState { state -> - // Save a new draft and keep the previously entered text, if it was not an edit - timelineEvent.root.eventId?.let { - if (state.sendMode is SendMode.EDIT) { - room.saveDraft(UserDraft.REPLY(it, ""), NoOpMatrixCallback()) - } else { - room.saveDraft(UserDraft.REPLY(it, action.text), NoOpMatrixCallback()) - } - } - } } } - private fun saveCurrentDraft(draft: String) { - // Save the draft with the current text if any - withState { - if (draft.isNotBlank()) { - when (it.sendMode) { - is SendMode.REGULAR -> room.saveDraft(UserDraft.REGULAR(draft), NoOpMatrixCallback()) - is SendMode.REPLY -> room.saveDraft(UserDraft.REPLY(it.sendMode.timelineEvent.root.eventId!!, draft), NoOpMatrixCallback()) - is SendMode.QUOTE -> room.saveDraft(UserDraft.QUOTE(it.sendMode.timelineEvent.root.eventId!!, draft), NoOpMatrixCallback()) - is SendMode.EDIT -> room.saveDraft(UserDraft.EDIT(it.sendMode.timelineEvent.root.eventId!!, draft), NoOpMatrixCallback()) - } - } - } - } - - private fun handleExitSpecialMode(action: RoomDetailAction.ExitSpecialMode) = withState { - if (it.sendMode is SendMode.EDIT) { - room.deleteDraft(NoOpMatrixCallback()) - } else { - // Save a new draft and keep the previously entered text - room.saveDraft(UserDraft.REGULAR(action.text), NoOpMatrixCallback()) - } - setState { copy(sendMode = SendMode.REGULAR(action.text)) } + private fun handleEnterRegularMode(action: RoomDetailAction.EnterRegularMode) = setState { + copy(sendMode = SendMode.REGULAR(action.text, action.fromSharing)) } private fun handleOpenOrDownloadFile(action: RoomDetailAction.DownloadOrOpen) { @@ -1108,7 +1082,7 @@ class RoomDetailViewModel @AssistedInject constructor( .buffer(1, TimeUnit.SECONDS) .filter { it.isNotEmpty() } .subscribeBy(onNext = { actions -> - val bufferedMostRecentDisplayedEvent = actions.maxBy { it.event.displayIndex }?.event ?: return@subscribeBy + val bufferedMostRecentDisplayedEvent = actions.maxByOrNull { it.event.displayIndex }?.event ?: return@subscribeBy val globalMostRecentDisplayedEvent = mostRecentDisplayedEvent if (trackUnreadMessages.get()) { if (globalMostRecentDisplayedEvent == null) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt index 16a7379b6a..b31c972d1a 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt @@ -37,7 +37,13 @@ import org.matrix.android.sdk.api.session.widgets.model.Widget * Depending on the state the bottom toolbar will change (icons/preview/actions...) */ sealed class SendMode(open val text: String) { - data class REGULAR(override val text: String) : SendMode(text) + data class REGULAR( + override val text: String, + val fromSharing: Boolean, + // This is necessary for forcing refresh on selectSubscribe + private val ts: Long = System.currentTimeMillis() + ) : SendMode(text) + data class QUOTE(val timelineEvent: TimelineEvent, override val text: String) : SendMode(text) data class EDIT(val timelineEvent: TimelineEvent, override val text: String) : SendMode(text) data class REPLY(val timelineEvent: TimelineEvent, override val text: String) : SendMode(text) @@ -58,7 +64,7 @@ data class RoomDetailViewState( val asyncRoomSummary: Async = Uninitialized, val activeRoomWidgets: Async> = Uninitialized, val typingMessage: String? = null, - val sendMode: SendMode = SendMode.REGULAR(""), + val sendMode: SendMode = SendMode.REGULAR("", false), val tombstoneEvent: Event? = null, val tombstoneEventHandling: Async = Uninitialized, val syncState: SyncState = SyncState.Idle, @@ -67,6 +73,7 @@ data class RoomDetailViewState( val canShowJumpToReadMarker: Boolean = true, val changeMembershipState: ChangeMembershipState = ChangeMembershipState.Unknown, val canSendMessage: Boolean = true, + val canInvite: Boolean = true, val isAllowedToManageWidgets: Boolean = false, val isAllowedToStartWebRTCCall: Boolean = true ) : MvRxState { @@ -77,4 +84,6 @@ data class RoomDetailViewState( // Also highlight the target event, if any highlightedEventId = args.eventId ) + + fun isDm() = asyncRoomSummary()?.isDirect == true } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomMessageTouchHelperCallback.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomMessageTouchHelperCallback.kt index ef13865374..41f386c606 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomMessageTouchHelperCallback.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomMessageTouchHelperCallback.kt @@ -25,12 +25,15 @@ import android.view.MotionEvent import android.view.View import androidx.annotation.DrawableRes import androidx.core.content.ContextCompat +import androidx.core.graphics.drawable.DrawableCompat import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper.ACTION_STATE_SWIPE import androidx.recyclerview.widget.RecyclerView import com.airbnb.epoxy.EpoxyModel import com.airbnb.epoxy.EpoxyTouchHelperCallback import com.airbnb.epoxy.EpoxyViewHolder +import im.vector.app.R +import im.vector.app.features.themes.ThemeUtils import timber.log.Timber import kotlin.math.abs import kotlin.math.min @@ -52,7 +55,16 @@ class RoomMessageTouchHelperCallback(private val context: Context, private var replyButtonProgress: Float = 0F private var lastReplyButtonAnimationTime: Long = 0 - private var imageDrawable: Drawable = ContextCompat.getDrawable(context, actionIcon)!! + private val imageDrawable: Drawable = DrawableCompat.wrap( + ContextCompat.getDrawable(context, actionIcon)!! + ) + + init { + DrawableCompat.setTint( + imageDrawable, + ThemeUtils.getColor(context, R.attr.riotx_text_primary) + ) + } private val triggerDistance = convertToPx(100) private val minShowDistance = convertToPx(20) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerView.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerView.kt index caa8841690..f4b14571c0 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerView.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerView.kt @@ -28,14 +28,16 @@ import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.core.text.toSpannable import androidx.core.view.isVisible -import androidx.transition.AutoTransition +import androidx.transition.ChangeBounds +import androidx.transition.Fade import androidx.transition.Transition import androidx.transition.TransitionManager +import androidx.transition.TransitionSet import butterknife.BindView import butterknife.ButterKnife import im.vector.app.R -import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel import kotlinx.android.synthetic.main.merge_composer_layout.view.* +import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel /** * Encapsulate the timeline composer UX. @@ -54,18 +56,25 @@ class TextComposerView @JvmOverloads constructor(context: Context, attrs: Attrib @BindView(R.id.composer_related_message_sender) lateinit var composerRelatedMessageTitle: TextView + @BindView(R.id.composer_related_message_preview) lateinit var composerRelatedMessageContent: TextView + @BindView(R.id.composer_related_message_avatar_view) lateinit var composerRelatedMessageAvatar: ImageView + @BindView(R.id.composer_related_message_action_image) lateinit var composerRelatedMessageActionIcon: ImageView + @BindView(R.id.composer_related_message_close) lateinit var composerRelatedMessageCloseButton: ImageButton + @BindView(R.id.composerEditText) lateinit var composerEditText: ComposerEditText + @BindView(R.id.composer_avatar_view) lateinit var composerAvatarImageView: ImageView + @BindView(R.id.composer_shield) lateinit var composerShieldImageView: ImageView @@ -106,29 +115,7 @@ class TextComposerView @JvmOverloads constructor(context: Context, attrs: Attrib return } currentConstraintSetId = R.layout.constraint_set_composer_layout_compact - if (animate) { - val transition = AutoTransition() - transition.duration = animationDuration - transition.addListener(object : Transition.TransitionListener { - override fun onTransitionEnd(transition: Transition) { - transitionComplete?.invoke() - } - - override fun onTransitionResume(transition: Transition) {} - - override fun onTransitionPause(transition: Transition) {} - - override fun onTransitionCancel(transition: Transition) {} - - override fun onTransitionStart(transition: Transition) {} - } - ) - TransitionManager.beginDelayedTransition((parent as? ViewGroup ?: this), transition) - } - ConstraintSet().also { - it.clone(context, currentConstraintSetId) - it.applyTo(this) - } + applyNewConstraintSet(animate, transitionComplete) } fun expand(animate: Boolean = true, transitionComplete: (() -> Unit)? = null) { @@ -137,10 +124,28 @@ class TextComposerView @JvmOverloads constructor(context: Context, attrs: Attrib return } currentConstraintSetId = R.layout.constraint_set_composer_layout_expanded + applyNewConstraintSet(animate, transitionComplete) + } + + private fun applyNewConstraintSet(animate: Boolean, transitionComplete: (() -> Unit)?) { if (animate) { - val transition = AutoTransition() - transition.duration = animationDuration - transition.addListener(object : Transition.TransitionListener { + configureAndBeginTransition(transitionComplete) + } + ConstraintSet().also { + it.clone(context, currentConstraintSetId) + // in case shield is hidden, we will have glitch without this + it.getConstraint(R.id.composer_shield).propertySet.visibility = composerShieldImageView.visibility + it.applyTo(this) + } + } + + private fun configureAndBeginTransition(transitionComplete: (() -> Unit)? = null) { + val transition = TransitionSet().apply { + ordering = TransitionSet.ORDERING_SEQUENTIAL + addTransition(ChangeBounds()) + addTransition(Fade(Fade.IN)) + duration = animationDuration + addListener(object : Transition.TransitionListener { override fun onTransitionEnd(transition: Transition) { transitionComplete?.invoke() } @@ -152,14 +157,9 @@ class TextComposerView @JvmOverloads constructor(context: Context, attrs: Attrib override fun onTransitionCancel(transition: Transition) {} override fun onTransitionStart(transition: Transition) {} - } - ) - TransitionManager.beginDelayedTransition((parent as? ViewGroup ?: this), transition) - } - ConstraintSet().also { - it.clone(context, currentConstraintSetId) - it.applyTo(this) + }) } + TransitionManager.beginDelayedTransition((parent as? ViewGroup ?: this), transition) } fun setRoomEncrypted(isEncrypted: Boolean, roomEncryptionTrustLevel: RoomEncryptionTrustLevel?) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/rainbow/RainbowGenerator.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/rainbow/RainbowGenerator.kt index 7bed9f8e64..7f9e40f218 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/rainbow/RainbowGenerator.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/rainbow/RainbowGenerator.kt @@ -18,8 +18,10 @@ package im.vector.app.features.home.room.detail.composer.rainbow import im.vector.app.core.utils.splitEmoji import javax.inject.Inject -import kotlin.math.abs +import kotlin.math.cos +import kotlin.math.pow import kotlin.math.roundToInt +import kotlin.math.sin /** * Inspired from React-Sdk @@ -29,7 +31,7 @@ class RainbowGenerator @Inject constructor() { fun generate(text: String): String { val split = text.splitEmoji() - val frequency = 360f / split.size + val frequency = 2 * Math.PI / split.size return split .mapIndexed { idx, letter -> @@ -37,53 +39,55 @@ class RainbowGenerator @Inject constructor() { if (letter == " ") { "$letter" } else { - val dashColor = hueToRGB(idx * frequency, 1.0f, 0.5f).toDashColor() + val (a, b) = generateAB(idx * frequency, 1f) + val dashColor = labToRGB(75, a, b).toDashColor() "$letter" } } .joinToString(separator = "") } - private fun hueToRGB(h: Float, s: Float, l: Float): RgbColor { - val c = s * (1 - abs(2 * l - 1)) - val x = c * (1 - abs((h / 60) % 2 - 1)) - val m = l - c / 2 + private fun generateAB(hue: Double, chroma: Float): Pair { + val a = chroma * 127 * cos(hue) + val b = chroma * 127 * sin(hue) - var r = 0f - var g = 0f - var b = 0f + return Pair(a, b) + } - when { - h < 60f -> { - r = c - g = x - } - h < 120f -> { - r = x - g = c - } - h < 180f -> { - g = c - b = x - } - h < 240f -> { - g = x - b = c - } - h < 300f -> { - r = x - b = c - } - else -> { - r = c - b = x - } + private fun labToRGB(l: Int, a: Double, b: Double): RgbColor { + // Convert CIELAB to CIEXYZ (D65) + var y = (l + 16) / 116.0 + val x = adjustXYZ(y + a / 500) * 0.9505 + val z = adjustXYZ(y - b / 200) * 1.0890 + + y = adjustXYZ(y) + + // Linear transformation from CIEXYZ to RGB + val red = 3.24096994 * x - 1.53738318 * y - 0.49861076 * z + val green = -0.96924364 * x + 1.8759675 * y + 0.04155506 * z + val blue = 0.05563008 * x - 0.20397696 * y + 1.05697151 * z + + return RgbColor(adjustRGB(red), adjustRGB(green), adjustRGB(blue)) + } + + private fun adjustXYZ(value: Double): Double { + if (value > 0.2069) { + return value.pow(3) } + return 0.1284 * value - 0.01771 + } - return RgbColor( - ((r + m) * 255).roundToInt(), - ((g + m) * 255).roundToInt(), - ((b + m) * 255).roundToInt() - ) + private fun gammaCorrection(value: Double): Double { + // Non-linear transformation to sRGB + if (value <= 0.0031308) { + return 12.92 * value + } + return 1.055 * value.pow(1 / 2.4) - 0.055 + } + + private fun adjustRGB(value: Double): Int { + return (gammaCorrection(value) + .coerceIn(0.0, 1.0) * 255) + .roundToInt() } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/readreceipts/DisplayReadReceiptsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/readreceipts/DisplayReadReceiptsBottomSheet.kt index 7593118c9f..5fefc9aba8 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/readreceipts/DisplayReadReceiptsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/readreceipts/DisplayReadReceiptsBottomSheet.kt @@ -18,6 +18,7 @@ package im.vector.app.features.home.room.detail.readreceipts import android.os.Bundle import android.os.Parcelable +import android.view.View import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import com.airbnb.mvrx.MvRx @@ -59,8 +60,8 @@ class DisplayReadReceiptsBottomSheet : VectorBaseBottomSheetDialogFragment(), Di override fun getLayoutResId() = R.layout.bottom_sheet_generic_list_with_title - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java) recyclerView.configureWith(epoxyController, hasFixedSize = false) bottomSheetTitle.text = getString(R.string.seen_by) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchAction.kt new file mode 100644 index 0000000000..36d22f1914 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchAction.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.detail.search + +import im.vector.app.core.platform.VectorViewModelAction + +sealed class SearchAction : VectorViewModelAction { + data class SearchWith(val searchTerm: String) : SearchAction() + object LoadMore : SearchAction() + object Retry : SearchAction() +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt new file mode 100644 index 0000000000..f85dccbb27 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt @@ -0,0 +1,79 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.detail.search + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.appcompat.widget.SearchView +import com.airbnb.mvrx.MvRx +import im.vector.app.R +import im.vector.app.core.di.ScreenComponent +import im.vector.app.core.extensions.addFragment +import im.vector.app.core.platform.VectorBaseActivity +import kotlinx.android.synthetic.main.activity_search.* + +class SearchActivity : VectorBaseActivity() { + + private val searchFragment: SearchFragment? + get() { + return supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as? SearchFragment + } + + override fun getLayoutRes() = R.layout.activity_search + + override fun injectWith(injector: ScreenComponent) { + super.injectWith(injector) + injector.inject(this) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + configureToolbar(searchToolbar) + } + + override fun initUiAndData() { + if (isFirstCreation()) { + val fragmentArgs: SearchArgs = intent?.extras?.getParcelable(MvRx.KEY_ARG) ?: return + addFragment(R.id.searchFragmentContainer, SearchFragment::class.java, fragmentArgs, FRAGMENT_TAG) + } + searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + override fun onQueryTextSubmit(query: String): Boolean { + searchFragment?.search(query) + return true + } + + override fun onQueryTextChange(newText: String): Boolean { + return true + } + }) + // Open the keyboard immediately + searchView.requestFocus() + } + + companion object { + private const val FRAGMENT_TAG = "SearchFragment" + + fun newIntent(context: Context, args: SearchArgs): Intent { + return Intent(context, SearchActivity::class.java).apply { + // If we do that we will have the same room two times on the stack. Let's allow infinite stack for the moment. + // flags = Intent.FLAG_ACTIVITY_REORDER_TO_FRONT + putExtra(MvRx.KEY_ARG, args) + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt new file mode 100644 index 0000000000..10dc9254d8 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt @@ -0,0 +1,128 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.detail.search + +import android.os.Bundle +import android.os.Parcelable +import android.view.View +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.LinearLayoutManager +import com.airbnb.mvrx.Fail +import com.airbnb.mvrx.Loading +import com.airbnb.mvrx.Success +import com.airbnb.mvrx.args +import com.airbnb.mvrx.fragmentViewModel +import com.airbnb.mvrx.withState +import im.vector.app.R +import im.vector.app.core.extensions.cleanup +import im.vector.app.core.extensions.configureWith +import im.vector.app.core.extensions.hideKeyboard +import im.vector.app.core.extensions.trackItemsVisibilityChange +import im.vector.app.core.platform.StateView +import im.vector.app.core.platform.VectorBaseFragment +import kotlinx.android.parcel.Parcelize +import kotlinx.android.synthetic.main.fragment_search.* +import org.matrix.android.sdk.api.session.events.model.Event +import javax.inject.Inject + +@Parcelize +data class SearchArgs( + val roomId: String +) : Parcelable + +class SearchFragment @Inject constructor( + val viewModelFactory: SearchViewModel.Factory, + private val controller: SearchResultController +) : VectorBaseFragment(), StateView.EventCallback, SearchResultController.Listener { + + private val fragmentArgs: SearchArgs by args() + private val searchViewModel: SearchViewModel by fragmentViewModel() + + private var pendingScrollToPosition: Int? = null + + override fun getLayoutResId() = R.layout.fragment_search + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + stateView.contentView = searchResultRecycler + stateView.eventCallback = this + + configureRecyclerView() + } + + private fun configureRecyclerView() { + searchResultRecycler.trackItemsVisibilityChange() + searchResultRecycler.configureWith(controller, showDivider = false) + (searchResultRecycler.layoutManager as? LinearLayoutManager)?.stackFromEnd = true + controller.listener = this + + controller.addModelBuildListener { + pendingScrollToPosition?.let { + searchResultRecycler.smoothScrollToPosition(it) + } + } + } + + override fun onDestroy() { + super.onDestroy() + searchResultRecycler?.cleanup() + controller.listener = null + } + + override fun invalidate() = withState(searchViewModel) { state -> + if (state.searchResult.isNullOrEmpty()) { + when (state.asyncSearchRequest) { + is Loading -> { + stateView.state = StateView.State.Loading + } + is Fail -> { + stateView.state = StateView.State.Error(errorFormatter.toHumanReadable(state.asyncSearchRequest.error)) + } + is Success -> { + stateView.state = StateView.State.Empty( + title = getString(R.string.search_no_results), + image = ContextCompat.getDrawable(requireContext(), R.drawable.ic_search_no_results)) + } + } + } else { + pendingScrollToPosition = (state.lastBatchSize - 1).coerceAtLeast(0) + + stateView.state = StateView.State.Content + controller.setData(state) + } + } + + fun search(query: String) { + view?.hideKeyboard() + searchViewModel.handle(SearchAction.SearchWith(query)) + } + + override fun onRetryClicked() { + searchViewModel.handle(SearchAction.Retry) + } + + override fun onItemClicked(event: Event) { + event.roomId?.let { + navigator.openRoom(requireContext(), it, event.eventId) + } + } + + override fun loadMore() { + searchViewModel.handle(SearchAction.LoadMore) + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt new file mode 100644 index 0000000000..c917c4557d --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt @@ -0,0 +1,96 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.detail.search + +import com.airbnb.epoxy.TypedEpoxyController +import com.airbnb.epoxy.VisibilityState +import im.vector.app.core.date.DateFormatKind +import im.vector.app.core.date.VectorDateFormatter +import im.vector.app.core.epoxy.loadingItem +import im.vector.app.core.ui.list.genericItemHeader +import im.vector.app.features.home.AvatarRenderer +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.search.EventAndSender +import org.matrix.android.sdk.api.util.toMatrixItem +import java.util.Calendar +import javax.inject.Inject + +class SearchResultController @Inject constructor( + private val session: Session, + private val avatarRenderer: AvatarRenderer, + private val dateFormatter: VectorDateFormatter +) : TypedEpoxyController() { + + var listener: Listener? = null + + private var idx = 0 + + interface Listener { + fun onItemClicked(event: Event) + fun loadMore() + } + + init { + setData(null) + } + + override fun buildModels(data: SearchViewState?) { + data ?: return + + if (data.hasMoreResult) { + loadingItem { + // Always use a different id, because we can be notified several times of visibility state changed + id("loadMore${idx++}") + onVisibilityStateChanged { _, _, visibilityState -> + if (visibilityState == VisibilityState.VISIBLE) { + listener?.loadMore() + } + } + } + } + + buildSearchResultItems(data.searchResult) + } + + private fun buildSearchResultItems(events: List) { + var lastDate: Calendar? = null + + events.forEach { eventAndSender -> + val eventDate = Calendar.getInstance().apply { + timeInMillis = eventAndSender.event.originServerTs ?: System.currentTimeMillis() + } + if (lastDate?.get(Calendar.DAY_OF_YEAR) != eventDate.get(Calendar.DAY_OF_YEAR)) { + genericItemHeader { + id(eventDate.hashCode()) + text(dateFormatter.format(eventDate.timeInMillis, DateFormatKind.EDIT_HISTORY_HEADER)) + } + } + lastDate = eventDate + + searchResultItem { + id(eventAndSender.event.eventId) + avatarRenderer(avatarRenderer) + dateFormatter(dateFormatter) + event(eventAndSender.event) + sender(eventAndSender.sender + ?: eventAndSender.event.senderId?.let { session.getUser(it) }?.toMatrixItem()) + listener { listener?.onItemClicked(eventAndSender.event) } + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultItem.kt new file mode 100644 index 0000000000..10407c64e0 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultItem.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.detail.search + +import android.widget.ImageView +import android.widget.TextView +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import im.vector.app.R +import im.vector.app.core.date.DateFormatKind +import im.vector.app.core.date.VectorDateFormatter +import im.vector.app.core.epoxy.ClickListener +import im.vector.app.core.epoxy.VectorEpoxyHolder +import im.vector.app.core.epoxy.VectorEpoxyModel +import im.vector.app.core.epoxy.onClick +import im.vector.app.core.extensions.setTextOrHide +import im.vector.app.features.home.AvatarRenderer +import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.util.MatrixItem + +@EpoxyModelClass(layout = R.layout.item_search_result) +abstract class SearchResultItem : VectorEpoxyModel() { + + @EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer + @EpoxyAttribute var dateFormatter: VectorDateFormatter? = null + @EpoxyAttribute lateinit var event: Event + @EpoxyAttribute var sender: MatrixItem? = null + @EpoxyAttribute var listener: ClickListener? = null + + override fun bind(holder: Holder) { + super.bind(holder) + + holder.view.onClick(listener) + sender?.let { avatarRenderer.render(it, holder.avatarImageView) } + holder.memberNameView.setTextOrHide(sender?.getBestName()) + holder.timeView.text = dateFormatter?.format(event.originServerTs, DateFormatKind.MESSAGE_SIMPLE) + // TODO Improve that (use formattedBody, etc.) + holder.contentView.text = event.content?.get("body") as? String + } + + class Holder : VectorEpoxyHolder() { + val avatarImageView by bind(R.id.messageAvatarImageView) + val memberNameView by bind(R.id.messageMemberNameView) + val timeView by bind(R.id.messageTimeView) + val contentView by bind(R.id.messageContentView) + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewEvents.kt new file mode 100644 index 0000000000..6f07cb765c --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewEvents.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.detail.search + +import im.vector.app.core.platform.VectorViewEvents + +sealed class SearchViewEvents : VectorViewEvents { + data class Failure(val throwable: Throwable) : SearchViewEvents() +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt new file mode 100644 index 0000000000..f61bcbd029 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt @@ -0,0 +1,159 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.detail.search + +import androidx.lifecycle.viewModelScope +import com.airbnb.mvrx.Fail +import com.airbnb.mvrx.FragmentViewModelContext +import com.airbnb.mvrx.Loading +import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.Success +import com.airbnb.mvrx.ViewModelContext +import com.squareup.inject.assisted.Assisted +import com.squareup.inject.assisted.AssistedInject +import im.vector.app.core.extensions.exhaustive +import im.vector.app.core.platform.VectorViewModel +import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.failure.Failure +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.room.Room +import org.matrix.android.sdk.api.session.search.SearchResult +import org.matrix.android.sdk.api.util.Cancelable +import org.matrix.android.sdk.internal.util.awaitCallback + +class SearchViewModel @AssistedInject constructor( + @Assisted private val initialState: SearchViewState, + session: Session +) : VectorViewModel(initialState) { + + private var room: Room? = session.getRoom(initialState.roomId) + + private var currentTask: Cancelable? = null + + private var nextBatch: String? = null + + @AssistedInject.Factory + interface Factory { + fun create(initialState: SearchViewState): SearchViewModel + } + + companion object : MvRxViewModelFactory { + + @JvmStatic + override fun create(viewModelContext: ViewModelContext, state: SearchViewState): SearchViewModel? { + val fragment: SearchFragment = (viewModelContext as FragmentViewModelContext).fragment() + return fragment.viewModelFactory.create(state) + } + } + + override fun handle(action: SearchAction) { + when (action) { + is SearchAction.SearchWith -> handleSearchWith(action) + is SearchAction.LoadMore -> handleLoadMore() + is SearchAction.Retry -> handleRetry() + }.exhaustive + } + + private fun handleSearchWith(action: SearchAction.SearchWith) { + if (action.searchTerm.isNotEmpty()) { + setState { + copy( + searchResult = emptyList(), + hasMoreResult = false, + lastBatchSize = 0, + searchTerm = action.searchTerm + ) + } + startSearching(false) + } + } + + private fun handleLoadMore() { + startSearching(true) + } + + private fun handleRetry() { + startSearching(false) + } + + private fun startSearching(isNextBatch: Boolean) = withState { state -> + if (state.searchTerm == null) return@withState + + // There is no batch to retrieve + if (isNextBatch && nextBatch == null) return@withState + + // Show full screen loading just for the clean search + if (!isNextBatch) { + setState { + copy( + asyncSearchRequest = Loading() + ) + } + } + + currentTask?.cancel() + + viewModelScope.launch { + try { + val result = awaitCallback { + currentTask = room?.search( + searchTerm = state.searchTerm, + nextBatch = nextBatch, + orderByRecent = true, + beforeLimit = 0, + afterLimit = 0, + includeProfile = true, + limit = 20, + callback = it + ) + } + onSearchResultSuccess(result) + } catch (failure: Throwable) { + if (failure is Failure.Cancelled) return@launch + + _viewEvents.post(SearchViewEvents.Failure(failure)) + setState { + copy( + asyncSearchRequest = Fail(failure) + ) + } + } + } + } + + private fun onSearchResultSuccess(searchResult: SearchResult) = withState { state -> + val accumulatedResult = searchResult.results.orEmpty().plus(state.searchResult) + + // Note: We do not care about the highlights for the moment, but it will be the same algorithm + + nextBatch = searchResult.nextBatch + + setState { + copy( + searchResult = accumulatedResult, + hasMoreResult = !nextBatch.isNullOrEmpty(), + lastBatchSize = searchResult.results.orEmpty().size, + asyncSearchRequest = Success(Unit) + ) + } + } + + override fun onCleared() { + currentTask?.cancel() + super.onCleared() + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt new file mode 100644 index 0000000000..9f700b6e31 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewState.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.detail.search + +import com.airbnb.mvrx.Async +import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.Uninitialized +import org.matrix.android.sdk.api.session.search.EventAndSender + +data class SearchViewState( + // Accumulated search result + val searchResult: List = emptyList(), + val hasMoreResult: Boolean = false, + // Last batch size, will help RecyclerView to position itself + val lastBatchSize: Int = 0, + val searchTerm: String? = null, + val roomId: String = "", + // Current pagination request + val asyncSearchRequest: Async = Uninitialized +) : MvRxState { + + constructor(args: SearchArgs) : this(roomId = args.roomId) +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/sticker/StickerPickerActionHandler.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/sticker/StickerPickerActionHandler.kt index 9cdc9eeabc..d24b41ffb0 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/sticker/StickerPickerActionHandler.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/sticker/StickerPickerActionHandler.kt @@ -17,10 +17,10 @@ package im.vector.app.features.home.room.detail.sticker import im.vector.app.features.home.room.detail.RoomDetailViewEvents -import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.widgets.model.WidgetType import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.widgets.model.WidgetType import javax.inject.Inject class StickerPickerActionHandler @Inject constructor(private val session: Session) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt index be59128c26..bddc7fa126 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt @@ -50,18 +50,28 @@ import im.vector.app.features.home.room.detail.timeline.item.ReadReceiptData import im.vector.app.features.home.room.detail.timeline.item.TimelineReadMarkerItem_ import im.vector.app.features.media.ImageContentRenderer import im.vector.app.features.media.VideoContentRenderer +import im.vector.app.features.settings.VectorPreferences +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.events.model.EventType +import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.room.model.Membership +import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.api.session.room.model.message.MessageImageInfoContent import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent import org.matrix.android.sdk.api.session.room.timeline.Timeline import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import javax.inject.Inject +private const val DEFAULT_PREFETCH_THRESHOLD = 30 + class TimelineEventController @Inject constructor(private val dateFormatter: VectorDateFormatter, + private val vectorPreferences: VectorPreferences, private val contentUploadStateTrackerBinder: ContentUploadStateTrackerBinder, private val contentDownloadStateTrackerBinder: ContentDownloadStateTrackerBinder, private val timelineItemFactory: TimelineItemFactory, private val timelineMediaSizeProvider: TimelineMediaSizeProvider, private val mergedHeaderItemFactory: MergedHeaderItemFactory, + private val session: Session, @TimelineEventControllerHandler private val backgroundHandler: Handler ) : EpoxyController(backgroundHandler, backgroundHandler), Timeline.Listener, EpoxyController.Interceptor { @@ -113,9 +123,12 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec private val modelCache = arrayListOf() private var currentSnapshot: List = emptyList() private var inSubmitList: Boolean = false + private var hasReachedInvite: Boolean = false + private var hasUTD: Boolean = false private var unreadState: UnreadState = UnreadState.Unknown private var positionOfReadMarker: Int? = null private var eventIdToHighlight: String? = null + private var previousModelsSize = 0 var callback: Callback? = null var timeline: Timeline? = null @@ -191,6 +204,29 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec models.add(position, readMarker) } } + val shouldAddBackwardPrefetch = timeline?.hasMoreToLoad(Timeline.Direction.BACKWARDS) ?: false + if (shouldAddBackwardPrefetch) { + val indexOfPrefetchBackward = (previousModelsSize - 1) + .coerceAtMost(models.size - DEFAULT_PREFETCH_THRESHOLD) + .coerceAtLeast(0) + + val loadingItem = LoadingItem_() + .id("prefetch_backward_loading${System.currentTimeMillis()}") + .showLoader(false) + .setVisibilityStateChangedListener(Timeline.Direction.BACKWARDS) + + models.add(indexOfPrefetchBackward, loadingItem) + } + val shouldAddForwardPrefetch = timeline?.hasMoreToLoad(Timeline.Direction.FORWARDS) ?: false + if (shouldAddForwardPrefetch) { + val indexOfPrefetchForward = DEFAULT_PREFETCH_THRESHOLD.coerceAtMost(models.size - 1) + val loadingItem = LoadingItem_() + .id("prefetch_forward_loading${System.currentTimeMillis()}") + .showLoader(false) + .setVisibilityStateChangedListener(Timeline.Direction.FORWARDS) + models.add(indexOfPrefetchForward, loadingItem) + } + previousModelsSize = models.size } fun update(viewState: RoomDetailViewState) { @@ -241,7 +277,9 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec val timelineModels = getModels() add(timelineModels) - + if (hasReachedInvite && hasUTD) { + return + } // Avoid displaying two loaders if there is no elements between them val showBackwardsLoader = !showingForwardLoader || timelineModels.isNotEmpty() // We can hide the loader but still add the item to controller so it can trigger backwards pagination @@ -301,6 +339,9 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec } private fun buildCacheItemsIfNeeded() = synchronized(modelCache) { + hasUTD = false + hasReachedInvite = false + if (modelCache.isEmpty()) { return } @@ -316,13 +357,21 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec private fun buildCacheItem(currentPosition: Int, items: List): CacheItemData { val event = items[currentPosition] val nextEvent = items.nextOrNull(currentPosition) - val date = event.root.localDateTime() - val nextDate = nextEvent?.root?.localDateTime() - val addDaySeparator = date.toLocalDate() != nextDate?.toLocalDate() + if (hasReachedInvite && hasUTD) { + return CacheItemData(event.localId, event.root.eventId, null, null, null) + } + updateUTDStates(event, nextEvent) val eventModel = timelineItemFactory.create(event, nextEvent, eventIdToHighlight, callback).also { it.id(event.localId) it.setOnVisibilityStateChanged(TimelineEventVisibilityStateChangedListener(callback, event)) } + val addDaySeparator = if (hasReachedInvite && hasUTD) { + true + } else { + val date = event.root.localDateTime() + val nextDate = nextEvent?.root?.localDateTime() + date.toLocalDate() != nextDate?.toLocalDate() + } val mergedHeaderModel = mergedHeaderItemFactory.create(event, nextEvent = nextEvent, items = items, @@ -346,6 +395,27 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec } } + private fun updateUTDStates(event: TimelineEvent, nextEvent: TimelineEvent?) { + if (vectorPreferences.labShowCompleteHistoryInEncryptedRoom()) { + return + } + if (event.root.type == EventType.STATE_ROOM_MEMBER + && event.root.stateKey == session.myUserId) { + val content = event.root.content.toModel() + if (content?.membership == Membership.INVITE) { + hasReachedInvite = true + } else if (content?.membership == Membership.JOIN) { + val prevContent = event.root.resolvedPrevContent().toModel() + if (prevContent?.membership?.isActive() == false) { + hasReachedInvite = true + } + } + } + if (nextEvent?.root?.getClearType() == EventType.ENCRYPTED) { + hasUTD = true + } + } + /** * Return true if added */ @@ -355,9 +425,6 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec return shouldAdd } - /** - * Return true if added - */ private fun LoadingItem_.setVisibilityStateChangedListener(direction: Timeline.Direction): LoadingItem_ { return onVisibilityStateChanged { _, _, visibilityState -> if (visibilityState == VisibilityState.VISIBLE) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/EventSharedAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/EventSharedAction.kt index 7693d97c35..c21d552409 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/EventSharedAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/EventSharedAction.kt @@ -21,6 +21,7 @@ import androidx.annotation.StringRes import im.vector.app.R import im.vector.app.core.platform.VectorSharedAction import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData +import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachmentContent sealed class EventSharedAction(@StringRes val titleRes: Int, @@ -47,7 +48,7 @@ sealed class EventSharedAction(@StringRes val titleRes: Int, data class Reply(val eventId: String) : EventSharedAction(R.string.reply, R.drawable.ic_reply) - data class Share(val eventId: String, val messageContent: MessageWithAttachmentContent) : + data class Share(val eventId: String, val messageContent: MessageContent) : EventSharedAction(R.string.share, R.drawable.ic_share) data class Save(val eventId: String, val messageContent: MessageWithAttachmentContent) : diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt index 9858d2a03c..f337f0ba5f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt @@ -16,6 +16,7 @@ package im.vector.app.features.home.room.detail.timeline.action import android.os.Bundle +import android.view.View import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import com.airbnb.mvrx.fragmentViewModel @@ -51,8 +52,8 @@ class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), Message override fun getLayoutResId() = R.layout.bottom_sheet_generic_list - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java) recyclerView.configureWith(messageActionsEpoxyController, hasFixedSize = false, disableItemAnimation = true) messageActionsEpoxyController.listener = this diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt index a49b74c243..0d1e2261cd 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt @@ -190,7 +190,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted EventType.CALL_CANDIDATES, EventType.CALL_HANGUP, EventType.CALL_ANSWER -> { - noticeEventFormatter.format(timelineEvent) + noticeEventFormatter.format(timelineEvent, room?.roomSummary()) } else -> null } ?: "" @@ -275,8 +275,8 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted add(EventSharedAction.ViewEditHistory(informationData)) } - if (canShare(msgType) && messageContent is MessageWithAttachmentContent) { - add(EventSharedAction.Share(timelineEvent.eventId, messageContent)) + if (canShare(msgType)) { + add(EventSharedAction.Share(timelineEvent.eventId, messageContent!!)) } if (canSave(msgType) && messageContent is MessageWithAttachmentContent) { @@ -409,6 +409,10 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted private fun canShare(msgType: String?): Boolean { return when (msgType) { + MessageType.MSGTYPE_TEXT, + MessageType.MSGTYPE_NOTICE, + MessageType.MSGTYPE_EMOTE, + MessageType.MSGTYPE_LOCATION, MessageType.MSGTYPE_IMAGE, MessageType.MSGTYPE_AUDIO, MessageType.MSGTYPE_VIDEO, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageSharedActionViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageSharedActionViewModel.kt index cff9a8c5e2..b6023333b1 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageSharedActionViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageSharedActionViewModel.kt @@ -22,5 +22,5 @@ import javax.inject.Inject * Activity shared view model to handle message actions */ class MessageSharedActionViewModel @Inject constructor() : VectorSharedActionViewModel() { - var pendingAction : EventSharedAction? = null + var pendingAction: EventSharedAction? = null } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt index 93fdd253d8..080ccaea7c 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt @@ -16,6 +16,7 @@ package im.vector.app.features.home.room.detail.timeline.edithistory import android.os.Bundle +import android.view.View import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import com.airbnb.mvrx.MvRx @@ -55,8 +56,8 @@ class ViewEditHistoryBottomSheet : VectorBaseBottomSheetDialogFragment() { override fun getLayoutResId() = R.layout.bottom_sheet_generic_list_with_title - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) recyclerView.configureWith( epoxyController, showDivider = true, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt index 42d5596300..f77e39c245 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt @@ -28,13 +28,13 @@ import im.vector.app.features.home.room.detail.timeline.helper.MessageItemAttrib import im.vector.app.features.home.room.detail.timeline.item.MessageTextItem_ import im.vector.app.features.home.room.detail.timeline.tools.createLinkMovementMethod import im.vector.app.features.settings.VectorPreferences +import me.gujun.android.span.image +import me.gujun.android.span.span import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent -import me.gujun.android.span.image -import me.gujun.android.span.span import javax.inject.Inject // This class handles timeline events who haven't been successfully decrypted diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptionItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptionItemFactory.kt index 5374b4792a..1eb09f2e7a 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptionItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptionItemFactory.kt @@ -25,6 +25,8 @@ import im.vector.app.features.home.room.detail.timeline.helper.MessageInformatio import im.vector.app.features.home.room.detail.timeline.helper.MessageItemAttributesFactory import im.vector.app.features.home.room.detail.timeline.item.StatusTileTimelineItem import im.vector.app.features.home.room.detail.timeline.item.StatusTileTimelineItem_ +import org.matrix.android.sdk.api.extensions.orFalse +import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM @@ -36,11 +38,15 @@ class EncryptionItemFactory @Inject constructor( private val messageColorProvider: MessageColorProvider, private val stringProvider: StringProvider, private val informationDataFactory: MessageInformationDataFactory, - private val avatarSizeProvider: AvatarSizeProvider) { + private val avatarSizeProvider: AvatarSizeProvider, + private val session: Session) { fun create(event: TimelineEvent, highlight: Boolean, callback: TimelineEventController.Callback?): StatusTileTimelineItem? { + if (!event.root.isStateEvent()) { + return null + } val algorithm = event.root.getClearContent().toModel()?.algorithm val informationData = informationDataFactory.create(event, null) val attributes = messageItemAttributesFactory.create(null, informationData, callback) @@ -51,7 +57,13 @@ class EncryptionItemFactory @Inject constructor( val shield: StatusTileTimelineItem.ShieldUIState if (isSafeAlgorithm) { title = stringProvider.getString(R.string.encryption_enabled) - description = stringProvider.getString(R.string.encryption_enabled_tile_description) + description = stringProvider.getString( + if (session.getRoomSummary(event.root.roomId ?: "")?.isDirect.orFalse()) { + R.string.direct_room_encryption_enabled_tile_description + } else { + R.string.encryption_enabled_tile_description + } + ) shield = StatusTileTimelineItem.ShieldUIState.BLACK } else { title = stringProvider.getString(R.string.encryption_not_enabled) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt index 0ba3b4d47e..e7a911ceb1 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt @@ -22,6 +22,7 @@ import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.timeline.TimelineEventController import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider import im.vector.app.features.home.room.detail.timeline.helper.MergedTimelineEventVisibilityStateChangedListener +import im.vector.app.features.home.room.detail.timeline.helper.RoomSummaryHolder import im.vector.app.features.home.room.detail.timeline.helper.canBeMerged import im.vector.app.features.home.room.detail.timeline.helper.isRoomConfiguration import im.vector.app.features.home.room.detail.timeline.helper.prevSameTypeEvents @@ -30,22 +31,19 @@ import im.vector.app.features.home.room.detail.timeline.item.MergedMembershipEve import im.vector.app.features.home.room.detail.timeline.item.MergedMembershipEventsItem_ import im.vector.app.features.home.room.detail.timeline.item.MergedRoomCreationItem import im.vector.app.features.home.room.detail.timeline.item.MergedRoomCreationItem_ -import im.vector.app.features.home.room.detail.timeline.item.MergedUTDItem -import im.vector.app.features.home.room.detail.timeline.item.MergedUTDItem_ -import im.vector.app.features.settings.VectorPreferences +import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM import org.matrix.android.sdk.internal.crypto.model.event.EncryptionEventContent -import timber.log.Timber import javax.inject.Inject class MergedHeaderItemFactory @Inject constructor(private val activeSessionHolder: ActiveSessionHolder, private val avatarRenderer: AvatarRenderer, private val avatarSizeProvider: AvatarSizeProvider, - private val vectorPreferences: VectorPreferences) { + private val roomSummaryHolder: RoomSummaryHolder) { private val collapsedEventIds = linkedSetOf() private val mergeItemCollapseStates = HashMap() @@ -63,10 +61,7 @@ class MergedHeaderItemFactory @Inject constructor(private val activeSessionHolde callback: TimelineEventController.Callback?, requestModelBuild: () -> Unit) : BasedMergedItem<*>? { - return if (shouldMergedAsCannotDecryptGroup(event, nextEvent)) { - Timber.v("## MERGE: Candidate for merge, top event ${event.eventId}") - buildUTDMergedSummary(currentPosition, items, event, eventIdToHighlight, /*requestModelBuild,*/ callback) - } else if (nextEvent?.root?.getClearType() == EventType.STATE_ROOM_CREATE + return if (nextEvent?.root?.getClearType() == EventType.STATE_ROOM_CREATE && event.isRoomConfiguration(nextEvent.root.getClearContent()?.toModel()?.creator)) { // It's the first item before room.create // Collapse all room configuration events @@ -78,6 +73,8 @@ class MergedHeaderItemFactory @Inject constructor(private val activeSessionHolde } } + private fun isDirectRoom() = roomSummaryHolder.roomSummary?.isDirect.orFalse() + private fun buildMembershipEventsMergedSummary(currentPosition: Int, items: List, event: TimelineEvent, @@ -100,7 +97,8 @@ class MergedHeaderItemFactory @Inject constructor(private val activeSessionHolde avatarUrl = mergedEvent.senderInfo.avatarUrl, memberName = mergedEvent.senderInfo.disambiguatedDisplayName, localId = mergedEvent.localId, - eventId = mergedEvent.root.eventId ?: "" + eventId = mergedEvent.root.eventId ?: "", + isDirectRoom = isDirectRoom() ) mergedData.add(data) } @@ -138,82 +136,6 @@ class MergedHeaderItemFactory @Inject constructor(private val activeSessionHolde } } - // Event should be UTD - // Next event should not - private fun shouldMergedAsCannotDecryptGroup(event: TimelineEvent, nextEvent: TimelineEvent?): Boolean { - if (!vectorPreferences.mergeUTDinTimeline()) return false - // if event is not UTD return false - if (!isEventUTD(event)) return false - // At this point event cannot be decrypted - // Let's check if older event is not UTD - return nextEvent == null || !isEventUTD(event) - } - - private fun isEventUTD(event: TimelineEvent): Boolean { - return event.root.getClearType() == EventType.ENCRYPTED && !event.root.isRedacted() - } - - private fun buildUTDMergedSummary(currentPosition: Int, - items: List, - event: TimelineEvent, - eventIdToHighlight: String?, - // requestModelBuild: () -> Unit, - callback: TimelineEventController.Callback?): MergedUTDItem_? { - Timber.v("## MERGE: buildUTDMergedSummary from position $currentPosition") - var prevEvent = items.prevOrNull(currentPosition) - var tmpPos = currentPosition - 1 - val mergedEvents = ArrayList().also { it.add(event) } - - while (prevEvent != null && isEventUTD(prevEvent)) { - mergedEvents.add(prevEvent) - tmpPos-- - prevEvent = if (tmpPos >= 0) items[tmpPos] else null - } - - Timber.v("## MERGE: buildUTDMergedSummary merge group size ${mergedEvents.size}") - if (mergedEvents.size < 3) return null - - var highlighted = false - val mergedData = ArrayList(mergedEvents.size) - mergedEvents.reversed() - .forEach { mergedEvent -> - if (!highlighted && mergedEvent.root.eventId == eventIdToHighlight) { - highlighted = true - } - val senderAvatar = mergedEvent.senderInfo.avatarUrl - val senderName = mergedEvent.senderInfo.disambiguatedDisplayName - val data = BasedMergedItem.Data( - userId = mergedEvent.root.senderId ?: "", - avatarUrl = senderAvatar, - memberName = senderName, - localId = mergedEvent.localId, - eventId = mergedEvent.root.eventId ?: "" - ) - mergedData.add(data) - } - val mergedEventIds = mergedEvents.map { it.localId } - - collapsedEventIds.addAll(mergedEventIds) - - val mergeId = mergedEventIds.joinToString(separator = "_") { it.toString() } - - val attributes = MergedUTDItem.Attributes( - isCollapsed = true, - mergeData = mergedData, - avatarRenderer = avatarRenderer, - onCollapsedStateChanged = {} - ) - return MergedUTDItem_() - .id(mergeId) - .big(mergedEventIds.size > 5) - .leftGuideline(avatarSizeProvider.leftGuideline) - .highlighted(highlighted) - .attributes(attributes) - .also { - it.setOnVisibilityStateChanged(MergedTimelineEventVisibilityStateChangedListener(callback, mergedEvents)) - } - } - private fun buildRoomCreationMergedSummary(currentPosition: Int, items: List, event: TimelineEvent, @@ -226,7 +148,7 @@ class MergedHeaderItemFactory @Inject constructor(private val activeSessionHolde var hasEncryption = false var encryptionAlgorithm: String? = null while (prevEvent != null && prevEvent.isRoomConfiguration(null)) { - if (prevEvent.root.getClearType() == EventType.STATE_ROOM_ENCRYPTION) { + if (prevEvent.root.isStateEvent() && prevEvent.root.getClearType() == EventType.STATE_ROOM_ENCRYPTION) { hasEncryption = true encryptionAlgorithm = prevEvent.root.getClearContent()?.toModel()?.algorithm } @@ -247,7 +169,8 @@ class MergedHeaderItemFactory @Inject constructor(private val activeSessionHolde avatarUrl = mergedEvent.senderInfo.avatarUrl, memberName = mergedEvent.senderInfo.disambiguatedDisplayName, localId = mergedEvent.localId, - eventId = mergedEvent.root.eventId ?: "" + eventId = mergedEvent.root.eventId ?: "", + isDirectRoom = isDirectRoom() ) mergedData.add(data) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt index 97bc693f26..dd7a87cce6 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -38,6 +38,7 @@ import im.vector.app.features.home.room.detail.timeline.helper.ContentDownloadSt import im.vector.app.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder import im.vector.app.features.home.room.detail.timeline.helper.MessageInformationDataFactory import im.vector.app.features.home.room.detail.timeline.helper.MessageItemAttributesFactory +import im.vector.app.features.home.room.detail.timeline.helper.RoomSummaryHolder import im.vector.app.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider import im.vector.app.features.home.room.detail.timeline.item.AbsMessageItem import im.vector.app.features.home.room.detail.timeline.item.MessageBlockCodeItem @@ -62,6 +63,8 @@ import im.vector.app.features.html.EventHtmlRenderer import im.vector.app.features.html.VectorHtmlCompressor import im.vector.app.features.media.ImageContentRenderer import im.vector.app.features.media.VideoContentRenderer +import me.gujun.android.span.span +import org.commonmark.node.Document import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.RelationType import org.matrix.android.sdk.api.session.events.model.toModel @@ -85,8 +88,6 @@ import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent import org.matrix.android.sdk.internal.crypto.attachments.toElementToDecrypt import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent -import me.gujun.android.span.span -import org.commonmark.node.Document import javax.inject.Inject class MessageItemFactory @Inject constructor( @@ -101,6 +102,7 @@ class MessageItemFactory @Inject constructor( private val messageItemAttributesFactory: MessageItemAttributesFactory, private val contentUploadStateTrackerBinder: ContentUploadStateTrackerBinder, private val contentDownloadStateTrackerBinder: ContentDownloadStateTrackerBinder, + private val roomSummaryHolder: RoomSummaryHolder, private val defaultItemFactory: DefaultItemFactory, private val noticeItemFactory: NoticeItemFactory, private val avatarSizeProvider: AvatarSizeProvider, @@ -130,7 +132,7 @@ class MessageItemFactory @Inject constructor( || event.isEncrypted() && event.root.content.toModel()?.relatesTo?.type == RelationType.REPLACE ) { // This is an edit event, we should display it when debugging as a notice event - return noticeItemFactory.create(event, highlight, callback) + return noticeItemFactory.create(event, highlight, roomSummaryHolder.roomSummary, callback) } val attributes = messageItemAttributesFactory.create(messageContent, informationData, callback) @@ -146,7 +148,7 @@ class MessageItemFactory @Inject constructor( is MessageAudioContent -> buildAudioMessageItem(messageContent, informationData, highlight, attributes) is MessageVerificationRequestContent -> buildVerificationRequestMessageItem(messageContent, informationData, highlight, callback, attributes) is MessageOptionsContent -> buildOptionsMessageItem(messageContent, informationData, highlight, callback, attributes) - is MessagePollResponseContent -> noticeItemFactory.create(event, highlight, callback) + is MessagePollResponseContent -> noticeItemFactory.create(event, highlight, roomSummaryHolder.roomSummary, callback) else -> buildNotHandledMessageItem(messageContent, informationData, highlight, callback, attributes) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/NoticeItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/NoticeItemFactory.kt index cd8c682f39..ec065543f5 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/NoticeItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/NoticeItemFactory.kt @@ -24,6 +24,7 @@ import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvide import im.vector.app.features.home.room.detail.timeline.helper.MessageInformationDataFactory import im.vector.app.features.home.room.detail.timeline.item.NoticeItem import im.vector.app.features.home.room.detail.timeline.item.NoticeItem_ +import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import javax.inject.Inject @@ -34,8 +35,9 @@ class NoticeItemFactory @Inject constructor(private val eventFormatter: NoticeEv fun create(event: TimelineEvent, highlight: Boolean, + roomSummary: RoomSummary?, callback: TimelineEventController.Callback?): NoticeItem? { - val formattedText = eventFormatter.format(event) ?: return null + val formattedText = eventFormatter.format(event, roomSummary) ?: return null val informationData = informationDataFactory.create(event, null) val attributes = NoticeItem.Attributes( avatarRenderer = avatarRenderer, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/RoomCreateItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/RoomCreateItemFactory.kt index 31adbdb8a6..25b5fd718b 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/RoomCreateItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/RoomCreateItemFactory.kt @@ -21,6 +21,7 @@ import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.UserPreferencesProvider import im.vector.app.features.home.room.detail.timeline.TimelineEventController +import im.vector.app.features.home.room.detail.timeline.helper.RoomSummaryHolder import im.vector.app.features.home.room.detail.timeline.item.RoomCreateItem_ import me.gujun.android.span.span import org.matrix.android.sdk.api.session.Session @@ -32,6 +33,7 @@ import javax.inject.Inject class RoomCreateItemFactory @Inject constructor(private val stringProvider: StringProvider, private val userPreferencesProvider: UserPreferencesProvider, private val session: Session, + private val roomSummaryHolder: RoomSummaryHolder, private val noticeItemFactory: NoticeItemFactory) { fun create(event: TimelineEvent, callback: TimelineEventController.Callback?): VectorEpoxyModel<*>? { @@ -52,7 +54,7 @@ class RoomCreateItemFactory @Inject constructor(private val stringProvider: Stri private fun defaultRendering(event: TimelineEvent, callback: TimelineEventController.Callback?): VectorEpoxyModel<*>? { return if (userPreferencesProvider.shouldShowHiddenEvents()) { - noticeItemFactory.create(event, false, callback) + noticeItemFactory.create(event, false, roomSummaryHolder.roomSummary, callback) } else { null } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt index 1f99efd08e..1a4db3bdfc 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt @@ -20,6 +20,7 @@ import im.vector.app.core.epoxy.EmptyItem_ import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.resources.UserPreferencesProvider import im.vector.app.features.home.room.detail.timeline.TimelineEventController +import im.vector.app.features.home.room.detail.timeline.helper.RoomSummaryHolder import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import timber.log.Timber @@ -31,6 +32,7 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me private val defaultItemFactory: DefaultItemFactory, private val encryptionItemFactory: EncryptionItemFactory, private val roomCreateItemFactory: RoomCreateItemFactory, + private val roomSummaryHolder: RoomSummaryHolder, private val verificationConclusionItemFactory: VerificationItemFactory, private val userPreferencesProvider: UserPreferencesProvider) { @@ -43,7 +45,7 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me val computedModel = try { when (event.root.getClearType()) { EventType.STICKER, - EventType.MESSAGE -> messageItemFactory.create(event, nextEvent, highlight, callback) + EventType.MESSAGE -> messageItemFactory.create(event, nextEvent, highlight, callback) // State and call EventType.STATE_ROOM_TOMBSTONE, EventType.STATE_ROOM_NAME, @@ -63,14 +65,12 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me EventType.CALL_ANSWER, EventType.STATE_ROOM_POWER_LEVELS, EventType.REACTION, - EventType.REDACTION -> noticeItemFactory.create(event, highlight, callback) - EventType.STATE_ROOM_ENCRYPTION -> { - encryptionItemFactory.create(event, highlight, callback) - } + EventType.REDACTION -> noticeItemFactory.create(event, highlight, roomSummaryHolder.roomSummary, callback) + EventType.STATE_ROOM_ENCRYPTION -> encryptionItemFactory.create(event, highlight, callback) // State room create - EventType.STATE_ROOM_CREATE -> roomCreateItemFactory.create(event, callback) + EventType.STATE_ROOM_CREATE -> roomCreateItemFactory.create(event, callback) // Crypto - EventType.ENCRYPTED -> { + EventType.ENCRYPTED -> { if (event.root.isRedacted()) { // Redacted event, let the MessageItemFactory handle it messageItemFactory.create(event, nextEvent, highlight, callback) @@ -83,22 +83,22 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me EventType.KEY_VERIFICATION_KEY, EventType.KEY_VERIFICATION_READY, EventType.KEY_VERIFICATION_MAC, - EventType.CALL_CANDIDATES -> { + EventType.CALL_CANDIDATES -> { // TODO These are not filtered out by timeline when encrypted // For now manually ignore if (userPreferencesProvider.shouldShowHiddenEvents()) { - noticeItemFactory.create(event, highlight, callback) + noticeItemFactory.create(event, highlight, roomSummaryHolder.roomSummary, callback) } else { null } } EventType.KEY_VERIFICATION_CANCEL, - EventType.KEY_VERIFICATION_DONE -> { + EventType.KEY_VERIFICATION_DONE -> { verificationConclusionItemFactory.create(event, highlight, callback) } // Unhandled event types - else -> { + else -> { // Should only happen when shouldShowHiddenEvents() settings is ON Timber.v("Type ${event.root.getClearType()} not handled") defaultItemFactory.create(event, highlight, callback) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VerificationItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VerificationItemFactory.kt index 0b623d78f1..59daf5a0a0 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VerificationItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VerificationItemFactory.kt @@ -24,6 +24,7 @@ import im.vector.app.features.home.room.detail.timeline.TimelineEventController import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider import im.vector.app.features.home.room.detail.timeline.helper.MessageInformationDataFactory import im.vector.app.features.home.room.detail.timeline.helper.MessageItemAttributesFactory +import im.vector.app.features.home.room.detail.timeline.helper.RoomSummaryHolder import im.vector.app.features.home.room.detail.timeline.item.StatusTileTimelineItem import im.vector.app.features.home.room.detail.timeline.item.StatusTileTimelineItem_ import org.matrix.android.sdk.api.session.Session @@ -50,6 +51,7 @@ class VerificationItemFactory @Inject constructor( private val avatarSizeProvider: AvatarSizeProvider, private val noticeItemFactory: NoticeItemFactory, private val userPreferencesProvider: UserPreferencesProvider, + private val roomSummaryHolder: RoomSummaryHolder, private val stringProvider: StringProvider, private val session: Session ) { @@ -151,7 +153,7 @@ class VerificationItemFactory @Inject constructor( highlight: Boolean, callback: TimelineEventController.Callback? ): VectorEpoxyModel<*>? { - if (userPreferencesProvider.shouldShowHiddenEvents()) return noticeItemFactory.create(event, highlight, callback) + if (userPreferencesProvider.shouldShowHiddenEvents()) return noticeItemFactory.create(event, highlight, roomSummaryHolder.roomSummary, callback) return null } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/DisplayableEventFormatter.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/DisplayableEventFormatter.kt index cb9f583a2a..f4632b0e10 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/DisplayableEventFormatter.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/DisplayableEventFormatter.kt @@ -23,6 +23,7 @@ import im.vector.app.core.resources.StringProvider import me.gujun.android.span.span import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.message.MessageOptionsContent import org.matrix.android.sdk.api.session.room.model.message.MessageType import org.matrix.android.sdk.api.session.room.model.message.OPTION_TYPE_BUTTONS @@ -40,7 +41,7 @@ class DisplayableEventFormatter @Inject constructor( private val noticeEventFormatter: NoticeEventFormatter ) { - fun format(timelineEvent: TimelineEvent, appendAuthor: Boolean): CharSequence { + fun format(timelineEvent: TimelineEvent, appendAuthor: Boolean, roomSummary: RoomSummary?): CharSequence { if (timelineEvent.root.isRedacted()) { return noticeEventFormatter.formatRedactedEvent(timelineEvent.root) } @@ -53,16 +54,16 @@ class DisplayableEventFormatter @Inject constructor( val senderName = timelineEvent.senderInfo.disambiguatedDisplayName when (timelineEvent.root.getClearType()) { - EventType.STICKER -> { + EventType.STICKER -> { return simpleFormat(senderName, stringProvider.getString(R.string.send_a_sticker), appendAuthor) } - EventType.REACTION -> { + EventType.REACTION -> { timelineEvent.root.getClearContent().toModel()?.relatesTo?.let { val emojiSpanned = emojiCompatWrapper.safeEmojiSpanify(stringProvider.getString(R.string.sent_a_reaction, it.key)) return simpleFormat(senderName, emojiSpanned, appendAuthor) } } - EventType.MESSAGE -> { + EventType.MESSAGE -> { timelineEvent.getLastMessageContent()?.let { messageContent -> when (messageContent.msgType) { MessageType.MSGTYPE_VERIFICATION_REQUEST -> { @@ -125,12 +126,12 @@ class DisplayableEventFormatter @Inject constructor( EventType.KEY_VERIFICATION_MAC, EventType.KEY_VERIFICATION_KEY, EventType.KEY_VERIFICATION_READY, - EventType.CALL_CANDIDATES -> { + EventType.CALL_CANDIDATES -> { return span { } } - else -> { + else -> { return span { - text = noticeEventFormatter.format(timelineEvent) ?: "" + text = noticeEventFormatter.format(timelineEvent, roomSummary) ?: "" textStyle = "italic" } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt index 9a4729abee..8055ef9a99 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt @@ -35,6 +35,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomJoinRules import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.api.session.room.model.RoomNameContent +import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomThirdPartyInviteContent import org.matrix.android.sdk.api.session.room.model.RoomTopicContent import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent @@ -56,23 +57,26 @@ class NoticeEventFormatter @Inject constructor(private val activeSessionDataSour private fun Event.isSentByCurrentUser() = senderId != null && senderId == currentUserId - fun format(timelineEvent: TimelineEvent): CharSequence? { + private fun RoomSummary?.isDm() = this?.isDirect.orFalse() + + fun format(timelineEvent: TimelineEvent, rs: RoomSummary?): CharSequence? { return when (val type = timelineEvent.root.getClearType()) { - EventType.STATE_ROOM_JOIN_RULES -> formatJoinRulesEvent(timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName) - EventType.STATE_ROOM_CREATE -> formatRoomCreateEvent(timelineEvent.root) + EventType.STATE_ROOM_JOIN_RULES -> formatJoinRulesEvent(timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName, rs) + EventType.STATE_ROOM_CREATE -> formatRoomCreateEvent(timelineEvent.root, rs) EventType.STATE_ROOM_NAME -> formatRoomNameEvent(timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName) EventType.STATE_ROOM_TOPIC -> formatRoomTopicEvent(timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName) EventType.STATE_ROOM_AVATAR -> formatRoomAvatarEvent(timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName) - EventType.STATE_ROOM_MEMBER -> formatRoomMemberEvent(timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName) - EventType.STATE_ROOM_THIRD_PARTY_INVITE -> formatRoomThirdPartyInvite(timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName) + EventType.STATE_ROOM_MEMBER -> formatRoomMemberEvent(timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName, rs) + EventType.STATE_ROOM_THIRD_PARTY_INVITE -> formatRoomThirdPartyInvite(timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName, rs) EventType.STATE_ROOM_ALIASES -> formatRoomAliasesEvent(timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName) EventType.STATE_ROOM_CANONICAL_ALIAS -> formatRoomCanonicalAliasEvent(timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName) - EventType.STATE_ROOM_HISTORY_VISIBILITY -> formatRoomHistoryVisibilityEvent(timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName) - EventType.STATE_ROOM_GUEST_ACCESS -> formatRoomGuestAccessEvent(timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName) + EventType.STATE_ROOM_HISTORY_VISIBILITY -> + formatRoomHistoryVisibilityEvent(timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName, rs) + EventType.STATE_ROOM_GUEST_ACCESS -> formatRoomGuestAccessEvent(timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName, rs) EventType.STATE_ROOM_ENCRYPTION -> formatRoomEncryptionEvent(timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName) EventType.STATE_ROOM_WIDGET, EventType.STATE_ROOM_WIDGET_LEGACY -> formatWidgetEvent(timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName) - EventType.STATE_ROOM_TOMBSTONE -> formatRoomTombstoneEvent(timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName) + EventType.STATE_ROOM_TOMBSTONE -> formatRoomTombstoneEvent(timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName, rs) EventType.STATE_ROOM_POWER_LEVELS -> formatRoomPowerLevels(timelineEvent.root, timelineEvent.senderInfo.disambiguatedDisplayName) EventType.CALL_INVITE, EventType.CALL_CANDIDATES, @@ -151,19 +155,19 @@ class NoticeEventFormatter @Inject constructor(private val activeSessionDataSour } } - fun format(event: Event, senderName: String?): CharSequence? { + fun format(event: Event, senderName: String?, rs: RoomSummary?): CharSequence? { return when (val type = event.getClearType()) { - EventType.STATE_ROOM_JOIN_RULES -> formatJoinRulesEvent(event, senderName) + EventType.STATE_ROOM_JOIN_RULES -> formatJoinRulesEvent(event, senderName, rs) EventType.STATE_ROOM_NAME -> formatRoomNameEvent(event, senderName) EventType.STATE_ROOM_TOPIC -> formatRoomTopicEvent(event, senderName) EventType.STATE_ROOM_AVATAR -> formatRoomAvatarEvent(event, senderName) - EventType.STATE_ROOM_MEMBER -> formatRoomMemberEvent(event, senderName) - EventType.STATE_ROOM_THIRD_PARTY_INVITE -> formatRoomThirdPartyInvite(event, senderName) - EventType.STATE_ROOM_HISTORY_VISIBILITY -> formatRoomHistoryVisibilityEvent(event, senderName) + EventType.STATE_ROOM_MEMBER -> formatRoomMemberEvent(event, senderName, rs) + EventType.STATE_ROOM_THIRD_PARTY_INVITE -> formatRoomThirdPartyInvite(event, senderName, rs) + EventType.STATE_ROOM_HISTORY_VISIBILITY -> formatRoomHistoryVisibilityEvent(event, senderName, rs) EventType.CALL_INVITE, EventType.CALL_HANGUP, EventType.CALL_ANSWER -> formatCallEvent(type, event, senderName) - EventType.STATE_ROOM_TOMBSTONE -> formatRoomTombstoneEvent(event, senderName) + EventType.STATE_ROOM_TOMBSTONE -> formatRoomTombstoneEvent(event, senderName, rs) else -> { Timber.v("Type $type not handled by this formatter") null @@ -175,14 +179,14 @@ class NoticeEventFormatter @Inject constructor(private val activeSessionDataSour return "{ \"type\": ${event.getClearType()} }" } - private fun formatRoomCreateEvent(event: Event): CharSequence? { + private fun formatRoomCreateEvent(event: Event, rs: RoomSummary?): CharSequence? { return event.getClearContent().toModel() ?.takeIf { it.creator.isNullOrBlank().not() } ?.let { if (event.isSentByCurrentUser()) { - sp.getString(R.string.notice_room_created_by_you) + sp.getString(if (rs.isDm()) R.string.notice_direct_room_created_by_you else R.string.notice_room_created_by_you) } else { - sp.getString(R.string.notice_room_created, it.creator) + sp.getString(if (rs.isDm()) R.string.notice_direct_room_created else R.string.notice_room_created, it.creator) } } } @@ -204,11 +208,11 @@ class NoticeEventFormatter @Inject constructor(private val activeSessionDataSour } } - private fun formatRoomTombstoneEvent(event: Event, senderName: String?): CharSequence? { + private fun formatRoomTombstoneEvent(event: Event, senderName: String?, rs: RoomSummary?): CharSequence? { return if (event.isSentByCurrentUser()) { - sp.getString(R.string.notice_room_update_by_you) + sp.getString(if (rs.isDm()) R.string.notice_direct_room_update_by_you else R.string.notice_room_update_by_you) } else { - sp.getString(R.string.notice_room_update, senderName) + sp.getString(if (rs.isDm()) R.string.notice_direct_room_update else R.string.notice_room_update, senderName) } } @@ -246,18 +250,20 @@ class NoticeEventFormatter @Inject constructor(private val activeSessionDataSour } } - private fun formatRoomHistoryVisibilityEvent(event: Event, senderName: String?): CharSequence? { + private fun formatRoomHistoryVisibilityEvent(event: Event, senderName: String?, rs: RoomSummary?): CharSequence? { val historyVisibility = event.getClearContent().toModel()?.historyVisibility ?: return null val formattedVisibility = roomHistoryVisibilityFormatter.format(historyVisibility) return if (event.isSentByCurrentUser()) { - sp.getString(R.string.notice_made_future_room_visibility_by_you, formattedVisibility) + sp.getString(if (rs.isDm()) R.string.notice_made_future_direct_room_visibility_by_you else R.string.notice_made_future_room_visibility_by_you, + formattedVisibility) } else { - sp.getString(R.string.notice_made_future_room_visibility, senderName, formattedVisibility) + sp.getString(if (rs.isDm()) R.string.notice_made_future_direct_room_visibility else R.string.notice_made_future_room_visibility, + senderName, formattedVisibility) } } - private fun formatRoomThirdPartyInvite(event: Event, senderName: String?): CharSequence? { + private fun formatRoomThirdPartyInvite(event: Event, senderName: String?, rs: RoomSummary?): CharSequence? { val content = event.getClearContent().toModel() val prevContent = event.resolvedPrevContent()?.toModel() @@ -265,17 +271,26 @@ class NoticeEventFormatter @Inject constructor(private val activeSessionDataSour prevContent != null -> { // Revoke case if (event.isSentByCurrentUser()) { - sp.getString(R.string.notice_room_third_party_revoked_invite_by_you, prevContent.displayName) + sp.getString( + if (rs.isDm()) { + R.string.notice_direct_room_third_party_revoked_invite_by_you + } else { + R.string.notice_room_third_party_revoked_invite_by_you + }, + prevContent.displayName) } else { - sp.getString(R.string.notice_room_third_party_revoked_invite, senderName, prevContent.displayName) + sp.getString(if (rs.isDm()) R.string.notice_direct_room_third_party_revoked_invite else R.string.notice_room_third_party_revoked_invite, + senderName, prevContent.displayName) } } content != null -> { // Invitation case if (event.isSentByCurrentUser()) { - sp.getString(R.string.notice_room_third_party_invite_by_you, content.displayName) + sp.getString(if (rs.isDm()) R.string.notice_direct_room_third_party_invite_by_you else R.string.notice_room_third_party_invite_by_you, + content.displayName) } else { - sp.getString(R.string.notice_room_third_party_invite, senderName, content.displayName) + sp.getString(if (rs.isDm()) R.string.notice_direct_room_third_party_invite else R.string.notice_room_third_party_invite, + senderName, content.displayName) } } else -> null @@ -323,13 +338,13 @@ class NoticeEventFormatter @Inject constructor(private val activeSessionDataSour } } - private fun formatRoomMemberEvent(event: Event, senderName: String?): String? { + private fun formatRoomMemberEvent(event: Event, senderName: String?, rs: RoomSummary?): String? { val eventContent: RoomMemberContent? = event.getClearContent().toModel() val prevEventContent: RoomMemberContent? = event.resolvedPrevContent().toModel() val isMembershipEvent = prevEventContent?.membership != eventContent?.membership || eventContent?.membership == Membership.LEAVE return if (isMembershipEvent) { - buildMembershipNotice(event, senderName, eventContent, prevEventContent) + buildMembershipNotice(event, senderName, eventContent, prevEventContent, rs) } else { buildProfileNotice(event, senderName, eventContent, prevEventContent) } @@ -387,26 +402,35 @@ class NoticeEventFormatter @Inject constructor(private val activeSessionDataSour } } - private fun formatRoomGuestAccessEvent(event: Event, senderName: String?): String? { + private fun formatRoomGuestAccessEvent(event: Event, senderName: String?, rs: RoomSummary?): String? { val eventContent: RoomGuestAccessContent? = event.getClearContent().toModel() return when (eventContent?.guestAccess) { GuestAccess.CanJoin -> if (event.isSentByCurrentUser()) { - sp.getString(R.string.notice_room_guest_access_can_join_by_you) + sp.getString( + if (rs.isDm()) R.string.notice_direct_room_guest_access_can_join_by_you else R.string.notice_room_guest_access_can_join_by_you + ) } else { - sp.getString(R.string.notice_room_guest_access_can_join, senderName) + sp.getString(if (rs.isDm()) R.string.notice_direct_room_guest_access_can_join else R.string.notice_room_guest_access_can_join, + senderName) } GuestAccess.Forbidden -> if (event.isSentByCurrentUser()) { - sp.getString(R.string.notice_room_guest_access_forbidden_by_you) + sp.getString( + if (rs.isDm()) R.string.notice_direct_room_guest_access_forbidden_by_you else R.string.notice_room_guest_access_forbidden_by_you + ) } else { - sp.getString(R.string.notice_room_guest_access_forbidden, senderName) + sp.getString(if (rs.isDm()) R.string.notice_direct_room_guest_access_forbidden else R.string.notice_room_guest_access_forbidden, + senderName) } else -> null } } private fun formatRoomEncryptionEvent(event: Event, senderName: String?): CharSequence? { + if (!event.isStateEvent()) { + return null + } val content = event.content.toModel() ?: return null return when (content.algorithm) { MXCRYPTO_ALGORITHM_MEGOLM -> @@ -476,7 +500,11 @@ class NoticeEventFormatter @Inject constructor(private val activeSessionDataSour return displayText.toString() } - private fun buildMembershipNotice(event: Event, senderName: String?, eventContent: RoomMemberContent?, prevEventContent: RoomMemberContent?): String? { + private fun buildMembershipNotice(event: Event, + senderName: String?, + eventContent: RoomMemberContent?, + prevEventContent: RoomMemberContent?, + rs: RoomSummary?): String? { val senderDisplayName = senderName ?: event.senderId ?: "" val targetDisplayName = eventContent?.displayName ?: prevEventContent?.displayName ?: event.stateKey ?: "" return when (eventContent?.membership) { @@ -524,14 +552,21 @@ class NoticeEventFormatter @Inject constructor(private val activeSessionDataSour } } Membership.JOIN -> - if (event.isSentByCurrentUser()) { - eventContent.safeReason?.let { reason -> - sp.getString(R.string.notice_room_join_with_reason_by_you, reason) - } ?: sp.getString(R.string.notice_room_join_by_you) - } else { - eventContent.safeReason?.let { reason -> - sp.getString(R.string.notice_room_join_with_reason, senderDisplayName, reason) - } ?: sp.getString(R.string.notice_room_join, senderDisplayName) + eventContent.safeReason?.let { reason -> + if (event.isSentByCurrentUser()) { + sp.getString(if (rs.isDm()) R.string.notice_direct_room_join_with_reason_by_you else R.string.notice_room_join_with_reason_by_you, + reason) + } else { + sp.getString(if (rs.isDm()) R.string.notice_direct_room_join_with_reason else R.string.notice_room_join_with_reason, + senderDisplayName, reason) + } + } ?: run { + if (event.isSentByCurrentUser()) { + sp.getString(if (rs.isDm()) R.string.notice_direct_room_join_by_you else R.string.notice_room_join_by_you) + } else { + sp.getString(if (rs.isDm()) R.string.notice_direct_room_join else R.string.notice_room_join, + senderDisplayName) + } } Membership.LEAVE -> // 2 cases here: this member may have left voluntarily or they may have been "left" by someone else ie. kicked @@ -548,14 +583,27 @@ class NoticeEventFormatter @Inject constructor(private val activeSessionDataSour } ?: sp.getString(R.string.notice_room_reject, senderDisplayName) } else -> - if (event.isSentByCurrentUser()) { - eventContent.safeReason?.let { reason -> - sp.getString(R.string.notice_room_leave_with_reason_by_you, reason) - } ?: sp.getString(R.string.notice_room_leave_by_you) - } else { - eventContent.safeReason?.let { reason -> - sp.getString(R.string.notice_room_leave_with_reason, senderDisplayName, reason) - } ?: sp.getString(R.string.notice_room_leave, senderDisplayName) + eventContent.safeReason?.let { reason -> + if (event.isSentByCurrentUser()) { + sp.getString( + if (rs.isDm()) { + R.string.notice_direct_room_leave_with_reason_by_you + } else { + R.string.notice_room_leave_with_reason_by_you + }, + reason + ) + } else { + sp.getString(if (rs.isDm()) R.string.notice_direct_room_leave_with_reason else R.string.notice_room_leave_with_reason, + senderDisplayName, reason) + } + } ?: run { + if (event.isSentByCurrentUser()) { + sp.getString(if (rs.isDm()) R.string.notice_direct_room_leave_by_you else R.string.notice_room_leave_by_you) + } else { + sp.getString(if (rs.isDm()) R.string.notice_direct_room_leave else R.string.notice_room_leave, + senderDisplayName) + } } } } else { @@ -618,14 +666,15 @@ class NoticeEventFormatter @Inject constructor(private val activeSessionDataSour } } - private fun formatJoinRulesEvent(event: Event, senderName: String?): CharSequence? { + private fun formatJoinRulesEvent(event: Event, senderName: String?, rs: RoomSummary?): CharSequence? { val content = event.getClearContent().toModel() ?: return null return when (content.joinRules) { RoomJoinRules.INVITE -> if (event.isSentByCurrentUser()) { - sp.getString(R.string.room_join_rules_invite_by_you) + sp.getString(if (rs.isDm()) R.string.direct_room_join_rules_invite_by_you else R.string.room_join_rules_invite_by_you) } else { - sp.getString(R.string.room_join_rules_invite, senderName) + sp.getString(if (rs.isDm()) R.string.direct_room_join_rules_invite else R.string.room_join_rules_invite, + senderName) } RoomJoinRules.PUBLIC -> if (event.isSentByCurrentUser()) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineDisplayableEvents.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineDisplayableEvents.kt index b1b1109580..14b8c12fee 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineDisplayableEvents.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineDisplayableEvents.kt @@ -55,7 +55,7 @@ fun TimelineEvent.canBeMerged(): Boolean { } fun TimelineEvent.isRoomConfiguration(roomCreatorUserId: String?): Boolean { - return when (root.getClearType()) { + return root.isStateEvent() && when (root.getClearType()) { EventType.STATE_ROOM_GUEST_ACCESS, EventType.STATE_ROOM_HISTORY_VISIBILITY, EventType.STATE_ROOM_JOIN_RULES, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsBaseMessageItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsBaseMessageItem.kt index 631cd9ff66..29aca2c4d5 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsBaseMessageItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsBaseMessageItem.kt @@ -136,8 +136,10 @@ abstract class AbsBaseMessageItem : BaseEventItem val messageColorProvider: MessageColorProvider val itemLongClickListener: View.OnLongClickListener? val itemClickListener: View.OnClickListener? + // val memberClickListener: View.OnClickListener? val reactionPillCallback: TimelineEventController.ReactionPillCallback? + // val avatarCallback: TimelineEventController.AvatarCallback? val readReceiptsCallback: TimelineEventController.ReadReceiptsCallback? // val emojiTypeFace: Typeface? diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/BaseEventItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/BaseEventItem.kt index 3f921fdd15..280a148dae 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/BaseEventItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/BaseEventItem.kt @@ -43,6 +43,7 @@ abstract class BaseEventItem : VectorEpoxyModel // To use for instance when opening a permalink with an eventId @EpoxyAttribute var highlighted: Boolean = false + @EpoxyAttribute open var leftGuideline: Int = 0 diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/BasedMergedItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/BasedMergedItem.kt index d9dcc98ed1..1f8ad3df1b 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/BasedMergedItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/BasedMergedItem.kt @@ -62,7 +62,8 @@ abstract class BasedMergedItem : BaseEventItem() val eventId: String, val userId: String, val memberName: String, - val avatarUrl: String? + val avatarUrl: String?, + val isDirectRoom: Boolean ) fun Data.toMatrixItem() = MatrixItem.UserItem(userId, memberName, avatarUrl) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt index dad5f99eeb..1896a812fc 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt @@ -48,9 +48,17 @@ abstract class MergedRoomCreationItem : BasedMergedItem() { - - @EpoxyAttribute - override lateinit var attributes: Attributes - - @EpoxyAttribute - var big: Boolean? = false - - override fun getViewType() = STUB_ID - - override fun bind(holder: Holder) { - super.bind(holder) - - holder.mergedTile.updateLayoutParams { - this.marginEnd = leftGuideline - if (big == true) { - this.height = TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, - 800f, - holder.view.context.resources.displayMetrics - ).toInt() - } else { - this.height = LinearLayout.LayoutParams.WRAP_CONTENT - } - } - -// if (attributes.isCollapsed) { -// // Take the oldest data -// val data = distinctMergeData.lastOrNull() -// -// val summary = holder.expandView.resources.getString(R.string.room_created_summary_item, -// data?.memberName ?: data?.userId ?: "") -// holder.summaryView.text = summary -// holder.summaryView.visibility = View.VISIBLE -// holder.avatarView.visibility = View.VISIBLE -// if (data != null) { -// holder.avatarView.visibility = View.VISIBLE -// attributes.avatarRenderer.render(data.toMatrixItem(), holder.avatarView) -// } else { -// holder.avatarView.visibility = View.GONE -// } -// -// if (attributes.hasEncryptionEvent) { -// holder.encryptionTile.isVisible = true -// holder.encryptionTile.updateLayoutParams { -// this.marginEnd = leftGuideline -// } -// if (attributes.isEncryptionAlgorithmSecure) { -// holder.e2eTitleTextView.text = holder.expandView.resources.getString(R.string.encryption_enabled) -// holder.e2eTitleDescriptionView.text = holder.expandView.resources.getString(R.string.encryption_enabled_tile_description) -// holder.e2eTitleDescriptionView.textAlignment = View.TEXT_ALIGNMENT_CENTER -// holder.e2eTitleTextView.setCompoundDrawablesWithIntrinsicBounds( -// ContextCompat.getDrawable(holder.view.context, R.drawable.ic_shield_black), -// null, null, null -// ) -// } else { -// holder.e2eTitleTextView.text = holder.expandView.resources.getString(R.string.encryption_not_enabled) -// holder.e2eTitleDescriptionView.text = holder.expandView.resources.getString(R.string.encryption_unknown_algorithm_tile_description) -// holder.e2eTitleTextView.setCompoundDrawablesWithIntrinsicBounds( -// ContextCompat.getDrawable(holder.view.context, R.drawable.ic_shield_warning), -// null, null, null -// ) -// } -// } else { -// holder.encryptionTile.isVisible = false -// } -// } else { -// holder.avatarView.visibility = View.INVISIBLE -// holder.summaryView.visibility = View.GONE -// holder.encryptionTile.isGone = true -// } - // No read receipt for this item - holder.readReceiptsView.isVisible = false - } - - class Holder : BasedMergedItem.Holder(STUB_ID) { - // val summaryView by bind(R.id.itemNoticeTextView) -// val avatarView by bind(R.id.itemNoticeAvatarView) - val mergedTile by bind(R.id.mergedUTDTile) -// -// val e2eTitleTextView by bind(R.id.itemVerificationDoneTitleTextView) -// val e2eTitleDescriptionView by bind(R.id.itemVerificationDoneDetailTextView) - } - - companion object { - private const val STUB_ID = R.id.messageContentMergedUTDStub - } - - data class Attributes( - override val isCollapsed: Boolean, - override val mergeData: List, - override val avatarRenderer: AvatarRenderer, - override val readReceiptsCallback: TimelineEventController.ReadReceiptsCallback? = null, - override val onCollapsedStateChanged: (Boolean) -> Unit - ) : BasedMergedItem.Attributes -} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageBlockCodeItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageBlockCodeItem.kt index 1309d4d084..45c00e1843 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageBlockCodeItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageBlockCodeItem.kt @@ -29,6 +29,7 @@ abstract class MessageBlockCodeItem : AbsMessageItem() { } } else { holder.resultWrapper.isVisible = true - val maxCount = votes?.maxBy { it.value }?.value ?: 0 + val maxCount = votes?.maxByOrNull { it.value }?.value ?: 0 optionsContent?.options?.forEachIndexed { index, item -> if (index < resultLines.size) { val optionCount = votes?.get(index) ?: 0 diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageTextItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageTextItem.kt index ffe15e2745..5cd45547b1 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageTextItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageTextItem.kt @@ -32,10 +32,13 @@ abstract class MessageTextItem : AbsMessageItem() { @EpoxyAttribute var searchForPills: Boolean = false + @EpoxyAttribute var message: CharSequence? = null + @EpoxyAttribute var useBigFont: Boolean = false + @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var movementMethod: MovementMethod? = null diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ReactionInfoSimpleItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ReactionInfoSimpleItem.kt index 9fc5275a81..2d60556fa3 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ReactionInfoSimpleItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ReactionInfoSimpleItem.kt @@ -32,10 +32,13 @@ abstract class ReactionInfoSimpleItem : EpoxyModelWithHolder Unit)? = null diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsBottomSheet.kt index a5555e4ebb..2e0d07aa67 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsBottomSheet.kt @@ -17,6 +17,7 @@ package im.vector.app.features.home.room.detail.timeline.reactions import android.os.Bundle +import android.view.View import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import com.airbnb.mvrx.MvRx @@ -55,8 +56,8 @@ class ViewReactionsBottomSheet : VectorBaseBottomSheetDialogFragment(), ViewReac override fun getLayoutResId() = R.layout.bottom_sheet_generic_list_with_title - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java) recyclerView.configureWith(epoxyController, hasFixedSize = false, showDivider = true) bottomSheetTitle.text = context?.getString(R.string.reactions) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetItem.kt index 691b6ec4ee..33a6f627a1 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetItem.kt @@ -36,6 +36,7 @@ abstract class RoomWidgetItem : EpoxyModelWithHolder() { @EpoxyAttribute lateinit var widget: Widget @EpoxyAttribute var widgetClicked: ClickListener? = null + @DrawableRes @EpoxyAttribute var iconRes: Int? = null diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBannerView.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBannerView.kt index 19897a9300..7fe8b87fe0 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBannerView.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBannerView.kt @@ -21,8 +21,8 @@ import android.util.AttributeSet import android.view.View import android.widget.RelativeLayout import im.vector.app.R -import org.matrix.android.sdk.api.session.widgets.model.Widget import kotlinx.android.synthetic.main.view_room_widgets_banner.view.* +import org.matrix.android.sdk.api.session.widgets.model.Widget class RoomWidgetsBannerView @JvmOverloads constructor( context: Context, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBottomSheet.kt index e87f5cdaa9..923f9d8e2e 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBottomSheet.kt @@ -17,6 +17,7 @@ package im.vector.app.features.home.room.detail.widget import android.os.Bundle +import android.view.View import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import com.airbnb.mvrx.parentFragmentViewModel @@ -31,8 +32,8 @@ import im.vector.app.features.home.room.detail.RoomDetailAction import im.vector.app.features.home.room.detail.RoomDetailViewModel import im.vector.app.features.home.room.detail.RoomDetailViewState import im.vector.app.features.navigation.Navigator -import org.matrix.android.sdk.api.session.widgets.model.Widget import kotlinx.android.synthetic.main.bottom_sheet_generic_list_with_title.* +import org.matrix.android.sdk.api.session.widgets.model.Widget import javax.inject.Inject /** @@ -55,8 +56,8 @@ class RoomWidgetsBottomSheet : VectorBaseBottomSheetDialogFragment(), RoomWidget override fun getLayoutResId() = R.layout.bottom_sheet_generic_list_with_title - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) recyclerView.configureWith(epoxyController, hasFixedSize = false) bottomSheetTitle.text = getString(R.string.active_widgets_title) bottomSheetTitle.textSize = 20f diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListDisplayModeFilter.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListDisplayModeFilter.kt index f6b2a2e298..b5b3feb599 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListDisplayModeFilter.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListDisplayModeFilter.kt @@ -17,9 +17,9 @@ package im.vector.app.features.home.room.list import im.vector.app.features.home.RoomListDisplayMode +import io.reactivex.functions.Predicate import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomSummary -import io.reactivex.functions.Predicate class RoomListDisplayModeFilter(private val displayMode: RoomListDisplayMode) : Predicate { @@ -32,8 +32,8 @@ class RoomListDisplayModeFilter(private val displayMode: RoomListDisplayMode) : RoomListDisplayMode.NOTIFICATIONS -> roomSummary.notificationCount > 0 || roomSummary.membership == Membership.INVITE || roomSummary.userDrafts.isNotEmpty() RoomListDisplayMode.PEOPLE -> roomSummary.isDirect && roomSummary.membership.isActive() - RoomListDisplayMode.ROOMS -> !roomSummary.isDirect && roomSummary.membership.isActive() - RoomListDisplayMode.FILTERED -> roomSummary.membership == Membership.JOIN + RoomListDisplayMode.ROOMS -> !roomSummary.isDirect && roomSummary.membership.isActive() + RoomListDisplayMode.FILTERED -> roomSummary.membership == Membership.JOIN } } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt index 47f036e143..57102b8bf1 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt @@ -47,12 +47,12 @@ import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedA import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel import im.vector.app.features.home.room.list.widget.FabMenuView import im.vector.app.features.notifications.NotificationDrawerManager +import kotlinx.android.parcel.Parcelize +import kotlinx.android.synthetic.main.fragment_room_list.* import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState -import kotlinx.android.parcel.Parcelize -import kotlinx.android.synthetic.main.fragment_room_list.* import javax.inject.Inject @Parcelize @@ -155,8 +155,8 @@ class RoomListFragment @Inject constructor( RoomListDisplayMode.ALL, RoomListDisplayMode.NOTIFICATIONS -> createChatFabMenu.isVisible = true RoomListDisplayMode.PEOPLE -> createChatRoomButton.isVisible = true - RoomListDisplayMode.ROOMS -> createGroupRoomButton.isVisible = true - else -> Unit // No button in this mode + RoomListDisplayMode.ROOMS -> createGroupRoomButton.isVisible = true + else -> Unit // No button in this mode } createChatRoomButton.debouncedClicks { @@ -182,8 +182,8 @@ class RoomListFragment @Inject constructor( RoomListDisplayMode.ALL, RoomListDisplayMode.NOTIFICATIONS -> createChatFabMenu.hide() RoomListDisplayMode.PEOPLE -> createChatRoomButton.hide() - RoomListDisplayMode.ROOMS -> createGroupRoomButton.hide() - else -> Unit + RoomListDisplayMode.ROOMS -> createGroupRoomButton.hide() + else -> Unit } } } @@ -226,8 +226,8 @@ class RoomListFragment @Inject constructor( RoomListDisplayMode.ALL, RoomListDisplayMode.NOTIFICATIONS -> createChatFabMenu.show() RoomListDisplayMode.PEOPLE -> createChatRoomButton.show() - RoomListDisplayMode.ROOMS -> createGroupRoomButton.show() - else -> Unit + RoomListDisplayMode.ROOMS -> createGroupRoomButton.show() + else -> Unit } } } @@ -326,19 +326,19 @@ class RoomListFragment @Inject constructor( getString(R.string.room_list_catchup_empty_body)) } } - RoomListDisplayMode.PEOPLE -> + RoomListDisplayMode.PEOPLE -> StateView.State.Empty( getString(R.string.room_list_people_empty_title), ContextCompat.getDrawable(requireContext(), R.drawable.ic_home_bottom_chat), getString(R.string.room_list_people_empty_body) ) - RoomListDisplayMode.ROOMS -> + RoomListDisplayMode.ROOMS -> StateView.State.Empty( getString(R.string.room_list_rooms_empty_title), ContextCompat.getDrawable(requireContext(), R.drawable.ic_home_bottom_group), getString(R.string.room_list_rooms_empty_body) ) - else -> + else -> // Always display the content in this mode, because if the footer StateView.State.Content } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListNameFilter.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListNameFilter.kt index 4c4a9755d2..274cf7c869 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListNameFilter.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListNameFilter.kt @@ -16,8 +16,8 @@ package im.vector.app.features.home.room.list -import org.matrix.android.sdk.api.session.room.model.RoomSummary import io.reactivex.functions.Predicate +import org.matrix.android.sdk.api.session.room.model.RoomSummary import javax.inject.Inject class RoomListNameFilter @Inject constructor() : Predicate { @@ -25,7 +25,7 @@ class RoomListNameFilter @Inject constructor() : Predicate { var filter: String = "" override fun test(roomSummary: RoomSummary): Boolean { - if (filter.isBlank()) { + if (filter.isEmpty()) { // No filter return true } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt index 4e11d91a4b..cbe4509ed0 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt @@ -24,6 +24,7 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.utils.DataSource import im.vector.app.features.home.RoomListDisplayMode +import io.reactivex.schedulers.Schedulers import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.NoOpMatrixCallback import org.matrix.android.sdk.api.extensions.orFalse @@ -31,7 +32,6 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.tag.RoomTag -import io.reactivex.schedulers.Schedulers import org.matrix.android.sdk.rx.rx import timber.log.Timber import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItem.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItem.kt index 304a592e7d..461374a85a 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItem.kt @@ -43,8 +43,10 @@ abstract class RoomSummaryItem : VectorEpoxyModel() { @EpoxyAttribute lateinit var typingMessage: CharSequence @EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer @EpoxyAttribute lateinit var matrixItem: MatrixItem + // Used only for diff calculation @EpoxyAttribute lateinit var lastEvent: String + // We use DoNotHash here as Spans are not implementing equals/hashcode @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) lateinit var lastFormattedEvent: CharSequence @EpoxyAttribute lateinit var lastEventTime: CharSequence diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItemFactory.kt index a2715e86b4..98da91effa 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItemFactory.kt @@ -88,7 +88,7 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor var latestEventTime: CharSequence = "" val latestEvent = roomSummary.scLatestPreviewableEvent(scSdkPreferences) if (latestEvent != null) { - latestFormattedEvent = displayableEventFormatter.format(latestEvent, roomSummary.isDirect.not()) + latestFormattedEvent = displayableEventFormatter.format(latestEvent, roomSummary.isDirect.not(), roomSummary) latestEventTime = dateFormatter.format(latestEvent.root.originServerTs, DateFormatKind.ROOM_LIST) } val typingMessage = typingHelper.getTypingMessage(roomSummary.typingUsers) diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt index c33d964d22..ccd38125f9 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt @@ -18,6 +18,7 @@ package im.vector.app.features.home.room.list.actions import android.os.Bundle import android.os.Parcelable +import android.view.View import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import com.airbnb.mvrx.fragmentViewModel @@ -67,8 +68,8 @@ class RoomListQuickActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), R override fun getLayoutResId() = R.layout.bottom_sheet_generic_list - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) sharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java) recyclerView.configureWith(roomListActionsEpoxyController, viewPool = sharedViewPool, hasFixedSize = false, disableItemAnimation = true) roomListActionsEpoxyController.listener = this diff --git a/vector/src/main/java/im/vector/app/features/html/FontTagHandler.kt b/vector/src/main/java/im/vector/app/features/html/FontTagHandler.kt index 72e337b48f..6a925997b7 100644 --- a/vector/src/main/java/im/vector/app/features/html/FontTagHandler.kt +++ b/vector/src/main/java/im/vector/app/features/html/FontTagHandler.kt @@ -16,8 +16,8 @@ package im.vector.app.features.html import android.graphics.Color -import android.text.style.ForegroundColorSpan import android.text.style.BackgroundColorSpan +import android.text.style.ForegroundColorSpan import io.noties.markwon.MarkwonConfiguration import io.noties.markwon.RenderProps import io.noties.markwon.html.HtmlTag diff --git a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomActivity.kt b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomActivity.kt index b4f0aabea5..087b7c2f55 100644 --- a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomActivity.kt +++ b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomActivity.kt @@ -112,9 +112,10 @@ class InviteUsersToRoomActivity : SimpleFragmentActivity() { } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (allGranted(grantResults)) { if (requestCode == PERMISSION_REQUEST_CODE_READ_CONTACTS) { - doOnPostResume { addFragmentToBackstack(R.id.container, ContactsBookFragment::class.java) } + doOnPostResume { addFragmentToBackstack(R.id.container, ContactsBookFragment::class.java) } } } } diff --git a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewEvents.kt b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewEvents.kt index 786fab58c3..87b10f909f 100644 --- a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewEvents.kt @@ -18,7 +18,7 @@ package im.vector.app.features.invite import im.vector.app.core.platform.VectorViewEvents -sealed class InviteUsersToRoomViewEvents : VectorViewEvents { +sealed class InviteUsersToRoomViewEvents : VectorViewEvents { object Loading : InviteUsersToRoomViewEvents() data class Failure(val throwable: Throwable) : InviteUsersToRoomViewEvents() data class Success(val successMessage: String) : InviteUsersToRoomViewEvents() diff --git a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt index 23325a9c41..21a998d8e2 100644 --- a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt @@ -25,9 +25,9 @@ import im.vector.app.R import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.features.userdirectory.PendingInvitee +import io.reactivex.Observable import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.rx.rx -import io.reactivex.Observable class InviteUsersToRoomViewModel @AssistedInject constructor(@Assisted initialState: InviteUsersToRoomViewState, diff --git a/vector/src/main/java/im/vector/app/features/invite/VectorInviteView.kt b/vector/src/main/java/im/vector/app/features/invite/VectorInviteView.kt index 2de587e52b..881c446eb2 100644 --- a/vector/src/main/java/im/vector/app/features/invite/VectorInviteView.kt +++ b/vector/src/main/java/im/vector/app/features/invite/VectorInviteView.kt @@ -25,10 +25,10 @@ import im.vector.app.R import im.vector.app.core.di.HasScreenInjector import im.vector.app.core.platform.ButtonStateView import im.vector.app.features.home.AvatarRenderer +import kotlinx.android.synthetic.main.vector_invite_view.view.* import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.user.model.User import org.matrix.android.sdk.api.util.toMatrixItem -import kotlinx.android.synthetic.main.vector_invite_view.view.* import javax.inject.Inject class VectorInviteView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) diff --git a/vector/src/main/java/im/vector/app/features/login/LoginAction.kt b/vector/src/main/java/im/vector/app/features/login/LoginAction.kt index 9067984852..eb5aa86b3b 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginAction.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginAction.kt @@ -27,7 +27,7 @@ sealed class LoginAction : VectorViewModelAction { data class UpdateSignMode(val signMode: SignMode) : LoginAction() data class LoginWithToken(val loginToken: String) : LoginAction() data class WebLoginSuccess(val credentials: Credentials) : LoginAction() - data class InitWith(val loginConfig: LoginConfig) : LoginAction() + data class InitWith(val loginConfig: LoginConfig?) : LoginAction() data class ResetPassword(val email: String, val newPassword: String) : LoginAction() object ResetPasswordMailConfirmed : LoginAction() @@ -39,6 +39,7 @@ sealed class LoginAction : VectorViewModelAction { data class AddThreePid(val threePid: RegisterThreePid) : RegisterAction() object SendAgainThreePid : RegisterAction() + // TODO Confirm Email (from link in the email, open in the phone, intercepted by RiotX) data class ValidateThreePid(val code: String) : RegisterAction() diff --git a/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt index 2a692c2d53..01e835b4e3 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt @@ -91,19 +91,19 @@ open class LoginActivity : VectorBaseActivity(), ToolbarConfigurable, UnlockedAc addFirstFragment() } - // Get config extra - val loginConfig = intent.getParcelableExtra(EXTRA_CONFIG) - if (loginConfig != null && isFirstCreation()) { - // TODO Check this - loginViewModel.handle(LoginAction.InitWith(loginConfig)) - } - loginViewModel .subscribe(this) { updateWithState(it) } loginViewModel.observeViewEvents { handleLoginViewEvents(it) } + + // Get config extra + val loginConfig = intent.getParcelableExtra(EXTRA_CONFIG) + if (isFirstCreation()) { + // TODO Check this + loginViewModel.handle(LoginAction.InitWith(loginConfig)) + } } protected open fun addFirstFragment() { @@ -146,8 +146,10 @@ open class LoginActivity : VectorBaseActivity(), ToolbarConfigurable, UnlockedAc LoginServerSelectionFragment::class.java, option = { ft -> findViewById(R.id.loginSplashLogo)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } - findViewById(R.id.loginSplashTitle)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } - findViewById(R.id.loginSplashSubmit)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } + // Disable transition of text + // findViewById(R.id.loginSplashTitle)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } + // No transition here now actually + // findViewById(R.id.loginSplashSubmit)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } // TODO Disabled because it provokes a flickering // ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim) }) diff --git a/vector/src/main/java/im/vector/app/features/login/LoginCaptchaFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginCaptchaFragment.kt index f84c4b7022..6dd17a7d58 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginCaptchaFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginCaptchaFragment.kt @@ -33,9 +33,9 @@ import androidx.core.view.isVisible import com.airbnb.mvrx.args import im.vector.app.R import im.vector.app.core.utils.AssetReader -import org.matrix.android.sdk.internal.di.MoshiProvider import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.fragment_login_captcha.* +import org.matrix.android.sdk.internal.di.MoshiProvider import timber.log.Timber import java.net.URLDecoder import java.util.Formatter diff --git a/vector/src/main/java/im/vector/app/features/login/LoginGenericTextInputFormFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginGenericTextInputFormFragment.kt index 1b5502eb34..453575b91b 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginGenericTextInputFormFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginGenericTextInputFormFragment.kt @@ -32,11 +32,11 @@ import im.vector.app.R import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.extensions.isEmail import im.vector.app.core.extensions.setTextOrHide +import kotlinx.android.parcel.Parcelize +import kotlinx.android.synthetic.main.fragment_login_generic_text_input_form.* import org.matrix.android.sdk.api.auth.registration.RegisterThreePid import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.is401 -import kotlinx.android.parcel.Parcelize -import kotlinx.android.synthetic.main.fragment_login_generic_text_input_form.* import javax.inject.Inject enum class TextInputFormFragmentMode { diff --git a/vector/src/main/java/im/vector/app/features/login/LoginResetPasswordMailConfirmationFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginResetPasswordMailConfirmationFragment.kt index c12f10e191..577edf754b 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginResetPasswordMailConfirmationFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginResetPasswordMailConfirmationFragment.kt @@ -21,8 +21,8 @@ import butterknife.OnClick import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Success import im.vector.app.R -import org.matrix.android.sdk.api.failure.is401 import kotlinx.android.synthetic.main.fragment_login_reset_password_mail_confirmation.* +import org.matrix.android.sdk.api.failure.is401 import javax.inject.Inject /** diff --git a/vector/src/main/java/im/vector/app/features/login/LoginServerUrlFormFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginServerUrlFormFragment.kt index 4b9f528254..af959fecd4 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginServerUrlFormFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginServerUrlFormFragment.kt @@ -28,6 +28,8 @@ import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.utils.ensureProtocol import im.vector.app.core.utils.openUrlInChromeCustomTab import kotlinx.android.synthetic.main.fragment_login_server_url_form.* +import org.matrix.android.sdk.api.failure.Failure +import java.net.UnknownHostException import javax.inject.Inject /** @@ -115,7 +117,13 @@ class LoginServerUrlFormFragment @Inject constructor() : AbstractLoginFragment() } override fun onError(throwable: Throwable) { - loginServerUrlFormHomeServerUrlTil.error = errorFormatter.toHumanReadable(throwable) + loginServerUrlFormHomeServerUrlTil.error = if (throwable is Failure.NetworkConnection + && throwable.ioException is UnknownHostException) { + // Invalid homeserver? + getString(R.string.login_error_homeserver_not_found) + } else { + errorFormatter.toHumanReadable(throwable) + } } override fun updateWithState(state: LoginViewState) { diff --git a/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt index f986227961..81d6a78123 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt @@ -417,6 +417,18 @@ class LoginViewModel @AssistedInject constructor( private fun handleInitWith(action: LoginAction.InitWith) { loginConfig = action.loginConfig + + // If there is a pending email validation continue on this step + try { + if (registrationWizard?.isRegistrationStarted == true) { + currentThreePid?.let { + handle(LoginAction.PostViewEvent(LoginViewEvents.OnSendEmailSuccess(it))) + } + } + } catch (e: Throwable) { + // NOOP. API is designed to use wizards in a login/registration flow, + // but we need to check the state anyway. + } } private fun handleResetPassword(action: LoginAction.ResetPassword) { @@ -672,6 +684,7 @@ class LoginViewModel @AssistedInject constructor( private fun onSessionCreated(session: Session) { activeSessionHolder.setActiveSession(session) + authenticationService.reset() session.configureAndStart(applicationContext) setState { copy( @@ -740,7 +753,7 @@ class LoginViewModel @AssistedInject constructor( override fun onSuccess(data: LoginFlowResult) { when (data) { - is LoginFlowResult.Success -> { + is LoginFlowResult.Success -> { val loginMode = when { // SSO login is taken first data.supportedLoginTypes.contains(LoginFlowTypes.SSO) -> LoginMode.Sso diff --git a/vector/src/main/java/im/vector/app/features/login/LoginWaitForEmailFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginWaitForEmailFragment.kt index 7202ffc950..6f86f1c9d6 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginWaitForEmailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginWaitForEmailFragment.kt @@ -21,9 +21,9 @@ import android.os.Parcelable import android.view.View import com.airbnb.mvrx.args import im.vector.app.R -import org.matrix.android.sdk.api.failure.is401 import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.fragment_login_wait_for_email.* +import org.matrix.android.sdk.api.failure.is401 import javax.inject.Inject @Parcelize diff --git a/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt index 6d943fd99f..78a24abfd3 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt @@ -35,11 +35,11 @@ import im.vector.app.core.extensions.appendParamToUrl import im.vector.app.core.utils.AssetReader import im.vector.app.features.signout.soft.SoftLogoutAction import im.vector.app.features.signout.soft.SoftLogoutViewModel +import kotlinx.android.synthetic.main.fragment_login_web.* import org.matrix.android.sdk.api.auth.LOGIN_FALLBACK_PATH import org.matrix.android.sdk.api.auth.REGISTER_FALLBACK_PATH import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.internal.di.MoshiProvider -import kotlinx.android.synthetic.main.fragment_login_web.* import timber.log.Timber import java.net.URLDecoder import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/login/SignMode.kt b/vector/src/main/java/im/vector/app/features/login/SignMode.kt index 9933b92f27..1853a3e10f 100644 --- a/vector/src/main/java/im/vector/app/features/login/SignMode.kt +++ b/vector/src/main/java/im/vector/app/features/login/SignMode.kt @@ -18,10 +18,13 @@ package im.vector.app.features.login enum class SignMode { Unknown, + // Account creation SignUp, + // Login SignIn, + // Login directly with matrix Id SignInWithMatrixId } diff --git a/vector/src/main/java/im/vector/app/features/login/terms/LoginTermsFragment.kt b/vector/src/main/java/im/vector/app/features/login/terms/LoginTermsFragment.kt index 529713af37..624167dd91 100755 --- a/vector/src/main/java/im/vector/app/features/login/terms/LoginTermsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/terms/LoginTermsFragment.kt @@ -29,9 +29,9 @@ import im.vector.app.core.utils.openUrlInChromeCustomTab import im.vector.app.features.login.AbstractLoginFragment import im.vector.app.features.login.LoginAction import im.vector.app.features.login.LoginViewState -import org.matrix.android.sdk.internal.auth.registration.LocalizedFlowDataLoginTerms import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.fragment_login_terms.* +import org.matrix.android.sdk.internal.auth.registration.LocalizedFlowDataLoginTerms import javax.inject.Inject @Parcelize diff --git a/vector/src/main/java/im/vector/app/features/media/BigImageViewerActivity.kt b/vector/src/main/java/im/vector/app/features/media/BigImageViewerActivity.kt index c619b4aa92..81d6f1f996 100644 --- a/vector/src/main/java/im/vector/app/features/media/BigImageViewerActivity.kt +++ b/vector/src/main/java/im/vector/app/features/media/BigImageViewerActivity.kt @@ -30,6 +30,7 @@ import com.yalantis.ucrop.UCrop import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ScreenComponent +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider @@ -112,10 +113,10 @@ class BigImageViewerActivity : VectorBaseActivity() { private fun onAvatarTypeSelected(isCamera: Boolean) { if (isCamera) { if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, this, PERMISSION_REQUEST_CODE_LAUNCH_CAMERA)) { - avatarCameraUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(this) + avatarCameraUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(this, takePhotoActivityResultLauncher) } } else { - MultiPicker.get(MultiPicker.IMAGE).single().startWith(this) + MultiPicker.get(MultiPicker.IMAGE).single().startWith(pickImageActivityResultLauncher) } } @@ -127,33 +128,43 @@ class BigImageViewerActivity : VectorBaseActivity() { .start(this) } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (resultCode == Activity.RESULT_OK) { - when (requestCode) { - MultiPicker.REQUEST_CODE_TAKE_PHOTO -> { - avatarCameraUri?.let { uri -> - MultiPicker.get(MultiPicker.CAMERA) - .getTakenPhoto(this, requestCode, resultCode, uri) - ?.let { - onRoomAvatarSelected(it) - } - } - } - MultiPicker.REQUEST_CODE_PICK_IMAGE -> { - MultiPicker - .get(MultiPicker.IMAGE) - .getSelectedFiles(this, requestCode, resultCode, data) - .firstOrNull()?.let { - onRoomAvatarSelected(it) - } - } - UCrop.REQUEST_CROP -> data?.let { onAvatarCropped(UCrop.getOutput(it)) } + private val takePhotoActivityResultLauncher = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + avatarCameraUri?.let { uri -> + MultiPicker.get(MultiPicker.CAMERA) + .getTakenPhoto(this, uri) + ?.let { + onRoomAvatarSelected(it) + } } } + } + + private val pickImageActivityResultLauncher = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + MultiPicker + .get(MultiPicker.IMAGE) + .getSelectedFiles(this, activityResult.data) + .firstOrNull()?.let { + onRoomAvatarSelected(it) + } + } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + // TODO handle this one (Ucrop lib) + @Suppress("DEPRECATION") super.onActivityResult(requestCode, resultCode, data) + + if (resultCode == Activity.RESULT_OK) { + when (requestCode) { + UCrop.REQUEST_CROP -> data?.let { onAvatarCropped(UCrop.getOutput(it)) } + } + } } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (allGranted(grantResults)) { when (requestCode) { PERMISSION_REQUEST_CODE_LAUNCH_CAMERA -> onAvatarTypeSelected(true) @@ -174,7 +185,6 @@ class BigImageViewerActivity : VectorBaseActivity() { private const val EXTRA_TITLE = "EXTRA_TITLE" private const val EXTRA_IMAGE_URL = "EXTRA_IMAGE_URL" private const val EXTRA_CAN_EDIT_IMAGE = "EXTRA_CAN_EDIT_IMAGE" - const val REQUEST_CODE = 1000 fun newIntent(context: Context, title: String?, imageUrl: String, canEditImage: Boolean = false): Intent { return Intent(context, BigImageViewerActivity::class.java).apply { diff --git a/vector/src/main/java/im/vector/app/features/media/ImageContentRenderer.kt b/vector/src/main/java/im/vector/app/features/media/ImageContentRenderer.kt index 0b3b35e93e..4223cda229 100644 --- a/vector/src/main/java/im/vector/app/features/media/ImageContentRenderer.kt +++ b/vector/src/main/java/im/vector/app/features/media/ImageContentRenderer.kt @@ -33,13 +33,14 @@ import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.glide.GlideApp import im.vector.app.core.glide.GlideRequest +import im.vector.app.core.glide.GlideRequests import im.vector.app.core.ui.model.Size import im.vector.app.core.utils.DimensionConverter import im.vector.app.core.utils.isLocalFile import kotlinx.android.parcel.Parcelize +import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.content.ContentUrlResolver import org.matrix.android.sdk.internal.crypto.attachments.ElementToDecrypt -import org.matrix.android.sdk.api.extensions.tryOrNull import timber.log.Timber import java.io.File import javax.inject.Inject @@ -51,6 +52,7 @@ interface AttachmentData : Parcelable { val mimeType: String? val url: String? val elementToDecrypt: ElementToDecrypt? + // If true will load non mxc url, be careful to set it only for attachments sent by you val allowNonMxcUrls: Boolean } @@ -206,12 +208,14 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder: .into(imageView) } - private fun createGlideRequest(data: Data, mode: Mode, imageView: ImageView, size: Size): GlideRequest { + fun createGlideRequest(data: Data, mode: Mode, imageView: ImageView, size: Size): GlideRequest { + return createGlideRequest(data, mode, GlideApp.with(imageView), size) + } + + fun createGlideRequest(data: Data, mode: Mode, glideRequests: GlideRequests, size: Size = processSize(data, mode)): GlideRequest { return if (data.elementToDecrypt != null) { // Encrypted image - GlideApp - .with(imageView) - .load(data) + glideRequests.load(data) } else { // Clear image val contentUrlResolver = activeSessionHolder.getActiveSession().contentUrlResolver() @@ -223,15 +227,12 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder: // Fallback to base url ?: data.url.takeIf { it?.startsWith("content://") == true } - GlideApp - .with(imageView) + glideRequests .load(resolvedUrl) .apply { if (mode == Mode.THUMBNAIL) { error( - GlideApp - .with(imageView) - .load(resolveUrl(data)) + glideRequests.load(resolveUrl(data)) ) } } diff --git a/vector/src/main/java/im/vector/app/features/media/ImageMediaViewerActivity.kt b/vector/src/main/java/im/vector/app/features/media/ImageMediaViewerActivity.kt deleted file mode 100644 index fa7f397b8f..0000000000 --- a/vector/src/main/java/im/vector/app/features/media/ImageMediaViewerActivity.kt +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Copyright 2019 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.app.features.media - -import android.content.Context -import android.content.Intent -import android.graphics.drawable.Drawable -import android.os.Bundle -import android.view.MenuItem -import android.view.View -import android.view.ViewTreeObserver -import androidx.appcompat.widget.Toolbar -import androidx.core.net.toUri -import androidx.core.transition.addListener -import androidx.core.view.ViewCompat -import androidx.core.view.isInvisible -import androidx.core.view.isVisible -import androidx.transition.Transition -import com.bumptech.glide.load.DataSource -import com.bumptech.glide.load.engine.GlideException -import com.bumptech.glide.request.RequestListener -import com.bumptech.glide.request.target.Target -import com.github.piasy.biv.indicator.progresspie.ProgressPieIndicator -import com.github.piasy.biv.view.GlideImageViewFactory -import im.vector.app.R -import im.vector.app.core.di.ScreenComponent -import im.vector.app.core.glide.GlideApp -import im.vector.app.core.intent.getMimeTypeFromUri -import im.vector.app.core.platform.VectorBaseActivity -import im.vector.app.core.utils.shareMedia -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.file.FileService -import kotlinx.android.synthetic.main.activity_image_media_viewer.* -import timber.log.Timber -import java.io.File -import javax.inject.Inject - -class ImageMediaViewerActivity : VectorBaseActivity() { - - @Inject lateinit var session: Session - @Inject lateinit var imageContentRenderer: ImageContentRenderer - - private lateinit var mediaData: ImageContentRenderer.Data - - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(im.vector.app.R.layout.activity_image_media_viewer) - - if (intent.hasExtra(EXTRA_MEDIA_DATA)) { - mediaData = intent.getParcelableExtra(EXTRA_MEDIA_DATA)!! - } else { - finish() - } - - intent.extras?.getString(EXTRA_SHARED_TRANSITION_NAME)?.let { - ViewCompat.setTransitionName(imageTransitionView, it) - } - - if (mediaData.url.isNullOrEmpty()) { - supportFinishAfterTransition() - return - } - - configureToolbar(imageMediaViewerToolbar, mediaData) - - if (isFirstCreation() && addTransitionListener()) { - // Encrypted image - imageTransitionView.isVisible = true - imageMediaViewerImageView.isVisible = false - encryptedImageView.isVisible = false - // Postpone transaction a bit until thumbnail is loaded - supportPostponeEnterTransition() - - // We are not passing the exact same image that in the - imageContentRenderer.renderFitTarget(mediaData, ImageContentRenderer.Mode.THUMBNAIL, imageTransitionView) { - // Proceed with transaction - scheduleStartPostponedTransition(imageTransitionView) - } - } else { - imageTransitionView.isVisible = false - - if (mediaData.elementToDecrypt != null) { - // Encrypted image - imageMediaViewerImageView.isVisible = false - encryptedImageView.isVisible = true - - GlideApp - .with(this) - .load(mediaData) - .dontAnimate() - .into(encryptedImageView) - } else { - // Clear image - imageMediaViewerImageView.isVisible = true - encryptedImageView.isVisible = false - - imageMediaViewerImageView.setImageViewFactory(GlideImageViewFactory()) - imageMediaViewerImageView.setProgressIndicator(ProgressPieIndicator()) - imageContentRenderer.render(mediaData, imageMediaViewerImageView) - } - } - } - - override fun getMenuRes() = R.menu.vector_media_viewer - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - R.id.mediaViewerShareAction -> { - onShareActionClicked() - return true - } - } - return super.onOptionsItemSelected(item) - } - - private fun onShareActionClicked() { - session.fileService().downloadFile( - downloadMode = FileService.DownloadMode.FOR_EXTERNAL_SHARE, - id = mediaData.eventId, - fileName = mediaData.filename, - mimeType = mediaData.mimeType, - url = mediaData.url, - elementToDecrypt = mediaData.elementToDecrypt, - callback = object : MatrixCallback { - override fun onSuccess(data: File) { - shareMedia(this@ImageMediaViewerActivity, data, getMimeTypeFromUri(this@ImageMediaViewerActivity, data.toUri())) - } - } - ) - } - - private fun configureToolbar(toolbar: Toolbar, mediaData: ImageContentRenderer.Data) { - setSupportActionBar(toolbar) - supportActionBar?.apply { - title = mediaData.filename - setHomeButtonEnabled(true) - setDisplayHomeAsUpEnabled(true) - } - } - - override fun onBackPressed() { - // show again for exit animation - imageTransitionView.isVisible = true - super.onBackPressed() - } - - private fun scheduleStartPostponedTransition(sharedElement: View) { - sharedElement.viewTreeObserver.addOnPreDrawListener( - object : ViewTreeObserver.OnPreDrawListener { - override fun onPreDraw(): Boolean { - sharedElement.viewTreeObserver.removeOnPreDrawListener(this) - supportStartPostponedEnterTransition() - return true - } - }) - } - - /** - * Try and add a [Transition.TransitionListener] to the entering shared element - * [Transition]. We do this so that we can load the full-size image after the transition - * has completed. - * - * @return true if we were successful in adding a listener to the enter transition - */ - private fun addTransitionListener(): Boolean { - val transition = window.sharedElementEnterTransition - - if (transition != null) { - // There is an entering shared element transition so add a listener to it - transition.addListener( - onEnd = { - if (mediaData.elementToDecrypt != null) { - // Encrypted image - GlideApp - .with(this) - .load(mediaData) - .dontAnimate() - .listener(object : RequestListener { - override fun onLoadFailed(e: GlideException?, - model: Any?, - target: Target?, - isFirstResource: Boolean): Boolean { - // TODO ? - Timber.e("TRANSITION onLoadFailed") - imageMediaViewerImageView.isVisible = false - encryptedImageView.isVisible = true - return false - } - - override fun onResourceReady(resource: Drawable?, - model: Any?, - target: Target?, - dataSource: DataSource?, - isFirstResource: Boolean): Boolean { - Timber.e("TRANSITION onResourceReady") - imageTransitionView.isInvisible = true - imageMediaViewerImageView.isVisible = false - encryptedImageView.isVisible = true - return false - } - }) - .into(encryptedImageView) - } else { - imageTransitionView.isInvisible = true - // Clear image - imageMediaViewerImageView.isVisible = true - encryptedImageView.isVisible = false - - imageMediaViewerImageView.setImageViewFactory(GlideImageViewFactory()) - imageMediaViewerImageView.setProgressIndicator(ProgressPieIndicator()) - imageContentRenderer.render(mediaData, imageMediaViewerImageView) - } - }, - onCancel = { - // Something to do? - } - ) - return true - } - - // If we reach here then we have not added a listener - return false - } - - companion object { - - private const val EXTRA_MEDIA_DATA = "EXTRA_MEDIA_DATA" - private const val EXTRA_SHARED_TRANSITION_NAME = "EXTRA_SHARED_TRANSITION_NAME" - - fun newIntent(context: Context, mediaData: ImageContentRenderer.Data, shareTransitionName: String?): Intent { - return Intent(context, ImageMediaViewerActivity::class.java).apply { - putExtra(EXTRA_MEDIA_DATA, mediaData) - putExtra(EXTRA_SHARED_TRANSITION_NAME, shareTransitionName) - } - } - } -} diff --git a/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt b/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt index 74f4c7148f..9302be502d 100644 --- a/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt +++ b/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt @@ -74,7 +74,7 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), BaseAttachmen override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - Timber.i("onCreate Activity ${this.javaClass.simpleName}") + Timber.i("onCreate Activity ${javaClass.simpleName}") val vectorComponent = getVectorComponent() screenComponent = DaggerScreenComponent.factory().create(vectorComponent, this) val timeForInjection = measureTimeMillis { @@ -154,6 +154,16 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), BaseAttachmen window.navigationBarColor = ContextCompat.getColor(this, R.color.black_alpha) } + override fun onResume() { + super.onResume() + Timber.i("onResume Activity ${javaClass.simpleName}") + } + + override fun onPause() { + super.onPause() + Timber.i("onPause Activity ${javaClass.simpleName}") + } + private fun getOtherThemes() = ActivityOtherThemes.VectorAttachmentsPreview override fun shouldAnimateDismiss(): Boolean { diff --git a/vector/src/main/java/im/vector/app/features/media/VideoContentRenderer.kt b/vector/src/main/java/im/vector/app/features/media/VideoContentRenderer.kt index 4eb14592e0..f8cd09ce2f 100644 --- a/vector/src/main/java/im/vector/app/features/media/VideoContentRenderer.kt +++ b/vector/src/main/java/im/vector/app/features/media/VideoContentRenderer.kt @@ -25,10 +25,10 @@ import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.utils.isLocalFile +import kotlinx.android.parcel.Parcelize import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.file.FileService import org.matrix.android.sdk.internal.crypto.attachments.ElementToDecrypt -import kotlinx.android.parcel.Parcelize import timber.log.Timber import java.io.File import java.net.URLEncoder diff --git a/vector/src/main/java/im/vector/app/features/media/VideoMediaViewerActivity.kt b/vector/src/main/java/im/vector/app/features/media/VideoMediaViewerActivity.kt deleted file mode 100644 index 5bdda9b0be..0000000000 --- a/vector/src/main/java/im/vector/app/features/media/VideoMediaViewerActivity.kt +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2019 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.app.features.media - -import android.content.Context -import android.content.Intent -import android.os.Bundle -import android.view.MenuItem -import androidx.appcompat.widget.Toolbar -import androidx.core.net.toUri -import im.vector.app.R -import im.vector.app.core.di.ScreenComponent -import im.vector.app.core.intent.getMimeTypeFromUri -import im.vector.app.core.platform.VectorBaseActivity -import im.vector.app.core.utils.shareMedia -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.file.FileService -import kotlinx.android.synthetic.main.activity_video_media_viewer.* -import java.io.File -import javax.inject.Inject - -class VideoMediaViewerActivity : VectorBaseActivity() { - - @Inject lateinit var session: Session - @Inject lateinit var imageContentRenderer: ImageContentRenderer - @Inject lateinit var videoContentRenderer: VideoContentRenderer - - private lateinit var mediaData: VideoContentRenderer.Data - - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(im.vector.app.R.layout.activity_video_media_viewer) - - if (intent.hasExtra(EXTRA_MEDIA_DATA)) { - mediaData = intent.getParcelableExtra(EXTRA_MEDIA_DATA)!! - - configureToolbar(videoMediaViewerToolbar, mediaData) - imageContentRenderer.render(mediaData.thumbnailMediaData, ImageContentRenderer.Mode.FULL_SIZE, videoMediaViewerThumbnailView) - videoContentRenderer.render(mediaData, - videoMediaViewerThumbnailView, - videoMediaViewerLoading, - videoMediaViewerVideoView, - videoMediaViewerErrorView) - } else { - finish() - } - } - - override fun getMenuRes() = R.menu.vector_media_viewer - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - R.id.mediaViewerShareAction -> { - onShareActionClicked() - return true - } - } - return super.onOptionsItemSelected(item) - } - - private fun onShareActionClicked() { - session.fileService().downloadFile( - downloadMode = FileService.DownloadMode.FOR_EXTERNAL_SHARE, - id = mediaData.eventId, - fileName = mediaData.filename, - mimeType = mediaData.mimeType, - url = mediaData.url, - elementToDecrypt = mediaData.elementToDecrypt, - callback = object : MatrixCallback { - override fun onSuccess(data: File) { - shareMedia(this@VideoMediaViewerActivity, data, getMimeTypeFromUri(this@VideoMediaViewerActivity, data.toUri())) - } - } - ) - } - - private fun configureToolbar(toolbar: Toolbar, mediaData: VideoContentRenderer.Data) { - setSupportActionBar(toolbar) - supportActionBar?.apply { - title = mediaData.filename - setHomeButtonEnabled(true) - setDisplayHomeAsUpEnabled(true) - } - } - - companion object { - - private const val EXTRA_MEDIA_DATA = "EXTRA_MEDIA_DATA" - - fun newIntent(context: Context, mediaData: VideoContentRenderer.Data): Intent { - return Intent(context, VideoMediaViewerActivity::class.java).apply { - putExtra(EXTRA_MEDIA_DATA, mediaData) - } - } - } -} diff --git a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt index 5de8796c06..106d804cd3 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt @@ -21,11 +21,11 @@ import android.content.Context import android.content.Intent import android.view.View import android.view.Window +import androidx.activity.result.ActivityResultLauncher import androidx.core.app.ActivityOptionsCompat import androidx.core.app.TaskStackBuilder import androidx.core.util.Pair import androidx.core.view.ViewCompat -import androidx.fragment.app.Fragment import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.error.fatalError @@ -37,12 +37,14 @@ import im.vector.app.features.createdirect.CreateDirectRoomActivity import im.vector.app.features.crypto.keysbackup.settings.KeysBackupManageActivity import im.vector.app.features.crypto.keysbackup.setup.KeysBackupSetupActivity import im.vector.app.features.crypto.recover.BootstrapBottomSheet +import im.vector.app.features.crypto.recover.SetupMode import im.vector.app.features.crypto.verification.SupportedVerificationMethodsProvider import im.vector.app.features.crypto.verification.VerificationBottomSheet import im.vector.app.features.debug.DebugMenuActivity import im.vector.app.features.home.room.detail.RoomDetailActivity import im.vector.app.features.home.room.detail.RoomDetailArgs -import im.vector.app.features.home.room.detail.widget.WidgetRequestCodes +import im.vector.app.features.home.room.detail.search.SearchActivity +import im.vector.app.features.home.room.detail.search.SearchArgs import im.vector.app.features.home.room.filtered.FilteredRoomsActivity import im.vector.app.features.invite.InviteUsersToRoomActivity import im.vector.app.features.media.AttachmentData @@ -153,7 +155,10 @@ class DefaultNavigator @Inject constructor( override fun upgradeSessionSecurity(context: Context, initCrossSigningOnly: Boolean) { if (context is VectorBaseActivity) { - BootstrapBottomSheet.show(context.supportFragmentManager, initCrossSigningOnly, false) + BootstrapBottomSheet.show( + context.supportFragmentManager, + if (initCrossSigningOnly) SetupMode.CROSS_SIGNING_ONLY else SetupMode.NORMAL + ) } } @@ -226,13 +231,19 @@ class DefaultNavigator @Inject constructor( // if cross signing is enabled we should propose full 4S sessionHolder.getSafeActiveSession()?.let { session -> if (session.cryptoService().crossSigningService().canCrossSign() && context is VectorBaseActivity) { - BootstrapBottomSheet.show(context.supportFragmentManager, initCrossSigningOnly = false, forceReset4S = false) + BootstrapBottomSheet.show(context.supportFragmentManager, SetupMode.NORMAL) } else { context.startActivity(KeysBackupSetupActivity.intent(context, showManualExport)) } } } + override fun open4SSetup(context: Context, setupMode: SetupMode) { + if (context is VectorBaseActivity) { + BootstrapBottomSheet.show(context.supportFragmentManager, setupMode) + } + } + override fun openKeysBackupManager(context: Context) { context.startActivity(KeysBackupManageActivity.intent(context)) } @@ -254,21 +265,32 @@ class DefaultNavigator @Inject constructor( } } - override fun openTerms(fragment: Fragment, serviceType: TermsService.ServiceType, baseUrl: String, token: String?, requestCode: Int) { - val intent = ReviewTermsActivity.intent(fragment.requireContext(), serviceType, baseUrl, token) - fragment.startActivityForResult(intent, requestCode) + override fun openTerms(context: Context, + activityResultLauncher: ActivityResultLauncher, + serviceType: TermsService.ServiceType, + baseUrl: String, + token: String?) { + val intent = ReviewTermsActivity.intent(context, serviceType, baseUrl, token) + activityResultLauncher.launch(intent) } - override fun openStickerPicker(fragment: Fragment, roomId: String, widget: Widget, requestCode: Int) { + override fun openStickerPicker(context: Context, + activityResultLauncher: ActivityResultLauncher, + roomId: String, + widget: Widget) { val widgetArgs = widgetArgsBuilder.buildStickerPickerArgs(roomId, widget) - val intent = WidgetActivity.newIntent(fragment.requireContext(), widgetArgs) - fragment.startActivityForResult(intent, WidgetRequestCodes.STICKER_PICKER_REQUEST_CODE) + val intent = WidgetActivity.newIntent(context, widgetArgs) + activityResultLauncher.launch(intent) } - override fun openIntegrationManager(fragment: Fragment, roomId: String, integId: String?, screen: String?) { + override fun openIntegrationManager(context: Context, + activityResultLauncher: ActivityResultLauncher, + roomId: String, + integId: String?, + screen: String?) { val widgetArgs = widgetArgsBuilder.buildIntegrationManagerArgs(roomId, integId, screen) - val intent = WidgetActivity.newIntent(fragment.requireContext(), widgetArgs) - fragment.startActivityForResult(intent, WidgetRequestCodes.INTEGRATION_MANAGER_REQUEST_CODE) + val intent = WidgetActivity.newIntent(context, widgetArgs) + activityResultLauncher.launch(intent) } override fun openRoomWidget(context: Context, roomId: String, widget: Widget, options: Map?) { @@ -281,14 +303,11 @@ class DefaultNavigator @Inject constructor( } } - override fun openPinCode(fragment: Fragment, pinMode: PinMode, requestCode: Int) { - val intent = PinActivity.newIntent(fragment.requireContext(), PinArgs(pinMode)) - fragment.startActivityForResult(intent, requestCode) - } - - override fun openPinCode(activity: Activity, pinMode: PinMode, requestCode: Int) { - val intent = PinActivity.newIntent(activity, PinArgs(pinMode)) - activity.startActivityForResult(intent, requestCode) + override fun openPinCode(context: Context, + activityResultLauncher: ActivityResultLauncher, + pinMode: PinMode) { + val intent = PinActivity.newIntent(context, PinArgs(pinMode)) + activityResultLauncher.launch(intent) } override fun openMediaViewer(activity: Activity, @@ -319,6 +338,11 @@ class DefaultNavigator @Inject constructor( } } + override fun openSearch(context: Context, roomId: String) { + val intent = SearchActivity.newIntent(context, SearchArgs(roomId)) + context.startActivity(intent) + } + private fun startActivity(context: Context, intent: Intent, buildTask: Boolean) { if (buildTask) { val stackBuilder = TaskStackBuilder.create(context) diff --git a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt index ed710a124f..1d01a5e4f0 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt @@ -18,17 +18,16 @@ package im.vector.app.features.navigation import android.app.Activity import android.content.Context +import android.content.Intent import android.view.View +import androidx.activity.result.ActivityResultLauncher import androidx.core.util.Pair -import androidx.fragment.app.Fragment -import im.vector.app.features.home.room.detail.widget.WidgetRequestCodes +import im.vector.app.features.crypto.recover.SetupMode import im.vector.app.features.media.AttachmentData -import im.vector.app.features.pin.PinActivity import im.vector.app.features.pin.PinMode import im.vector.app.features.roomdirectory.roompreview.RoomPreviewData import im.vector.app.features.settings.VectorSettingsActivity import im.vector.app.features.share.SharedData -import im.vector.app.features.terms.ReviewTermsActivity import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom import org.matrix.android.sdk.api.session.room.model.thirdparty.RoomDirectoryData import org.matrix.android.sdk.api.session.terms.TermsService @@ -71,6 +70,8 @@ interface Navigator { fun openKeysBackupSetup(context: Context, showManualExport: Boolean) + fun open4SSetup(context: Context, setupMode: SetupMode) + fun openKeysBackupManager(context: Context) fun openGroupDetail(groupId: String, context: Context, buildTask: Boolean = false) @@ -81,22 +82,26 @@ interface Navigator { fun openBigImageViewer(activity: Activity, sharedElement: View?, matrixItem: MatrixItem) - fun openPinCode(fragment: Fragment, pinMode: PinMode, requestCode: Int = PinActivity.PIN_REQUEST_CODE) + fun openPinCode(context: Context, + activityResultLauncher: ActivityResultLauncher, + pinMode: PinMode) - fun openPinCode(activity: Activity, pinMode: PinMode, requestCode: Int = PinActivity.PIN_REQUEST_CODE) - - fun openTerms(fragment: Fragment, + fun openTerms(context: Context, + activityResultLauncher: ActivityResultLauncher, serviceType: TermsService.ServiceType, baseUrl: String, - token: String?, - requestCode: Int = ReviewTermsActivity.TERMS_REQUEST_CODE) + token: String?) - fun openStickerPicker(fragment: Fragment, + fun openStickerPicker(context: Context, + activityResultLauncher: ActivityResultLauncher, roomId: String, - widget: Widget, - requestCode: Int = WidgetRequestCodes.STICKER_PICKER_REQUEST_CODE) + widget: Widget) - fun openIntegrationManager(fragment: Fragment, roomId: String, integId: String?, screen: String?) + fun openIntegrationManager(context: Context, + activityResultLauncher: ActivityResultLauncher, + roomId: String, + integId: String?, + screen: String?) fun openRoomWidget(context: Context, roomId: String, widget: Widget, options: Map? = null) @@ -106,4 +111,6 @@ interface Navigator { view: View, inMemory: List = emptyList(), options: ((MutableList>) -> Unit)?) + + fun openSearch(context: Context, roomId: String) } diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEvent.kt b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEvent.kt index 2be5650904..a4f099b905 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEvent.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEvent.kt @@ -29,12 +29,15 @@ interface NotifiableEvent : Serializable { val description: String? val type: String? val timestamp: Long + // NotificationCompat.VISIBILITY_PUBLIC , VISIBILITY_PRIVATE , VISIBILITY_SECRET var lockScreenVisibility: Int + // Compat: Only for android <7, for newer version the sound is defined in the channel var soundName: String? var hasBeenDisplayed: Boolean var isRedacted: Boolean + // Used to know if event should be replaced with the one coming from eventstream var isPushGatewayEvent: Boolean } diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventResolver.kt b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventResolver.kt index 649dbd89f9..0740295191 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventResolver.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventResolver.kt @@ -91,7 +91,7 @@ class NotifiableEventResolver @Inject constructor(private val stringProvider: St if (room == null) { Timber.e("## Unable to resolve room for eventId [$event]") // Ok room is not known in store, but we can still display something - val body = displayableEventFormatter.format(event, false) + val body = displayableEventFormatter.format(event, false, null) val roomName = stringProvider.getString(R.string.notification_unknown_room_name) val senderDisplayName = event.senderInfo.disambiguatedDisplayName @@ -124,7 +124,7 @@ class NotifiableEventResolver @Inject constructor(private val stringProvider: St } } - val body = displayableEventFormatter.format(event, false).toString() + val body = displayableEventFormatter.format(event, false, room.roomSummary()).toString() val roomName = room.roomSummary()?.displayName ?: "" val senderDisplayName = event.senderInfo.disambiguatedDisplayName @@ -165,7 +165,7 @@ class NotifiableEventResolver @Inject constructor(private val stringProvider: St val roomId = event.roomId ?: return null val dName = event.senderId?.let { session.getUser(it)?.displayName } if (Membership.INVITE == content.membership) { - val body = noticeEventFormatter.format(event, dName) + val body = noticeEventFormatter.format(event, dName, session.getRoomSummary(roomId)) ?: stringProvider.getString(R.string.notification_new_invitation) return InviteNotifiableEvent( session.myUserId, diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt index 26f00fcef9..7f3c0a5beb 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt @@ -245,8 +245,8 @@ class NotificationDrawerManager @Inject constructor(private val context: Context roomEvents.add(event) } } - is InviteNotifiableEvent -> invitationEvents.add(event) - is SimpleNotifiableEvent -> simpleEvents.add(event) + is InviteNotifiableEvent -> invitationEvents.add(event) + is SimpleNotifiableEvent -> simpleEvents.add(event) else -> Timber.w("Type not handled") } } @@ -590,6 +590,10 @@ class NotificationDrawerManager @Inject constructor(private val context: Context } } + fun displayDiagnosticNotification() { + notificationUtils.displayDiagnosticNotification() + } + companion object { private const val SUMMARY_NOTIFICATION_ID = 0 private const val ROOM_MESSAGES_NOTIFICATION_ID = 1 diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt index 1554d086d0..5b46922f73 100755 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt @@ -27,8 +27,10 @@ import android.app.PendingIntent import android.content.Context import android.content.Intent import android.graphics.Bitmap +import android.graphics.Canvas import android.net.Uri import android.os.Build +import androidx.annotation.DrawableRes import androidx.annotation.StringRes import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat @@ -36,6 +38,7 @@ import androidx.core.app.RemoteInput import androidx.core.app.TaskStackBuilder import androidx.core.content.ContextCompat import androidx.core.content.getSystemService +import androidx.core.content.res.ResourcesCompat import androidx.core.graphics.drawable.IconCompat import androidx.fragment.app.Fragment import im.vector.app.BuildConfig @@ -47,8 +50,8 @@ import im.vector.app.features.call.service.CallHeadsUpActionReceiver import im.vector.app.features.home.HomeActivity import im.vector.app.features.home.room.detail.RoomDetailActivity import im.vector.app.features.home.room.detail.RoomDetailArgs -import im.vector.app.features.pin.PinLocker import im.vector.app.features.settings.VectorPreferences +import im.vector.app.features.settings.troubleshoot.TestNotificationReceiver import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @@ -61,7 +64,6 @@ import kotlin.random.Random @Singleton class NotificationUtils @Inject constructor(private val context: Context, private val stringProvider: StringProvider, - private val pinLocker: PinLocker, private val vectorPreferences: VectorPreferences) { companion object { @@ -89,6 +91,8 @@ class NotificationUtils @Inject constructor(private val context: Context, const val DISMISS_SUMMARY_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.DISMISS_SUMMARY_ACTION" const val DISMISS_ROOM_NOTIF_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.DISMISS_ROOM_NOTIF_ACTION" private const val TAP_TO_VIEW_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.TAP_TO_VIEW_ACTION" + const val DIAGNOSTIC_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.DIAGNOSTIC" + const val PUSH_ACTION = "${BuildConfig.APPLICATION_ID}.PUSH" /* ========================================================================================== * IDs for channels @@ -848,6 +852,43 @@ class NotificationUtils @Inject constructor(private val context: Context, } } + fun displayDiagnosticNotification() { + val testActionIntent = Intent(context, TestNotificationReceiver::class.java) + testActionIntent.action = DIAGNOSTIC_ACTION + val testPendingIntent = PendingIntent.getBroadcast( + context, + 0, + testActionIntent, + PendingIntent.FLAG_UPDATE_CURRENT + ) + + notificationManager.notify( + "DIAGNOSTIC", + 888, + NotificationCompat.Builder(context, NOISY_NOTIFICATION_CHANNEL_ID) + .setContentTitle(stringProvider.getString(R.string.app_name)) + .setContentText(stringProvider.getString(R.string.settings_troubleshoot_test_push_notification_content)) + .setSmallIcon(R.drawable.ic_status_bar) + .setLargeIcon(getBitmap(context, R.drawable.element_logo_green)) + .setColor(ContextCompat.getColor(context, R.color.notification_accent_color)) + .setPriority(NotificationCompat.PRIORITY_MAX) + .setCategory(NotificationCompat.CATEGORY_STATUS) + .setAutoCancel(true) + .setContentIntent(testPendingIntent) + .build() + ) + } + + private fun getBitmap(context: Context, @DrawableRes drawableRes: Int): Bitmap? { + val drawable = ResourcesCompat.getDrawable(context.resources, drawableRes, null) ?: return null + val canvas = Canvas() + val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888) + canvas.setBitmap(bitmap) + drawable.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight) + drawable.draw(canvas) + return bitmap + } + /** * Return true it the user has enabled the do not disturb mode */ diff --git a/vector/src/main/java/im/vector/app/features/notifications/RoomEventGroupInfo.kt b/vector/src/main/java/im/vector/app/features/notifications/RoomEventGroupInfo.kt index b9363864e7..64e40ed748 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/RoomEventGroupInfo.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/RoomEventGroupInfo.kt @@ -26,6 +26,7 @@ data class RoomEventGroupInfo( ) { // An event in the list has not yet been display var hasNewEvent: Boolean = false + // true if at least one on the not yet displayed event is noisy var shouldBing: Boolean = false var customSound: String? = null diff --git a/vector/src/main/java/im/vector/app/features/pin/PinActivity.kt b/vector/src/main/java/im/vector/app/features/pin/PinActivity.kt index 0216ec512e..2e6a0b6c08 100644 --- a/vector/src/main/java/im/vector/app/features/pin/PinActivity.kt +++ b/vector/src/main/java/im/vector/app/features/pin/PinActivity.kt @@ -28,8 +28,6 @@ import im.vector.app.core.platform.VectorBaseActivity class PinActivity : VectorBaseActivity(), ToolbarConfigurable, UnlockedActivity { companion object { - const val PIN_REQUEST_CODE = 17890 - fun newIntent(context: Context, args: PinArgs): Intent { return Intent(context, PinActivity::class.java).apply { putExtra(MvRx.KEY_ARG, args) diff --git a/vector/src/main/java/im/vector/app/features/pin/PinCodeStore.kt b/vector/src/main/java/im/vector/app/features/pin/PinCodeStore.kt index 630b165214..fb7c6897e2 100644 --- a/vector/src/main/java/im/vector/app/features/pin/PinCodeStore.kt +++ b/vector/src/main/java/im/vector/app/features/pin/PinCodeStore.kt @@ -21,9 +21,9 @@ import androidx.core.content.edit import com.beautycoder.pflockscreen.security.PFResult import com.beautycoder.pflockscreen.security.PFSecurityManager import com.beautycoder.pflockscreen.security.callbacks.PFPinCodeHelperCallback -import org.matrix.android.sdk.api.extensions.orFalse import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import org.matrix.android.sdk.api.extensions.orFalse import javax.inject.Inject import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine diff --git a/vector/src/main/java/im/vector/app/features/pin/PinLocker.kt b/vector/src/main/java/im/vector/app/features/pin/PinLocker.kt index 3fc4152ee6..adc618d82e 100644 --- a/vector/src/main/java/im/vector/app/features/pin/PinLocker.kt +++ b/vector/src/main/java/im/vector/app/features/pin/PinLocker.kt @@ -81,6 +81,11 @@ class PinLocker @Inject constructor( computeState() } + fun screenIsOff() { + shouldBeLocked = true + computeState() + } + @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun entersForeground() { val timeElapsedSinceBackground = SystemClock.elapsedRealtime() - entersBackgroundTs diff --git a/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt b/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt index 593527448b..665eb93428 100644 --- a/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt +++ b/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt @@ -27,6 +27,7 @@ import com.tapadoo.alerter.OnHideAlertListener import dagger.Lazy import im.vector.app.R import im.vector.app.core.platform.VectorBaseActivity +import im.vector.app.core.utils.isAnimationDisabled import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.pin.PinActivity import im.vector.app.features.themes.ThemeUtils @@ -77,6 +78,21 @@ class PopupAlertManager @Inject constructor(private val avatarRenderer: Lazy) { + activityResultLauncher.launch(Intent(activity, QrCodeScannerActivity::class.java)) } fun getResultText(data: Intent?): String? { diff --git a/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt b/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt index 7bbc001f0a..e7382a17e9 100755 --- a/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt +++ b/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt @@ -1,6 +1,4 @@ /* - * Copyright 2016 OpenMarket Ltd - * Copyright 2017 Vector Creations Ltd * Copyright 2018 New Vector Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -38,7 +36,6 @@ import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.locale.SystemLocaleProvider import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.version.VersionProvider -import org.matrix.android.sdk.api.Matrix import okhttp3.Call import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.OkHttpClient @@ -47,6 +44,7 @@ import okhttp3.RequestBody.Companion.asRequestBody import okhttp3.Response import org.json.JSONException import org.json.JSONObject +import org.matrix.android.sdk.api.Matrix import timber.log.Timber import java.io.File import java.io.IOException diff --git a/vector/src/main/java/im/vector/app/features/rageshake/BugReporterMultipartBody.java b/vector/src/main/java/im/vector/app/features/rageshake/BugReporterMultipartBody.java index 878b9f7171..7a29bf12e4 100755 --- a/vector/src/main/java/im/vector/app/features/rageshake/BugReporterMultipartBody.java +++ b/vector/src/main/java/im/vector/app/features/rageshake/BugReporterMultipartBody.java @@ -1,5 +1,4 @@ /* - * Copyright 2017 Vector Creations Ltd * Copyright 2018 New Vector Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/vector/src/main/java/im/vector/app/features/reactions/EmojiChooserFragment.kt b/vector/src/main/java/im/vector/app/features/reactions/EmojiChooserFragment.kt index 447525f72f..af40aa970d 100644 --- a/vector/src/main/java/im/vector/app/features/reactions/EmojiChooserFragment.kt +++ b/vector/src/main/java/im/vector/app/features/reactions/EmojiChooserFragment.kt @@ -17,7 +17,6 @@ package im.vector.app.features.reactions import android.os.Bundle import android.view.View -import androidx.lifecycle.observe import im.vector.app.R import im.vector.app.core.extensions.cleanup import im.vector.app.core.platform.VectorBaseFragment diff --git a/vector/src/main/java/im/vector/app/features/reactions/EmojiReactionPickerActivity.kt b/vector/src/main/java/im/vector/app/features/reactions/EmojiReactionPickerActivity.kt index 4fe74c2bcc..25cb90d3a4 100644 --- a/vector/src/main/java/im/vector/app/features/reactions/EmojiReactionPickerActivity.kt +++ b/vector/src/main/java/im/vector/app/features/reactions/EmojiReactionPickerActivity.kt @@ -212,10 +212,8 @@ class EmojiReactionPickerActivity : VectorBaseActivity(), return intent } - fun getOutput(data: Intent): Pair? { - val eventId = data.getStringExtra(EXTRA_EVENT_ID) ?: return null - val reaction = data.getStringExtra(EXTRA_REACTION_RESULT) ?: return null - return eventId to reaction - } + fun getOutputEventId(data: Intent?): String? = data?.getStringExtra(EXTRA_EVENT_ID) + + fun getOutputReaction(data: Intent?): String? = data?.getStringExtra(EXTRA_REACTION_RESULT) } } diff --git a/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt b/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt index 783d4d45a2..868a4e4dd6 100644 --- a/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt @@ -27,6 +27,8 @@ import im.vector.app.R import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider +import io.reactivex.Observable +import io.reactivex.schedulers.Schedulers import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.EventType @@ -36,8 +38,6 @@ import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.rx.rx import org.matrix.android.sdk.rx.unwrap -import io.reactivex.Observable -import io.reactivex.schedulers.Schedulers /** * This ViewModel observe a room summary and notify when the room is left diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/JoinState.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/JoinState.kt index dfcbb470cd..12c264aa65 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/JoinState.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/JoinState.kt @@ -23,6 +23,7 @@ enum class JoinState { NOT_JOINED, JOINING, JOINING_ERROR, + // Room is joined and this is confirmed by the sync JOINED } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt index 963f842304..4bfd60394a 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt @@ -31,8 +31,8 @@ import im.vector.app.features.roomdirectory.RoomDirectoryAction import im.vector.app.features.roomdirectory.RoomDirectorySharedAction import im.vector.app.features.roomdirectory.RoomDirectorySharedActionViewModel import im.vector.app.features.roomdirectory.RoomDirectoryViewModel -import org.matrix.android.sdk.api.session.room.model.thirdparty.RoomDirectoryData import kotlinx.android.synthetic.main.fragment_room_directory_picker.* +import org.matrix.android.sdk.api.session.room.model.thirdparty.RoomDirectoryData import timber.log.Timber import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt index ca9ecb62ef..a3ffd80ade 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt @@ -172,13 +172,16 @@ class RoomMemberProfileController @Inject constructor( val membership = state.asyncMembership() ?: return buildProfileSection(stringProvider.getString(R.string.room_profile_section_more)) - buildProfileAction( - id = "read_receipt", - editable = false, - title = stringProvider.getString(R.string.room_member_jump_to_read_receipt), - dividerColor = dividerColor, - action = { callback?.onJumpToReadReceiptClicked() } - ) + + if (state.hasReadReceipt) { + buildProfileAction( + id = "read_receipt", + editable = false, + title = stringProvider.getString(R.string.room_member_jump_to_read_receipt), + dividerColor = dividerColor, + action = { callback?.onJumpToReadReceiptClicked() } + ) + } val ignoreActionTitle = state.buildIgnoreActionTitle() diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt index cda2d83e3d..2f5b2d5387 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -43,6 +43,8 @@ import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.startSharePlainTextIntent import im.vector.app.features.crypto.verification.VerificationBottomSheet import im.vector.app.features.home.AvatarRenderer +import im.vector.app.features.home.room.detail.RoomDetailPendingAction +import im.vector.app.features.home.room.detail.RoomDetailPendingActionStore import im.vector.app.features.roommemberprofile.devices.DeviceListBottomSheet import im.vector.app.features.roommemberprofile.powerlevel.EditPowerLevelDialogs import kotlinx.android.parcel.Parcelize @@ -61,7 +63,8 @@ data class RoomMemberProfileArgs( class RoomMemberProfileFragment @Inject constructor( val viewModelFactory: RoomMemberProfileViewModel.Factory, private val roomMemberProfileController: RoomMemberProfileController, - private val avatarRenderer: AvatarRenderer + private val avatarRenderer: AvatarRenderer, + private val roomDetailPendingActionStore: RoomDetailPendingActionStore ) : VectorBaseFragment(), RoomMemberProfileController.Callback { private val fragmentArgs: RoomMemberProfileArgs by args() @@ -276,15 +279,22 @@ class RoomMemberProfileFragment @Inject constructor( } override fun onJumpToReadReceiptClicked() { - vectorBaseActivity.notImplemented("Jump to read receipts") + roomDetailPendingActionStore.data = RoomDetailPendingAction.JumpToReadReceipt(fragmentArgs.userId) + vectorBaseActivity.finish() } override fun onMentionClicked() { - vectorBaseActivity.notImplemented("Mention") + roomDetailPendingActionStore.data = RoomDetailPendingAction.MentionUser(fragmentArgs.userId) + vectorBaseActivity.finish() } private fun handleShareRoomMemberProfile(permalink: String) { - startSharePlainTextIntent(fragment = this, chooserTitle = null, text = permalink) + startSharePlainTextIntent( + fragment = this, + activityResultLauncher = null, + chooserTitle = null, + text = permalink + ) } private fun onAvatarClicked(view: View, userMatrixItem: MatrixItem) { diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt index f2c85c96c7..78562ea351 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -85,7 +85,8 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v setState { copy( isMine = session.myUserId == this.userId, - userMatrixItem = room?.getRoomMember(initialState.userId)?.toMatrixItem()?.let { Success(it) } ?: Uninitialized + userMatrixItem = room?.getRoomMember(initialState.userId)?.toMatrixItem()?.let { Success(it) } ?: Uninitialized, + hasReadReceipt = room?.getUserReadReceipt(initialState.userId) != null ) } observeIgnoredState() diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewState.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewState.kt index dec003b37d..f943a5cf08 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewState.kt @@ -39,6 +39,7 @@ data class RoomMemberProfileViewState( val allDevicesAreTrusted: Boolean = false, val allDevicesAreCrossSignedTrusted: Boolean = false, val asyncMembership: Async = Uninitialized, + val hasReadReceipt: Boolean = false, val actionPermissions: ActionPermissions = ActionPermissions() ) : MvRxState { diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheet.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheet.kt index a8c6842c08..323e25a022 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheet.kt @@ -18,7 +18,9 @@ package im.vector.app.features.roommemberprofile.devices import android.content.DialogInterface import android.os.Bundle +import android.os.Parcelable import android.view.KeyEvent +import android.view.View import androidx.fragment.app.Fragment import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.fragmentViewModel @@ -29,6 +31,7 @@ import im.vector.app.core.extensions.commitTransaction import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.features.crypto.verification.VerificationBottomSheet +import kotlinx.android.parcel.Parcelize import javax.inject.Inject import kotlin.reflect.KClass @@ -44,8 +47,8 @@ class DeviceListBottomSheet : VectorBaseBottomSheetDialogFragment() { injector.inject(this) } - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) viewModel.observeViewEvents { when (it) { is DeviceListBottomSheetViewEvents.Verify -> { @@ -104,10 +107,16 @@ class DeviceListBottomSheet : VectorBaseBottomSheetDialogFragment() { } } + @Parcelize + data class Args( + val userId: String, + val allowDeviceAction: Boolean + ) : Parcelable + companion object { - fun newInstance(userId: String): DeviceListBottomSheet { + fun newInstance(userId: String, allowDeviceAction: Boolean = true): DeviceListBottomSheet { val args = Bundle() - args.putString(MvRx.KEY_ARG, userId) + args.putParcelable(MvRx.KEY_ARG, Args(userId, allowDeviceAction)) return DeviceListBottomSheet().apply { arguments = args } } } diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt index db97399f1b..0f1ac3bfea 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt @@ -44,24 +44,24 @@ data class DeviceListViewState( ) : MvRxState class DeviceListBottomSheetViewModel @AssistedInject constructor(@Assisted private val initialState: DeviceListViewState, - @Assisted private val userId: String, + @Assisted private val args: DeviceListBottomSheet.Args, private val session: Session) : VectorViewModel(initialState) { @AssistedInject.Factory interface Factory { - fun create(initialState: DeviceListViewState, userId: String): DeviceListBottomSheetViewModel + fun create(initialState: DeviceListViewState, args: DeviceListBottomSheet.Args): DeviceListBottomSheetViewModel } init { - session.rx().liveUserCryptoDevices(userId) + session.rx().liveUserCryptoDevices(args.userId) .execute { copy(cryptoDevices = it).also { refreshSelectedId() } } - session.rx().liveCrossSigningInfo(userId) + session.rx().liveCrossSigningInfo(args.userId) .execute { copy(memberCrossSigningKey = it.invoke()?.getOrNull()) } @@ -88,6 +88,7 @@ class DeviceListBottomSheetViewModel @AssistedInject constructor(@Assisted priva } private fun selectDevice(action: DeviceListAction.SelectDevice) { + if (!args.allowDeviceAction) return setState { copy(selectedDevice = action.device) } @@ -100,8 +101,9 @@ class DeviceListBottomSheetViewModel @AssistedInject constructor(@Assisted priva } private fun manuallyVerify(action: DeviceListAction.ManuallyVerify) { - session.cryptoService().verificationService().beginKeyVerification(VerificationMethod.SAS, userId, action.deviceId, null)?.let { txID -> - _viewEvents.post(DeviceListBottomSheetViewEvents.Verify(userId, txID)) + if (!args.allowDeviceAction) return + session.cryptoService().verificationService().beginKeyVerification(VerificationMethod.SAS, args.userId, action.deviceId, null)?.let { txID -> + _viewEvents.post(DeviceListBottomSheetViewEvents.Verify(args.userId, txID)) } } @@ -109,12 +111,12 @@ class DeviceListBottomSheetViewModel @AssistedInject constructor(@Assisted priva @JvmStatic override fun create(viewModelContext: ViewModelContext, state: DeviceListViewState): DeviceListBottomSheetViewModel? { val fragment: DeviceListBottomSheet = (viewModelContext as FragmentViewModelContext).fragment() - val userId = viewModelContext.args() - return fragment.viewModelFactory.create(state, userId) + val args = viewModelContext.args() + return fragment.viewModelFactory.create(state, args) } override fun initialState(viewModelContext: ViewModelContext): DeviceListViewState? { - val userId = viewModelContext.args() + val userId = viewModelContext.args().userId val session = (viewModelContext.activity as HasScreenInjector).injector().activeSessionHolder().getActiveSession() return session.getUser(userId)?.toMatrixItem()?.let { DeviceListViewState( diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListEpoxyController.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListEpoxyController.kt index f68965f42c..035df8caa2 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListEpoxyController.kt @@ -33,8 +33,8 @@ import im.vector.app.core.ui.list.genericItem import im.vector.app.core.ui.list.genericItemWithValue import im.vector.app.core.utils.DimensionConverter import im.vector.app.features.settings.VectorPreferences -import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import me.gujun.android.span.span +import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import javax.inject.Inject class DeviceListEpoxyController @Inject constructor(private val stringProvider: StringProvider, diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListFragment.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListFragment.kt index 9f90ffbb84..e9fdd15af4 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListFragment.kt @@ -17,6 +17,7 @@ package im.vector.app.features.roommemberprofile.devices import android.os.Bundle +import android.view.View import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import com.airbnb.mvrx.parentFragmentViewModel @@ -41,8 +42,8 @@ class DeviceListFragment @Inject constructor( @BindView(R.id.bottomSheetRecyclerView) lateinit var recyclerView: RecyclerView - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) recyclerView.setPadding(0, dimensionConverter.dpToPx(16), 0, dimensionConverter.dpToPx(16)) recyclerView.configureWith( epoxyController, diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceTrustInfoActionFragment.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceTrustInfoActionFragment.kt index decc22c979..e31f3172e9 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceTrustInfoActionFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceTrustInfoActionFragment.kt @@ -17,6 +17,7 @@ package im.vector.app.features.roommemberprofile.devices import android.os.Bundle +import android.view.View import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import com.airbnb.mvrx.parentFragmentViewModel @@ -41,8 +42,8 @@ class DeviceTrustInfoActionFragment @Inject constructor( @BindView(R.id.bottomSheetRecyclerView) lateinit var recyclerView: RecyclerView - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) recyclerView.setPadding(0, dimensionConverter.dpToPx(16), 0, dimensionConverter.dpToPx(16)) recyclerView.configureWith( epoxyController, diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceTrustInfoEpoxyController.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceTrustInfoEpoxyController.kt index fad1a35d6a..200d268b27 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceTrustInfoEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceTrustInfoEpoxyController.kt @@ -27,8 +27,8 @@ import im.vector.app.core.ui.list.genericItemWithValue import im.vector.app.core.utils.DimensionConverter import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem import im.vector.app.features.settings.VectorPreferences -import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import me.gujun.android.span.span +import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import javax.inject.Inject class DeviceTrustInfoEpoxyController @Inject constructor(private val stringProvider: StringProvider, diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/powerlevel/EditPowerLevelDialogs.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/powerlevel/EditPowerLevelDialogs.kt index b871a3690e..eee0cc4081 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/powerlevel/EditPowerLevelDialogs.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/powerlevel/EditPowerLevelDialogs.kt @@ -23,8 +23,8 @@ import androidx.appcompat.app.AlertDialog import androidx.core.view.isVisible import im.vector.app.R import im.vector.app.core.extensions.hideKeyboard -import org.matrix.android.sdk.api.session.room.powerlevels.Role import kotlinx.android.synthetic.main.dialog_edit_power_level.view.* +import org.matrix.android.sdk.api.session.room.powerlevels.Role object EditPowerLevelDialogs { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileAction.kt index 8061ad6bfc..ece221d884 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileAction.kt @@ -18,12 +18,13 @@ package im.vector.app.features.roomprofile import android.net.Uri -import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState import im.vector.app.core.platform.VectorViewModelAction +import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState -sealed class RoomProfileAction: VectorViewModelAction { - object LeaveRoom: RoomProfileAction() +sealed class RoomProfileAction : VectorViewModelAction { + object LeaveRoom : RoomProfileAction() data class ChangeRoomNotificationState(val notificationState: RoomNotificationState) : RoomProfileAction() data class ChangeRoomAvatar(val uri: Uri, val fileName: String?) : RoomProfileAction() object ShareRoomProfile : RoomProfileAction() + object CreateShortcut : RoomProfileAction() } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt index de2bf8e749..b3e5966083 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt @@ -29,6 +29,7 @@ import im.vector.app.core.extensions.addFragment import im.vector.app.core.extensions.addFragmentToBackstack import im.vector.app.core.platform.ToolbarConfigurable import im.vector.app.core.platform.VectorBaseActivity +import im.vector.app.features.home.room.detail.RoomDetailPendingActionStore import im.vector.app.features.room.RequireActiveMembershipViewEvents import im.vector.app.features.room.RequireActiveMembershipViewModel import im.vector.app.features.room.RequireActiveMembershipViewState @@ -61,6 +62,9 @@ open class RoomProfileActivity : @Inject lateinit var requireActiveMembershipViewModelFactory: RequireActiveMembershipViewModel.Factory + @Inject + lateinit var roomDetailPendingActionStore: RoomDetailPendingActionStore + override fun create(initialState: RequireActiveMembershipViewState): RequireActiveMembershipViewModel { return requireActiveMembershipViewModelFactory.create(initialState) } @@ -101,6 +105,13 @@ open class RoomProfileActivity : addFragment(R.id.simpleFragmentContainer, RoomProfileFragment::class.java, roomProfileArgs) } + override fun onResume() { + super.onResume() + if (roomDetailPendingActionStore.data != null) { + finish() + } + } + private fun handleRoomLeft(roomLeft: RequireActiveMembershipViewEvents.RoomLeft) { if (roomLeft.leftMessage != null) { Toast.makeText(this, roomLeft.leftMessage, Toast.LENGTH_LONG).show() diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt index 815478b46b..7dc744da31 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt @@ -18,19 +18,22 @@ package im.vector.app.features.roomprofile import com.airbnb.epoxy.TypedEpoxyController -import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel import im.vector.app.R +import im.vector.app.core.epoxy.expandableTextItem import im.vector.app.core.epoxy.profiles.buildProfileAction import im.vector.app.core.epoxy.profiles.buildProfileSection import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider import im.vector.app.core.ui.list.genericFooterItem +import im.vector.app.features.home.ShortcutCreator import im.vector.app.features.settings.VectorPreferences +import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel import javax.inject.Inject class RoomProfileController @Inject constructor( private val stringProvider: StringProvider, private val vectorPreferences: VectorPreferences, + private val shortcutCreator: ShortcutCreator, colorProvider: ColorProvider ) : TypedEpoxyController() { @@ -44,6 +47,7 @@ class RoomProfileController @Inject constructor( fun onBannedMemberListClicked() fun onNotificationsClicked() fun onUploadsClicked() + fun createShortcut() fun onSettingsClicked() fun onLeaveRoomClicked() fun onRoomIdClicked() @@ -54,12 +58,26 @@ class RoomProfileController @Inject constructor( return } val roomSummary = data.roomSummary() ?: return + + // Topic + roomSummary + .topic + .takeIf { it.isNotEmpty() } + ?.let { + buildProfileSection(stringProvider.getString(R.string.room_settings_topic)) + expandableTextItem { + id("topic") + content(it) + maxLines(2) + } + } + // Security buildProfileSection(stringProvider.getString(R.string.room_profile_section_security)) val learnMoreSubtitle = if (roomSummary.isEncrypted) { - R.string.room_profile_encrypted_subtitle + if (roomSummary.isDirect) R.string.direct_room_profile_encrypted_subtitle else R.string.room_profile_encrypted_subtitle } else { - R.string.room_profile_not_encrypted_subtitle + if (roomSummary.isDirect) R.string.direct_room_profile_not_encrypted_subtitle else R.string.room_profile_not_encrypted_subtitle } genericFooterItem { id("e2e info") @@ -71,7 +89,11 @@ class RoomProfileController @Inject constructor( buildProfileSection(stringProvider.getString(R.string.room_profile_section_more)) buildProfileAction( id = "settings", - title = stringProvider.getString(R.string.room_profile_section_more_settings), + title = stringProvider.getString(if (roomSummary.isDirect) { + R.string.direct_room_profile_section_more_settings + } else { + R.string.room_profile_section_more_settings + }), dividerColor = dividerColor, icon = R.drawable.ic_room_profile_settings, action = { callback?.onSettingsClicked() } @@ -110,12 +132,27 @@ class RoomProfileController @Inject constructor( icon = R.drawable.ic_room_profile_uploads, action = { callback?.onUploadsClicked() } ) + if (shortcutCreator.canCreateShortcut()) { + buildProfileAction( + id = "shortcut", + title = stringProvider.getString(R.string.room_settings_add_homescreen_shortcut), + dividerColor = dividerColor, + editable = false, + icon = R.drawable.ic_add_to_home_screen_24dp, + action = { callback?.createShortcut() } + ) + } buildProfileAction( id = "leave", - title = stringProvider.getString(R.string.room_profile_section_more_leave), + title = stringProvider.getString(if (roomSummary.isDirect) { + R.string.direct_room_profile_section_more_leave + } else { + R.string.room_profile_section_more_leave + }), dividerColor = dividerColor, divider = false, destructive = true, + icon = R.drawable.ic_room_actions_leave, editable = false, action = { callback?.onLeaveRoomClicked() } ) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt index d94a326ba9..c2f25c08d3 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt @@ -27,6 +27,7 @@ import android.view.View import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.core.app.ActivityOptionsCompat +import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.net.toUri import androidx.core.view.ViewCompat import androidx.core.view.isVisible @@ -41,14 +42,14 @@ import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.copyOnLongClick import im.vector.app.core.extensions.exhaustive +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.intent.getFilenameFromUri import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO -import im.vector.app.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_CAMERA -import im.vector.app.core.utils.allGranted import im.vector.app.core.utils.checkPermissions import im.vector.app.core.utils.copyToClipboard +import im.vector.app.core.utils.registerForPermissionsResult import im.vector.app.core.utils.startSharePlainTextIntent import im.vector.app.features.crypto.util.toImageRes import im.vector.app.features.home.AvatarRenderer @@ -115,6 +116,7 @@ class RoomProfileFragment @Inject constructor( is RoomProfileViewEvents.Failure -> showFailure(it.throwable) is RoomProfileViewEvents.ShareRoomProfile -> onShareRoomProfile(it.permalink) RoomProfileViewEvents.OnChangeAvatarSuccess -> dismissLoadingDialog() + is RoomProfileViewEvents.OnShortcutReady -> addShortcut(it) }.exhaustive } roomListQuickActionsSharedActionViewModel @@ -127,7 +129,6 @@ class RoomProfileFragment @Inject constructor( private fun setupLongClicks() { roomProfileNameView.copyOnLongClick() roomProfileAliasView.copyOnLongClick() - roomProfileTopicView.copyOnLongClick() } override fun onOptionsItemSelected(item: MenuItem): Boolean { @@ -185,7 +186,6 @@ class RoomProfileFragment @Inject constructor( roomProfileNameView.text = it.displayName matrixProfileToolbarTitleView.text = it.displayName roomProfileAliasView.setTextOrHide(it.canonicalAlias) - roomProfileTopicView.setTextOrHide(it.topic) val matrixItem = it.toMatrixItem() avatarRenderer.render(matrixItem, roomProfileAvatarView) avatarRenderer.render(matrixItem, matrixProfileToolbarAvatarImageView) @@ -232,6 +232,16 @@ class RoomProfileFragment @Inject constructor( roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomUploads) } + override fun createShortcut() { + // Ask the view model to prepare it... + roomProfileViewModel.handle(RoomProfileAction.CreateShortcut) + } + + private fun addShortcut(onShortcutReady: RoomProfileViewEvents.OnShortcutReady) { + // ... and propose the user to add it + ShortcutManagerCompat.requestPinShortcut(requireContext(), onShortcutReady.shortcutInfo, null) + } + override fun onLeaveRoomClicked() { AlertDialog.Builder(requireContext()) .setTitle(R.string.room_participants_leave_prompt_title) @@ -248,14 +258,19 @@ class RoomProfileFragment @Inject constructor( } private fun onShareRoomProfile(permalink: String) { - startSharePlainTextIntent(fragment = this, chooserTitle = null, text = permalink) + startSharePlainTextIntent( + fragment = this, + activityResultLauncher = null, + chooserTitle = null, + text = permalink + ) } private fun onAvatarClicked(view: View, matrixItem: MatrixItem.RoomItem) = withState(roomProfileViewModel) { if (matrixItem.avatarUrl?.isNotEmpty() == true) { val intent = BigImageViewerActivity.newIntent(requireContext(), matrixItem.getBestName(), matrixItem.avatarUrl!!, it.canChangeAvatar) val options = ActivityOptionsCompat.makeSceneTransitionAnimation(requireActivity(), view, ViewCompat.getTransitionName(view) ?: "") - startActivityForResult(intent, BigImageViewerActivity.REQUEST_CODE, options.toBundle()) + bigImageStartForActivityResult.launch(intent, options) } else if (it.canChangeAvatar) { showAvatarSelector() } @@ -273,14 +288,20 @@ class RoomProfileFragment @Inject constructor( .show() } + private val takePhotoPermissionActivityResultLauncher = registerForPermissionsResult { allGranted -> + if (allGranted) { + onAvatarTypeSelected(true) + } + } + private var avatarCameraUri: Uri? = null private fun onAvatarTypeSelected(isCamera: Boolean) { if (isCamera) { - if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, this, PERMISSION_REQUEST_CODE_LAUNCH_CAMERA)) { - avatarCameraUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(this) + if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), takePhotoPermissionActivityResultLauncher)) { + avatarCameraUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(requireActivity(), takePhotoActivityResultLauncher) } } else { - MultiPicker.get(MultiPicker.IMAGE).single().startWith(this) + MultiPicker.get(MultiPicker.IMAGE).single().startWith(pickImageActivityResultLauncher) } } @@ -292,37 +313,43 @@ class RoomProfileFragment @Inject constructor( .start(requireContext(), this) } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (resultCode == Activity.RESULT_OK) { - when (requestCode) { - MultiPicker.REQUEST_CODE_TAKE_PHOTO -> { - avatarCameraUri?.let { uri -> - MultiPicker.get(MultiPicker.CAMERA) - .getTakenPhoto(requireContext(), requestCode, resultCode, uri) - ?.let { - onRoomAvatarSelected(it) - } - } - } - MultiPicker.REQUEST_CODE_PICK_IMAGE -> { - MultiPicker - .get(MultiPicker.IMAGE) - .getSelectedFiles(requireContext(), requestCode, resultCode, data) - .firstOrNull()?.let { - onRoomAvatarSelected(it) - } - } - UCrop.REQUEST_CROP -> data?.let { onAvatarCropped(UCrop.getOutput(it)) } - BigImageViewerActivity.REQUEST_CODE -> data?.let { onAvatarCropped(it.data) } + private val takePhotoActivityResultLauncher = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + avatarCameraUri?.let { uri -> + MultiPicker.get(MultiPicker.CAMERA) + .getTakenPhoto(requireContext(), uri) + ?.let { + onRoomAvatarSelected(it) + } } } - super.onActivityResult(requestCode, resultCode, data) } - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { - if (allGranted(grantResults)) { + private val pickImageActivityResultLauncher = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + MultiPicker + .get(MultiPicker.IMAGE) + .getSelectedFiles(requireContext(), activityResult.data) + .firstOrNull()?.let { + onRoomAvatarSelected(it) + } + } + } + + private val bigImageStartForActivityResult = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + activityResult.data?.let { onAvatarCropped(it.data) } + } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + // TODO handle this one (Ucrop lib) + @Suppress("DEPRECATION") + super.onActivityResult(requestCode, resultCode, data) + + if (resultCode == Activity.RESULT_OK) { when (requestCode) { - PERMISSION_REQUEST_CODE_LAUNCH_CAMERA -> onAvatarTypeSelected(true) + UCrop.REQUEST_CROP -> data?.let { onAvatarCropped(UCrop.getOutput(it)) } } } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewEvents.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewEvents.kt index 1245016ada..380efd6fcd 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewEvents.kt @@ -16,6 +16,7 @@ package im.vector.app.features.roomprofile +import androidx.core.content.pm.ShortcutInfoCompat import im.vector.app.core.platform.VectorViewEvents /** @@ -27,4 +28,5 @@ sealed class RoomProfileViewEvents : VectorViewEvents { object OnChangeAvatarSuccess : RoomProfileViewEvents() data class ShareRoomProfile(val permalink: String) : RoomProfileViewEvents() + data class OnShortcutReady(val shortcutInfo: ShortcutInfoCompat) : RoomProfileViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt index 2d409888e9..922dd995e9 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt @@ -17,15 +17,20 @@ package im.vector.app.features.roomprofile +import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject import im.vector.app.R +import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider +import im.vector.app.features.home.ShortcutCreator import im.vector.app.features.powerlevel.PowerLevelsObservableFactory +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.EventType @@ -36,10 +41,12 @@ import org.matrix.android.sdk.rx.rx import org.matrix.android.sdk.rx.unwrap import java.util.UUID -class RoomProfileViewModel @AssistedInject constructor(@Assisted private val initialState: RoomProfileViewState, - private val stringProvider: StringProvider, - private val session: Session) - : VectorViewModel(initialState) { +class RoomProfileViewModel @AssistedInject constructor( + @Assisted private val initialState: RoomProfileViewState, + private val stringProvider: StringProvider, + private val shortcutCreator: ShortcutCreator, + private val session: Session +) : VectorViewModel(initialState) { @AssistedInject.Factory interface Factory { @@ -88,11 +95,24 @@ class RoomProfileViewModel @AssistedInject constructor(@Assisted private val ini } } - override fun handle(action: RoomProfileAction) = when (action) { - RoomProfileAction.LeaveRoom -> handleLeaveRoom() - is RoomProfileAction.ChangeRoomNotificationState -> handleChangeNotificationMode(action) - is RoomProfileAction.ShareRoomProfile -> handleShareRoomProfile() - is RoomProfileAction.ChangeRoomAvatar -> handleChangeAvatar(action) + override fun handle(action: RoomProfileAction) { + when (action) { + RoomProfileAction.LeaveRoom -> handleLeaveRoom() + is RoomProfileAction.ChangeRoomNotificationState -> handleChangeNotificationMode(action) + is RoomProfileAction.ShareRoomProfile -> handleShareRoomProfile() + is RoomProfileAction.ChangeRoomAvatar -> handleChangeAvatar(action) + RoomProfileAction.CreateShortcut -> handleCreateShortcut() + }.exhaustive + } + + private fun handleCreateShortcut() { + viewModelScope.launch(Dispatchers.IO) { + withState { state -> + state.roomSummary() + ?.let { shortcutCreator.create(it) } + ?.let { _viewEvents.post(RoomProfileViewEvents.OnShortcutReady(it)) } + } + } } private fun handleChangeNotificationMode(action: RoomProfileAction.ChangeRoomNotificationState) { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedListMemberAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedListMemberAction.kt index 9af554e883..ca7d567d90 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedListMemberAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedListMemberAction.kt @@ -16,10 +16,11 @@ package im.vector.app.features.roomprofile.banned -import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import im.vector.app.core.platform.VectorViewModelAction +import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary sealed class RoomBannedListMemberAction : VectorViewModelAction { data class QueryInfo(val roomMemberSummary: RoomMemberSummary) : RoomBannedListMemberAction() data class UnBanUser(val roomMemberSummary: RoomMemberSummary) : RoomBannedListMemberAction() + data class Filter(val filter: String) : RoomBannedListMemberAction() } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedListMemberViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedListMemberViewModel.kt index b65b3de807..1cce2f96cb 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedListMemberViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedListMemberViewModel.kt @@ -22,6 +22,13 @@ import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject +import im.vector.app.R +import im.vector.app.core.extensions.exhaustive +import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.resources.StringProvider +import im.vector.app.features.powerlevel.PowerLevelsObservableFactory +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.EventType @@ -34,12 +41,6 @@ import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.internal.util.awaitCallback import org.matrix.android.sdk.rx.rx import org.matrix.android.sdk.rx.unwrap -import im.vector.app.R -import im.vector.app.core.platform.VectorViewModel -import im.vector.app.core.resources.StringProvider -import im.vector.app.features.powerlevel.PowerLevelsObservableFactory -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch class RoomBannedListMemberViewModel @AssistedInject constructor(@Assisted initialState: RoomBannedMemberListViewState, private val stringProvider: StringProvider, @@ -90,6 +91,15 @@ class RoomBannedListMemberViewModel @AssistedInject constructor(@Assisted initia when (action) { is RoomBannedListMemberAction.QueryInfo -> onQueryBanInfo(action.roomMemberSummary) is RoomBannedListMemberAction.UnBanUser -> unBanUser(action.roomMemberSummary) + is RoomBannedListMemberAction.Filter -> handleFilter(action) + }.exhaustive + } + + private fun handleFilter(action: RoomBannedListMemberAction.Filter) { + setState { + copy( + filter = action.filter + ) } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListController.kt index a65a30441d..2a0c787a7a 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListController.kt @@ -17,8 +17,6 @@ package im.vector.app.features.roomprofile.banned import com.airbnb.epoxy.TypedEpoxyController -import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary -import org.matrix.android.sdk.api.util.toMatrixItem import im.vector.app.R import im.vector.app.core.epoxy.dividerItem import im.vector.app.core.epoxy.profiles.buildProfileSection @@ -28,11 +26,15 @@ import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider import im.vector.app.core.ui.list.genericFooterItem import im.vector.app.features.home.AvatarRenderer +import im.vector.app.features.roomprofile.members.RoomMemberSummaryFilter +import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary +import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject class RoomBannedMemberListController @Inject constructor( private val avatarRenderer: AvatarRenderer, private val stringProvider: StringProvider, + private val roomMemberSummaryFilter: RoomMemberSummaryFilter, colorProvider: ColorProvider ) : TypedEpoxyController() { @@ -63,34 +65,37 @@ class RoomBannedMemberListController @Inject constructor( } else { buildProfileSection(quantityString) - bannedList.join( - each = { _, roomMember -> - val actionInProgress = data.onGoingModerationAction.contains(roomMember.userId) - profileMatrixItemWithProgress { - id(roomMember.userId) - matrixItem(roomMember.toMatrixItem()) - avatarRenderer(avatarRenderer) - apply { - if (actionInProgress) { - inProgress(true) - editable(false) - } else { - inProgress(false) - editable(true) - clickListener { _ -> - callback?.onUnbanClicked(roomMember) + roomMemberSummaryFilter.filter = data.filter + bannedList + .filter { roomMemberSummaryFilter.test(it) } + .join( + each = { _, roomMember -> + val actionInProgress = data.onGoingModerationAction.contains(roomMember.userId) + profileMatrixItemWithProgress { + id(roomMember.userId) + matrixItem(roomMember.toMatrixItem()) + avatarRenderer(avatarRenderer) + apply { + if (actionInProgress) { + inProgress(true) + editable(false) + } else { + inProgress(false) + editable(true) + clickListener { _ -> + callback?.onUnbanClicked(roomMember) + } + } } } + }, + between = { _, roomMemberBefore -> + dividerItem { + id("divider_${roomMemberBefore.userId}") + color(dividerColor) + } } - } - }, - between = { _, roomMemberBefore -> - dividerItem { - id("divider_${roomMemberBefore.userId}") - color(dividerColor) - } - } - ) + ) } } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListFragment.kt index 480deea6af..81b977ac97 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListFragment.kt @@ -19,11 +19,11 @@ package im.vector.app.features.roomprofile.banned import android.os.Bundle import android.view.View import androidx.appcompat.app.AlertDialog +import androidx.appcompat.widget.SearchView +import androidx.core.view.isVisible import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState -import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary -import org.matrix.android.sdk.api.util.toMatrixItem import im.vector.app.R import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith @@ -32,6 +32,8 @@ import im.vector.app.core.utils.toast import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.roomprofile.RoomProfileArgs import kotlinx.android.synthetic.main.fragment_room_setting_generic.* +import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary +import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject class RoomBannedMemberListFragment @Inject constructor( @@ -53,6 +55,7 @@ class RoomBannedMemberListFragment @Inject constructor( super.onViewCreated(view, savedInstanceState) roomMemberListController.callback = this setupToolbar(roomSettingsToolbar) + setupSearchView() recyclerView.configureWith(roomMemberListController, hasFixedSize = true) viewModel.observeViewEvents { @@ -72,7 +75,7 @@ class RoomBannedMemberListFragment @Inject constructor( } .show() } - is RoomBannedViewEvents.ToastError -> { + is RoomBannedViewEvents.ToastError -> { requireActivity().toast(it.info) } } @@ -84,6 +87,21 @@ class RoomBannedMemberListFragment @Inject constructor( super.onDestroyView() } + private fun setupSearchView() { + searchViewAppBarLayout.isVisible = true + searchView.queryHint = getString(R.string.search_banned_user_hint) + searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + override fun onQueryTextSubmit(query: String): Boolean { + return true + } + + override fun onQueryTextChange(newText: String): Boolean { + viewModel.handle(RoomBannedListMemberAction.Filter(newText)) + return true + } + }) + } + override fun invalidate() = withState(viewModel) { viewState -> roomMemberListController.setData(viewState) renderRoomSummary(viewState) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewState.kt index ee8e534b1f..2861b30222 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewState.kt @@ -19,14 +19,15 @@ package im.vector.app.features.roomprofile.banned import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized +import im.vector.app.features.roomprofile.RoomProfileArgs import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary -import im.vector.app.features.roomprofile.RoomProfileArgs data class RoomBannedMemberListViewState( val roomId: String, val roomSummary: Async = Uninitialized, val bannedMemberSummaries: Async> = Uninitialized, + val filter: String = "", val onGoingModerationAction: List = emptyList(), val canUserBan: Boolean = false ) : MvRxState { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedViewEvents.kt b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedViewEvents.kt index ea2d775a29..6b59debe96 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedViewEvents.kt @@ -16,8 +16,8 @@ package im.vector.app.features.roomprofile.banned -import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import im.vector.app.core.platform.VectorViewEvents +import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary sealed class RoomBannedViewEvents : VectorViewEvents { data class ShowBannedInfo(val bannedByUserId: String, val banReason: String, val roomMemberSummary: RoomMemberSummary) : RoomBannedViewEvents() diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListAction.kt index c61dcb98f5..342a2e8585 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListAction.kt @@ -20,4 +20,5 @@ import im.vector.app.core.platform.VectorViewModelAction sealed class RoomMemberListAction : VectorViewModelAction { data class RevokeThreePidInvite(val stateKey: String) : RoomMemberListAction() + data class FilterMemberList(val searchTerm: String) : RoomMemberListAction() } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt index 59b1bca26f..71ac7fcec4 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt @@ -17,12 +17,6 @@ package im.vector.app.features.roomprofile.members import com.airbnb.epoxy.TypedEpoxyController -import org.matrix.android.sdk.api.session.events.model.Event -import org.matrix.android.sdk.api.session.events.model.toModel -import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary -import org.matrix.android.sdk.api.session.room.model.RoomThirdPartyInviteContent -import org.matrix.android.sdk.api.util.MatrixItem -import org.matrix.android.sdk.api.util.toMatrixItem import im.vector.app.R import im.vector.app.core.epoxy.dividerItem import im.vector.app.core.epoxy.profiles.buildProfileSection @@ -31,17 +25,24 @@ import im.vector.app.core.extensions.join import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider import im.vector.app.features.home.AvatarRenderer +import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary +import org.matrix.android.sdk.api.session.room.model.RoomThirdPartyInviteContent +import org.matrix.android.sdk.api.util.MatrixItem +import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject class RoomMemberListController @Inject constructor( private val avatarRenderer: AvatarRenderer, private val stringProvider: StringProvider, + private val roomMemberSummaryFilter: RoomMemberSummaryFilter, colorProvider: ColorProvider ) : TypedEpoxyController() { interface Callback { fun onRoomMemberClicked(roomMember: RoomMemberSummary) - fun onThreePidInvites(event: Event) + fun onThreePidInviteClicked(event: Event) } private val dividerColor = colorProvider.getColorFromAttribute(R.attr.vctr_list_divider_color) @@ -53,17 +54,29 @@ class RoomMemberListController @Inject constructor( } override fun buildModels(data: RoomMemberListViewState?) { - val roomMembersByPowerLevel = data?.roomMemberSummaries?.invoke() ?: return - val threePidInvites = data.threePidInvites().orEmpty() + data ?: return + + roomMemberSummaryFilter.filter = data.filter + + val roomMembersByPowerLevel = data.roomMemberSummaries.invoke() ?: return + val threePidInvites = data.threePidInvites() + ?.filter { event -> + event.content.toModel() + ?.takeIf { + data.filter.isEmpty() || it.displayName.contains(data.filter, ignoreCase = true) + } != null + } + .orEmpty() var threePidInvitesDone = threePidInvites.isEmpty() for ((powerLevelCategory, roomMemberList) in roomMembersByPowerLevel) { - if (roomMemberList.isEmpty()) { + val filteredRoomMemberList = roomMemberList.filter { roomMemberSummaryFilter.test(it) } + if (filteredRoomMemberList.isEmpty()) { continue } if (powerLevelCategory == RoomMemberListCategories.USER && !threePidInvitesDone) { - // If there is not regular invite, display threepid invite before the regular user + // If there is no regular invite, display threepid invite before the regular user buildProfileSection( stringProvider.getString(RoomMemberListCategories.INVITE.titleRes) ) @@ -75,7 +88,7 @@ class RoomMemberListController @Inject constructor( buildProfileSection( stringProvider.getString(powerLevelCategory.titleRes) ) - roomMemberList.join( + filteredRoomMemberList.join( each = { _, roomMember -> profileMatrixItem { id(roomMember.userId) @@ -94,12 +107,13 @@ class RoomMemberListController @Inject constructor( } } ) - if (powerLevelCategory == RoomMemberListCategories.INVITE) { + if (powerLevelCategory == RoomMemberListCategories.INVITE && !threePidInvitesDone) { // Display the threepid invite after the regular invite dividerItem { id("divider_threepidinvites") color(dividerColor) } + buildThreePidInvites(data) threePidInvitesDone = true } @@ -128,7 +142,7 @@ class RoomMemberListController @Inject constructor( avatarRenderer(avatarRenderer) editable(data.actionsPermissions.canRevokeThreePidInvite) clickListener { _ -> - callback?.onThreePidInvites(event) + callback?.onThreePidInviteClicked(event) } } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListFragment.kt index 46d0f35b95..1b3e33a161 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListFragment.kt @@ -17,25 +17,27 @@ package im.vector.app.features.roomprofile.members import android.os.Bundle -import android.view.Menu -import android.view.MenuItem import android.view.View import androidx.appcompat.app.AlertDialog +import androidx.appcompat.widget.SearchView +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState -import org.matrix.android.sdk.api.session.events.model.Event -import org.matrix.android.sdk.api.session.events.model.toModel -import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary -import org.matrix.android.sdk.api.session.room.model.RoomThirdPartyInviteContent -import org.matrix.android.sdk.api.util.toMatrixItem import im.vector.app.R import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.roomprofile.RoomProfileArgs +import kotlinx.android.synthetic.main.fragment_room_member_list.* import kotlinx.android.synthetic.main.fragment_room_setting_generic.* +import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary +import org.matrix.android.sdk.api.session.room.model.RoomThirdPartyInviteContent +import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject class RoomMemberListFragment @Inject constructor( @@ -47,35 +49,53 @@ class RoomMemberListFragment @Inject constructor( private val viewModel: RoomMemberListViewModel by fragmentViewModel() private val roomProfileArgs: RoomProfileArgs by args() - override fun getLayoutResId() = R.layout.fragment_room_setting_generic - - override fun getMenuRes() = R.menu.menu_room_member_list - - override fun onPrepareOptionsMenu(menu: Menu) { - val canInvite = withState(viewModel) { - it.actionsPermissions.canInvite - } - menu.findItem(R.id.menu_room_member_list_add_member).isVisible = canInvite - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - R.id.menu_room_member_list_add_member -> { - navigator.openInviteUsersToRoom(requireContext(), roomProfileArgs.roomId) - return true - } - } - return super.onOptionsItemSelected(item) - } + override fun getLayoutResId() = R.layout.fragment_room_member_list override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) roomMemberListController.callback = this setupToolbar(roomSettingsToolbar) + setupSearchView() + setupInviteUsersButton() recyclerView.configureWith(roomMemberListController, hasFixedSize = true) - viewModel.selectSubscribe(this, RoomMemberListViewState::actionsPermissions) { - invalidateOptionsMenu() + } + + private fun setupInviteUsersButton() { + inviteUsersButton.debouncedClicks { + navigator.openInviteUsersToRoom(requireContext(), roomProfileArgs.roomId) } + // Hide FAB when list is scrolling + recyclerView.addOnScrollListener( + object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + when (newState) { + RecyclerView.SCROLL_STATE_IDLE -> { + if (withState(viewModel) { it.actionsPermissions.canInvite }) { + inviteUsersButton.show() + } + } + RecyclerView.SCROLL_STATE_DRAGGING, + RecyclerView.SCROLL_STATE_SETTLING -> { + inviteUsersButton.hide() + } + } + } + } + ) + } + + private fun setupSearchView() { + searchView.queryHint = getString(R.string.search_members_hint) + searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + override fun onQueryTextSubmit(query: String): Boolean { + return true + } + + override fun onQueryTextChange(newText: String): Boolean { + viewModel.handle(RoomMemberListAction.FilterMemberList(newText)) + return true + } + }) } override fun onDestroyView() { @@ -86,13 +106,16 @@ class RoomMemberListFragment @Inject constructor( override fun invalidate() = withState(viewModel) { viewState -> roomMemberListController.setData(viewState) renderRoomSummary(viewState) + inviteUsersButton.isVisible = viewState.actionsPermissions.canInvite + // Display filter only if there are more than 2 members in this room + searchViewAppBarLayout.isVisible = viewState.roomSummary()?.otherMemberIds.orEmpty().size > 1 } override fun onRoomMemberClicked(roomMember: RoomMemberSummary) { navigator.openRoomMemberProfile(roomMember.userId, roomId = roomProfileArgs.roomId, context = requireActivity()) } - override fun onThreePidInvites(event: Event) { + override fun onThreePidInviteClicked(event: Event) { // Display a dialog to revoke invite if power level is high enough val content = event.content.toModel() ?: return val stateKey = event.stateKey ?: return diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt index fedf1729ad..9e402c675b 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt @@ -188,6 +188,7 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState override fun handle(action: RoomMemberListAction) { when (action) { is RoomMemberListAction.RevokeThreePidInvite -> handleRevokeThreePidInvite(action) + is RoomMemberListAction.FilterMemberList -> handleFilterMemberList(action) }.exhaustive } @@ -201,4 +202,12 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState ) } } + + private fun handleFilterMemberList(action: RoomMemberListAction.FilterMemberList) { + setState { + copy( + filter = action.searchTerm + ) + } + } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewState.kt index 4d21aa103f..6fe8df3d36 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewState.kt @@ -20,17 +20,18 @@ import androidx.annotation.StringRes import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized +import im.vector.app.R +import im.vector.app.features.roomprofile.RoomProfileArgs import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary -import im.vector.app.R -import im.vector.app.features.roomprofile.RoomProfileArgs data class RoomMemberListViewState( val roomId: String, val roomSummary: Async = Uninitialized, val roomMemberSummaries: Async = Uninitialized, + val filter: String = "", val threePidInvites: Async> = Uninitialized, val trustLevelMap: Async> = Uninitialized, val actionsPermissions: ActionPermissions = ActionPermissions() diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberSummaryFilter.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberSummaryFilter.kt new file mode 100644 index 0000000000..e2cc3f7b99 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberSummaryFilter.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.roomprofile.members + +import io.reactivex.functions.Predicate +import org.matrix.android.sdk.api.extensions.orFalse +import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary +import javax.inject.Inject + +class RoomMemberSummaryFilter @Inject constructor() : Predicate { + var filter: String = "" + + override fun test(roomMemberSummary: RoomMemberSummary): Boolean { + if (filter.isEmpty()) { + // No filter + return true + } + + return roomMemberSummary.displayName?.contains(filter, ignoreCase = true).orFalse() + // We should maybe exclude the domain from the userId + || roomMemberSummary.userId.contains(filter, ignoreCase = true) + } +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsAction.kt index cac6994952..5d35586cce 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsAction.kt @@ -16,8 +16,8 @@ package im.vector.app.features.roomprofile.settings -import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility import im.vector.app.core.platform.VectorViewModelAction +import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility sealed class RoomSettingsAction : VectorViewModelAction { data class SetRoomName(val newName: String) : RoomSettingsAction() diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt index a60f223532..f680e28aa8 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt @@ -17,10 +17,6 @@ package im.vector.app.features.roomprofile.settings import com.airbnb.epoxy.TypedEpoxyController -import org.matrix.android.sdk.api.session.events.model.Event -import org.matrix.android.sdk.api.session.events.model.toModel -import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent -import org.matrix.android.sdk.api.session.room.model.RoomSummary import im.vector.app.R import im.vector.app.core.epoxy.profiles.buildProfileAction import im.vector.app.core.epoxy.profiles.buildProfileSection @@ -28,6 +24,10 @@ import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider import im.vector.app.features.form.formEditTextItem import im.vector.app.features.home.room.detail.timeline.format.RoomHistoryVisibilityFormatter +import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent +import org.matrix.android.sdk.api.session.room.model.RoomSummary import javax.inject.Inject class RoomSettingsController @Inject constructor( diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt index 6a27d5ee0a..68c631b391 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt @@ -25,10 +25,6 @@ import androidx.core.view.isVisible import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState -import org.matrix.android.sdk.api.session.events.model.toModel -import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility -import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent -import org.matrix.android.sdk.api.util.toMatrixItem import im.vector.app.R import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith @@ -40,6 +36,10 @@ import im.vector.app.features.home.room.detail.timeline.format.RoomHistoryVisibi import im.vector.app.features.roomprofile.RoomProfileArgs import kotlinx.android.synthetic.main.fragment_room_setting_generic.* import kotlinx.android.synthetic.main.merge_overlay_waiting_view.* +import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility +import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent +import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject class RoomSettingsFragment @Inject constructor( diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt index 337610aa46..0fdb6139c2 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt @@ -21,17 +21,17 @@ import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject +import im.vector.app.core.extensions.exhaustive +import im.vector.app.core.platform.VectorViewModel +import im.vector.app.features.powerlevel.PowerLevelsObservableFactory +import io.reactivex.Completable +import io.reactivex.Observable import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.rx.rx import org.matrix.android.sdk.rx.unwrap -import im.vector.app.core.extensions.exhaustive -import im.vector.app.core.platform.VectorViewModel -import im.vector.app.features.powerlevel.PowerLevelsObservableFactory -import io.reactivex.Completable -import io.reactivex.Observable class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: RoomSettingsViewState, private val session: Session) @@ -102,12 +102,12 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: val powerLevelsHelper = PowerLevelsHelper(it) val permissions = RoomSettingsViewState.ActionPermissions( canChangeName = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_NAME), - canChangeTopic = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_TOPIC), + canChangeTopic = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_TOPIC), canChangeCanonicalAlias = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_CANONICAL_ALIAS), canChangeHistoryReadability = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_HISTORY_VISIBILITY), - canEnableEncryption = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_ENCRYPTION) + canEnableEncryption = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_ENCRYPTION) ) setState { copy(actionPermissions = permissions) } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewState.kt index b20861ae8a..fe04c8b508 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewState.kt @@ -19,10 +19,10 @@ package im.vector.app.features.roomprofile.settings import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized +import im.vector.app.features.roomprofile.RoomProfileArgs import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility import org.matrix.android.sdk.api.session.room.model.RoomSummary -import im.vector.app.features.roomprofile.RoomProfileArgs data class RoomSettingsViewState( val roomId: String, diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsAction.kt index f383122561..ea5312469d 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsAction.kt @@ -16,8 +16,8 @@ package im.vector.app.features.roomprofile.uploads -import org.matrix.android.sdk.api.session.room.uploads.UploadEvent import im.vector.app.core.platform.VectorViewModelAction +import org.matrix.android.sdk.api.session.room.uploads.UploadEvent sealed class RoomUploadsAction : VectorViewModelAction { data class Download(val uploadEvent: UploadEvent) : RoomUploadsAction() diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt index b3f5047df4..fb53d91e09 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt @@ -23,23 +23,21 @@ import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import com.google.android.material.tabs.TabLayoutMediator -import org.matrix.android.sdk.api.util.toMatrixItem import im.vector.app.R import im.vector.app.core.extensions.exhaustive import im.vector.app.core.intent.getMimeTypeFromUri import im.vector.app.core.platform.VectorBaseFragment -import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.saveMedia import im.vector.app.core.utils.shareMedia import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.roomprofile.RoomProfileArgs import kotlinx.android.synthetic.main.fragment_room_uploads.* +import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject class RoomUploadsFragment @Inject constructor( private val viewModelFactory: RoomUploadsViewModel.Factory, - private val stringProvider: StringProvider, private val avatarRenderer: AvatarRenderer, private val notificationUtils: NotificationUtils ) : VectorBaseFragment(), RoomUploadsViewModel.Factory by viewModelFactory { @@ -58,8 +56,8 @@ class RoomUploadsFragment @Inject constructor( TabLayoutMediator(roomUploadsTabs, roomUploadsViewPager) { tab, position -> when (position) { - 0 -> tab.text = stringProvider.getString(R.string.uploads_media_title) - 1 -> tab.text = stringProvider.getString(R.string.uploads_files_title) + 0 -> tab.text = getString(R.string.uploads_media_title) + 1 -> tab.text = getString(R.string.uploads_files_title) } }.attach() diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt index db8de1f974..76b1a9e0c3 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt @@ -26,6 +26,9 @@ import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject +import im.vector.app.core.extensions.exhaustive +import im.vector.app.core.platform.VectorViewModel +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.file.FileService import org.matrix.android.sdk.api.session.room.model.message.MessageType @@ -35,9 +38,6 @@ import org.matrix.android.sdk.internal.crypto.attachments.toElementToDecrypt import org.matrix.android.sdk.internal.util.awaitCallback import org.matrix.android.sdk.rx.rx import org.matrix.android.sdk.rx.unwrap -import im.vector.app.core.extensions.exhaustive -import im.vector.app.core.platform.VectorViewModel -import kotlinx.coroutines.launch import java.io.File class RoomUploadsViewModel @AssistedInject constructor( diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewState.kt index b983a6d074..b85e4f04de 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewState.kt @@ -19,9 +19,9 @@ package im.vector.app.features.roomprofile.uploads import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized +import im.vector.app.features.roomprofile.RoomProfileArgs import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.uploads.UploadEvent -import im.vector.app.features.roomprofile.RoomProfileArgs data class RoomUploadsViewState( val roomId: String = "", diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/files/RoomUploadsFilesFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/files/RoomUploadsFilesFragment.kt index 66b4dfda33..3c52f2e107 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/files/RoomUploadsFilesFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/files/RoomUploadsFilesFragment.kt @@ -24,7 +24,6 @@ import com.airbnb.mvrx.Loading import com.airbnb.mvrx.Success import com.airbnb.mvrx.parentFragmentViewModel import com.airbnb.mvrx.withState -import org.matrix.android.sdk.api.session.room.uploads.UploadEvent import im.vector.app.R import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith @@ -34,6 +33,7 @@ import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.features.roomprofile.uploads.RoomUploadsAction import im.vector.app.features.roomprofile.uploads.RoomUploadsViewModel import kotlinx.android.synthetic.main.fragment_generic_state_view_recycler.* +import org.matrix.android.sdk.api.session.room.uploads.UploadEvent import javax.inject.Inject class RoomUploadsFilesFragment @Inject constructor( diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/RoomUploadsMediaFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/RoomUploadsMediaFragment.kt index eabeab9183..d25277d9f5 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/RoomUploadsMediaFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/RoomUploadsMediaFragment.kt @@ -29,10 +29,6 @@ import com.airbnb.mvrx.Success import com.airbnb.mvrx.parentFragmentViewModel import com.airbnb.mvrx.withState import com.google.android.material.appbar.AppBarLayout -import org.matrix.android.sdk.api.session.room.model.message.MessageImageContent -import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent -import org.matrix.android.sdk.api.session.room.model.message.getFileUrl -import org.matrix.android.sdk.internal.crypto.attachments.toElementToDecrypt import im.vector.app.R import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.trackItemsVisibilityChange @@ -48,6 +44,10 @@ import im.vector.app.features.roomprofile.uploads.RoomUploadsViewModel import im.vector.app.features.roomprofile.uploads.RoomUploadsViewState import kotlinx.android.synthetic.main.fragment_generic_state_view_recycler.* import kotlinx.android.synthetic.main.fragment_room_uploads.* +import org.matrix.android.sdk.api.session.room.model.message.MessageImageContent +import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent +import org.matrix.android.sdk.api.session.room.model.message.getFileUrl +import org.matrix.android.sdk.internal.crypto.attachments.toElementToDecrypt import javax.inject.Inject class RoomUploadsMediaFragment @Inject constructor( diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/UploadsMediaController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/UploadsMediaController.kt index 6f60a77296..2a77e65b9e 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/UploadsMediaController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/UploadsMediaController.kt @@ -19,12 +19,6 @@ package im.vector.app.features.roomprofile.uploads.media import android.view.View import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.epoxy.VisibilityState -import org.matrix.android.sdk.api.session.room.model.message.MessageImageContent -import org.matrix.android.sdk.api.session.room.model.message.MessageType -import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent -import org.matrix.android.sdk.api.session.room.model.message.getFileUrl -import org.matrix.android.sdk.api.session.room.uploads.UploadEvent -import org.matrix.android.sdk.internal.crypto.attachments.toElementToDecrypt import im.vector.app.core.epoxy.squareLoadingItem import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.resources.StringProvider @@ -32,6 +26,12 @@ import im.vector.app.core.utils.DimensionConverter import im.vector.app.features.media.ImageContentRenderer import im.vector.app.features.media.VideoContentRenderer import im.vector.app.features.roomprofile.uploads.RoomUploadsViewState +import org.matrix.android.sdk.api.session.room.model.message.MessageImageContent +import org.matrix.android.sdk.api.session.room.model.message.MessageType +import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent +import org.matrix.android.sdk.api.session.room.model.message.getFileUrl +import org.matrix.android.sdk.api.session.room.uploads.UploadEvent +import org.matrix.android.sdk.internal.crypto.attachments.toElementToDecrypt import javax.inject.Inject class UploadsMediaController @Inject constructor( diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/UploadsVideoItem.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/UploadsVideoItem.kt index 3a042943c2..908e3ba72c 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/UploadsVideoItem.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/UploadsVideoItem.kt @@ -39,9 +39,9 @@ abstract class UploadsVideoItem : VectorEpoxyModel() { override fun bind(holder: Holder) { super.bind(holder) holder.view.setOnClickListener( - DebouncedClickListener(View.OnClickListener { _ -> - listener?.onItemClicked(holder.imageView, data) - }) + DebouncedClickListener(View.OnClickListener { _ -> + listener?.onItemClicked(holder.imageView, data) + }) ) imageContentRenderer.render(data.thumbnailMediaData, holder.imageView, IMAGE_SIZE_DP) ViewCompat.setTransitionName(holder.imageView, "videoPreview_${id()}") diff --git a/vector/src/main/java/im/vector/app/features/session/SessionListener.kt b/vector/src/main/java/im/vector/app/features/session/SessionListener.kt index 89e02f3600..66b1bae70d 100644 --- a/vector/src/main/java/im/vector/app/features/session/SessionListener.kt +++ b/vector/src/main/java/im/vector/app/features/session/SessionListener.kt @@ -18,10 +18,10 @@ package im.vector.app.features.session import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import org.matrix.android.sdk.api.failure.GlobalError -import org.matrix.android.sdk.api.session.Session import im.vector.app.core.extensions.postLiveEvent import im.vector.app.core.utils.LiveEvent +import org.matrix.android.sdk.api.failure.GlobalError +import org.matrix.android.sdk.api.session.Session import javax.inject.Inject import javax.inject.Singleton diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt index 5d27e9a337..233b327038 100755 --- a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt @@ -1,6 +1,4 @@ /* - * Copyright 2016 OpenMarket Ltd - * Copyright 2017 Vector Creations Ltd * Copyright 2018 New Vector Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -154,7 +152,7 @@ class VectorPreferences @Inject constructor(private val context: Context) { private const val SETTINGS_DEVELOPER_MODE_FAIL_FAST_PREFERENCE_KEY = "SETTINGS_DEVELOPER_MODE_FAIL_FAST_PREFERENCE_KEY" // SETTINGS_LABS_HIDE_TECHNICAL_E2E_ERRORS - private const val SETTINGS_LABS_MERGE_E2E_ERRORS = "SETTINGS_LABS_MERGE_E2E_ERRORS" + private const val SETTINGS_LABS_SHOW_COMPLETE_HISTORY_IN_ENCRYPTED_ROOM = "SETTINGS_LABS_SHOW_COMPLETE_HISTORY_IN_ENCRYPTED_ROOM" const val SETTINGS_LABS_UNREAD_NOTIFICATIONS_AS_TAB = "SETTINGS_LABS_UNREAD_NOTIFICATIONS_AS_TAB" // analytics @@ -291,8 +289,8 @@ class VectorPreferences @Inject constructor(private val context: Context) { return defaultPrefs.getBoolean(SETTINGS_LABS_ENABLE_SWIPE_TO_REPLY, false) } - fun mergeUTDinTimeline(): Boolean { - return defaultPrefs.getBoolean(SETTINGS_LABS_MERGE_E2E_ERRORS, false) + fun labShowCompleteHistoryInEncryptedRoom(): Boolean { + return developerMode() && defaultPrefs.getBoolean(SETTINGS_LABS_SHOW_COMPLETE_HISTORY_IN_ENCRYPTED_ROOM, false) } fun labAllowedExtendedLogging(): Boolean { diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt index 50692ef255..28702940ee 100755 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt @@ -21,14 +21,14 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat -import org.matrix.android.sdk.api.failure.GlobalError -import org.matrix.android.sdk.api.session.Session import im.vector.app.R import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.replaceFragment import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.features.settings.devices.VectorSettingsDevicesFragment import kotlinx.android.synthetic.main.activity_vector_settings.* +import org.matrix.android.sdk.api.failure.GlobalError +import org.matrix.android.sdk.api.session.Session import timber.log.Timber import javax.inject.Inject @@ -71,7 +71,7 @@ class VectorSettingsActivity : VectorBaseActivity(), VectorSettingsDevicesFragment::class.java, null, FRAGMENT_TAG) - EXTRA_DIRECT_ACCESS_NOTIFICATIONS -> { + EXTRA_DIRECT_ACCESS_NOTIFICATIONS -> { requestHighlightPreferenceKeyOnResume(VectorPreferences.SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY) replaceFragment(R.id.vector_settings_page, VectorSettingsNotificationPreferenceFragment::class.java, null, FRAGMENT_TAG) } @@ -107,7 +107,8 @@ class VectorSettingsActivity : VectorBaseActivity(), } if (oFragment != null) { - oFragment.setTargetFragment(caller, 0) + // Deprecated, I comment it, I think it is useless + // oFragment.setTargetFragment(caller, 0) // Replace the existing Fragment with the new Fragment supportFragmentManager.beginTransaction() .setCustomAnimations(R.anim.right_in, R.anim.fade_out, R.anim.fade_in, R.anim.right_out) @@ -135,7 +136,7 @@ class VectorSettingsActivity : VectorBaseActivity(), } } - fun navigateTo(fragmentClass: Class) { + fun navigateTo(fragmentClass: Class) { supportFragmentManager.beginTransaction() .setCustomAnimations(R.anim.right_in, R.anim.fade_out, R.anim.fade_in, R.anim.right_out) .replace(R.id.vector_settings_page, fragmentClass, null) diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsBaseFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsBaseFragment.kt index 512545afe2..add6fba38c 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsBaseFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsBaseFragment.kt @@ -70,7 +70,7 @@ abstract class VectorSettingsBaseFragment : PreferenceFragmentCompat(), HasScree override fun onResume() { super.onResume() - Timber.i("onResume Fragment ${this.javaClass.simpleName}") + Timber.i("onResume Fragment ${javaClass.simpleName}") vectorActivity.supportActionBar?.setTitle(titleRes) // find the view from parent activity mLoadingView = vectorActivity.findViewById(R.id.vector_settings_spinner_views) diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt index f538c4993b..dfae073e7e 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt @@ -41,6 +41,7 @@ import com.google.android.material.textfield.TextInputLayout import com.yalantis.ucrop.UCrop import im.vector.app.R import im.vector.app.core.extensions.hideKeyboard +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.showPassword import im.vector.app.core.intent.getFilenameFromUri import im.vector.app.core.platform.SimpleTextWatcher @@ -48,11 +49,10 @@ import im.vector.app.core.preference.UserAvatarPreference import im.vector.app.core.preference.VectorPreference import im.vector.app.core.preference.VectorSwitchPreference import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO -import im.vector.app.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_CAMERA import im.vector.app.core.utils.TextUtils -import im.vector.app.core.utils.allGranted import im.vector.app.core.utils.checkPermissions import im.vector.app.core.utils.getSizeOfFiles +import im.vector.app.core.utils.registerForPermissionsResult import im.vector.app.core.utils.toast import im.vector.app.features.MainActivity import im.vector.app.features.MainActivityArgs @@ -259,8 +259,7 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() { findPreference("SETTINGS_SIGN_OUT_KEY")!! .onPreferenceClickListener = Preference.OnPreferenceClickListener { activity?.let { - SignOutUiWorker(requireActivity()) - .perform(requireContext()) + SignOutUiWorker(requireActivity()).perform() } false @@ -280,89 +279,38 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() { session.integrationManagerService().removeListener(integrationServiceListener) } - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { - if (allGranted(grantResults)) { - if (requestCode == PERMISSION_REQUEST_CODE_LAUNCH_CAMERA) { - onAvatarTypeSelected(true) + private val attachmentPhotoActivityResultLauncher = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + avatarCameraUri?.let { uri -> + MultiPicker.get(MultiPicker.CAMERA) + .getTakenPhoto(requireContext(), uri) + ?.let { + onAvatarSelected(it) + } } } } + private val attachmentImageActivityResultLauncher = registerStartForActivityResult { activityResult -> + val data = activityResult.data ?: return@registerStartForActivityResult + if (activityResult.resultCode == Activity.RESULT_OK) { + MultiPicker + .get(MultiPicker.IMAGE) + .getSelectedFiles(requireContext(), data) + .firstOrNull()?.let { + onAvatarSelected(it) + } + } + } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + // TODO handle this one (Ucrop lib) + @Suppress("DEPRECATION") super.onActivityResult(requestCode, resultCode, data) if (resultCode == Activity.RESULT_OK) { when (requestCode) { - REQUEST_NEW_PHONE_NUMBER -> refreshPhoneNumbersList() - REQUEST_PHONEBOOK_COUNTRY -> onPhonebookCountryUpdate(data) - MultiPicker.REQUEST_CODE_TAKE_PHOTO -> { - avatarCameraUri?.let { uri -> - MultiPicker.get(MultiPicker.CAMERA) - .getTakenPhoto(requireContext(), requestCode, resultCode, uri) - ?.let { - onAvatarSelected(it) - } - } - } - MultiPicker.REQUEST_CODE_PICK_IMAGE -> { - MultiPicker - .get(MultiPicker.IMAGE) - .getSelectedFiles(requireContext(), requestCode, resultCode, data) - .firstOrNull()?.let { - onAvatarSelected(it) - } - } - UCrop.REQUEST_CROP -> data?.let { onAvatarCropped(UCrop.getOutput(it)) } - /* TODO - VectorUtils.TAKE_IMAGE -> { - val thumbnailUri = VectorUtils.getThumbnailUriFromIntent(activity, data, session.mediaCache) - - if (null != thumbnailUri) { - displayLoadingView() - - val resource = ResourceUtils.openResource(activity, thumbnailUri, null) - - if (null != resource) { - session.mediaCache.uploadContent(resource.mContentStream, null, resource.mMimeType, null, object : MXMediaUploadListener() { - - override fun onUploadError(uploadId: String?, serverResponseCode: Int, serverErrorMessage: String?) { - activity?.runOnUiThread { onCommonDone(serverResponseCode.toString() + " : " + serverErrorMessage) } - } - - override fun onUploadComplete(uploadId: String?, contentUri: String?) { - activity?.runOnUiThread { - session.myUser.updateAvatarUrl(contentUri, object : MatrixCallback { - override fun onSuccess(info: Void?) { - onCommonDone(null) - refreshDisplay() - } - - override fun onNetworkError(e: Exception) { - onCommonDone(e.localizedMessage) - } - - override fun onMatrixError(e: MatrixError) { - if (MatrixError.M_CONSENT_NOT_GIVEN == e.errcode) { - activity?.runOnUiThread { - hideLoadingView() - (activity as VectorAppCompatActivity).consentNotGivenHelper.displayDialog(e) - } - } else { - onCommonDone(e.localizedMessage) - } - } - - override fun onUnexpectedError(e: Exception) { - onCommonDone(e.localizedMessage) - } - }) - } - } - }) - } - } - } - */ + UCrop.REQUEST_CROP -> data?.let { onAvatarCropped(UCrop.getOutput(it)) } } } } @@ -401,13 +349,19 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() { }.show() } + private val takePhotoActivityResultLauncher = registerForPermissionsResult { allGranted -> + if (allGranted) { + onAvatarTypeSelected(true) + } + } + private fun onAvatarTypeSelected(isCamera: Boolean) { if (isCamera) { - if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, this, PERMISSION_REQUEST_CODE_LAUNCH_CAMERA)) { - avatarCameraUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(this) + if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), takePhotoActivityResultLauncher)) { + avatarCameraUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(requireActivity(), attachmentPhotoActivityResultLauncher) } } else { - MultiPicker.get(MultiPicker.IMAGE).single().startWith(this) + MultiPicker.get(MultiPicker.IMAGE).single().startWith(attachmentImageActivityResultLauncher) } } @@ -465,29 +419,10 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() { */ } - private fun onPhonebookCountryUpdate(data: Intent?) { - /* TODO - if (data != null && data.hasExtra(CountryPickerActivity.EXTRA_OUT_COUNTRY_NAME) - && data.hasExtra(CountryPickerActivity.EXTRA_OUT_COUNTRY_CODE)) { - val countryCode = data.getStringExtra(CountryPickerActivity.EXTRA_OUT_COUNTRY_CODE) - if (!TextUtils.equals(countryCode, PhoneNumberUtils.getCountryCode(activity))) { - PhoneNumberUtils.setCountryCode(activity, countryCode) - mContactPhonebookCountryPreference.summary = data.getStringExtra(CountryPickerActivity.EXTRA_OUT_COUNTRY_NAME) - } - } - */ - } - // ============================================================================================================== // Phone number management // ============================================================================================================== - /** - * Refresh phone number list - */ - private fun refreshPhoneNumbersList() { - } - /** * Update the password. */ @@ -647,9 +582,4 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() { }) } } - - companion object { - private const val REQUEST_NEW_PHONE_NUMBER = 456 - private const val REQUEST_PHONEBOOK_COUNTRY = 789 - } } diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsHelpAboutFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsHelpAboutFragment.kt index f011702dca..861e0dea1f 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsHelpAboutFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsHelpAboutFragment.kt @@ -21,7 +21,6 @@ import android.net.Uri import android.provider.Settings import androidx.preference.Preference import im.vector.app.BuildConfig -import org.matrix.android.sdk.api.Matrix import im.vector.app.R import im.vector.app.core.preference.VectorPreference import im.vector.app.core.utils.copyToClipboard @@ -29,6 +28,7 @@ import im.vector.app.core.utils.displayInWebView import im.vector.app.core.utils.openUrlInChromeCustomTab import im.vector.app.features.version.VersionProvider import im.vector.app.openOssLicensesMenuActivity +import org.matrix.android.sdk.api.Matrix import javax.inject.Inject class VectorSettingsHelpAboutFragment @Inject constructor( diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsNotificationPreferenceFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsNotificationPreferenceFragment.kt index d6d9cf6e31..4bee1ac0c8 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsNotificationPreferenceFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsNotificationPreferenceFragment.kt @@ -27,6 +27,7 @@ import androidx.preference.Preference import androidx.preference.SwitchPreference import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.preference.VectorEditTextPreference import im.vector.app.core.preference.VectorPreference import im.vector.app.core.preference.VectorPreferenceCategory @@ -114,6 +115,10 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor( handleSystemPreference() } + private val batteryStartForActivityResult = registerStartForActivityResult { + // Noop + } + // BackgroundSyncModeChooserDialog.InteractionListener override fun onOptionSelected(mode: BackgroundSyncMode) { // option has change, need to act @@ -122,9 +127,7 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor( // Even if using foreground service with foreground notif, it stops to work // in doze mode for certain devices :/ if (!isIgnoringBatteryOptimizations(requireContext())) { - requestDisablingBatteryOptimization(requireActivity(), - this@VectorSettingsNotificationPreferenceFragment, - REQUEST_BATTERY_OPTIMIZATION) + requestDisablingBatteryOptimization(requireActivity(), batteryStartForActivityResult) } } vectorPreferences.setFdroidSyncBackgroundMode(mode) @@ -210,27 +213,22 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor( intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, vectorPreferences.getNotificationRingTone()) } - startActivityForResult(intent, REQUEST_NOTIFICATION_RINGTONE) + ringtoneStartForActivityResult.launch(intent) false } } } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - if (resultCode == Activity.RESULT_OK) { - when (requestCode) { - REQUEST_NOTIFICATION_RINGTONE -> { - vectorPreferences.setNotificationRingTone(data?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI) as Uri?) + private val ringtoneStartForActivityResult = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + vectorPreferences.setNotificationRingTone(activityResult.data?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI) as Uri?) - // test if the selected ring tone can be played - val notificationRingToneName = vectorPreferences.getNotificationRingToneName() - if (null != notificationRingToneName) { - vectorPreferences.setNotificationRingTone(vectorPreferences.getNotificationRingTone()) - findPreference(VectorPreferences.SETTINGS_NOTIFICATION_RINGTONE_SELECTION_PREFERENCE_KEY)!! - .summary = notificationRingToneName - } - } + // test if the selected ring tone can be played + val notificationRingToneName = vectorPreferences.getNotificationRingToneName() + if (null != notificationRingToneName) { + vectorPreferences.setNotificationRingTone(vectorPreferences.getNotificationRingTone()) + findPreference(VectorPreferences.SETTINGS_NOTIFICATION_RINGTONE_SELECTION_PREFERENCE_KEY)!! + .summary = notificationRingToneName } } } @@ -340,9 +338,4 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor( }) } } - - companion object { - private const val REQUEST_NOTIFICATION_RINGTONE = 888 - private const val REQUEST_BATTERY_OPTIMIZATION = 500 - } } diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsNotificationsTroubleshootFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsNotificationsTroubleshootFragment.kt index f07d6471c9..1d8e95a18e 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsNotificationsTroubleshootFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsNotificationsTroubleshootFragment.kt @@ -16,13 +16,16 @@ package im.vector.app.features.settings import android.app.Activity +import android.content.BroadcastReceiver import android.content.Context import android.content.Intent +import android.content.IntentFilter import android.os.Bundle import android.view.View import android.view.ViewGroup import android.widget.Button import android.widget.TextView +import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -30,12 +33,16 @@ import androidx.transition.TransitionManager import butterknife.BindView import im.vector.app.R import im.vector.app.core.extensions.cleanup +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.rageshake.BugReporter import im.vector.app.features.settings.troubleshoot.NotificationTroubleshootTestManager import im.vector.app.features.settings.troubleshoot.TroubleshootTest import im.vector.app.push.fcm.NotificationTroubleshootTestManagerFactory +import org.matrix.android.sdk.api.extensions.orFalse +import org.matrix.android.sdk.api.extensions.tryOrNull import javax.inject.Inject class VectorSettingsNotificationsTroubleshootFragment @Inject constructor( @@ -45,12 +52,16 @@ class VectorSettingsNotificationsTroubleshootFragment @Inject constructor( @BindView(R.id.troubleshoot_test_recycler_view) lateinit var mRecyclerView: RecyclerView + @BindView(R.id.troubleshoot_bottom_view) lateinit var mBottomView: ViewGroup + @BindView(R.id.toubleshoot_summ_description) lateinit var mSummaryDescription: TextView + @BindView(R.id.troubleshoot_summ_button) lateinit var mSummaryButton: Button + @BindView(R.id.troubleshoot_run_button) lateinit var mRunButton: Button @@ -76,28 +87,27 @@ class VectorSettingsNotificationsTroubleshootFragment @Inject constructor( } mRunButton.debouncedClicks { - testManager?.retry() + testManager?.retry(testStartForActivityResult) } startUI() } private fun startUI() { - mSummaryDescription.text = getString(R.string.settings_troubleshoot_diagnostic_running_status, - 0, 0) + mSummaryDescription.text = getString(R.string.settings_troubleshoot_diagnostic_running_status, 0, 0) testManager = testManagerFactory.create(this) testManager?.statusListener = { troubleshootTestManager -> if (isAdded) { TransitionManager.beginDelayedTransition(mBottomView) when (troubleshootTestManager.diagStatus) { - TroubleshootTest.TestStatus.NOT_STARTED -> { + TroubleshootTest.TestStatus.NOT_STARTED -> { mSummaryDescription.text = "" mSummaryButton.visibility = View.GONE mRunButton.visibility = View.VISIBLE } - TroubleshootTest.TestStatus.RUNNING -> { - // Forces int type because it's breaking lint - val size: Int = troubleshootTestManager.testList.size - val currentTestIndex: Int = troubleshootTestManager.currentTestIndex + TroubleshootTest.TestStatus.RUNNING, + TroubleshootTest.TestStatus.WAITING_FOR_USER -> { + val size = troubleshootTestManager.testListSize + val currentTestIndex = troubleshootTestManager.currentTestIndex mSummaryDescription.text = getString( R.string.settings_troubleshoot_diagnostic_running_status, currentTestIndex, @@ -106,17 +116,9 @@ class VectorSettingsNotificationsTroubleshootFragment @Inject constructor( mSummaryButton.visibility = View.GONE mRunButton.visibility = View.GONE } - TroubleshootTest.TestStatus.FAILED -> { + TroubleshootTest.TestStatus.FAILED -> { // check if there are quick fixes - var hasQuickFix = false - testManager?.testList?.let { - for (test in it) { - if (test.status == TroubleshootTest.TestStatus.FAILED && test.quickFix != null) { - hasQuickFix = true - break - } - } - } + val hasQuickFix = testManager?.hasQuickFix().orFalse() if (hasQuickFix) { mSummaryDescription.text = getString(R.string.settings_troubleshoot_diagnostic_failure_status_with_quickfix) } else { @@ -125,7 +127,7 @@ class VectorSettingsNotificationsTroubleshootFragment @Inject constructor( mSummaryButton.visibility = View.VISIBLE mRunButton.visibility = View.VISIBLE } - TroubleshootTest.TestStatus.SUCCESS -> { + TroubleshootTest.TestStatus.SUCCESS -> { mSummaryDescription.text = getString(R.string.settings_troubleshoot_diagnostic_success_status) mSummaryButton.visibility = View.VISIBLE mRunButton.visibility = View.VISIBLE @@ -134,7 +136,7 @@ class VectorSettingsNotificationsTroubleshootFragment @Inject constructor( } } mRecyclerView.adapter = testManager?.adapter - testManager?.runDiagnostic() + testManager?.runDiagnostic(testStartForActivityResult) } override fun onDestroyView() { @@ -142,12 +144,14 @@ class VectorSettingsNotificationsTroubleshootFragment @Inject constructor( super.onDestroyView() } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (resultCode == Activity.RESULT_OK && requestCode == NotificationTroubleshootTestManager.REQ_CODE_FIX) { - testManager?.retry() - return + private val testStartForActivityResult = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + retry() } - super.onActivityResult(requestCode, resultCode, data) + } + + private fun retry() { + testManager?.retry(testStartForActivityResult) } override fun onDetach() { @@ -159,6 +163,39 @@ class VectorSettingsNotificationsTroubleshootFragment @Inject constructor( override fun onResume() { super.onResume() (activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.settings_notification_troubleshoot) + + tryOrNull("Unable to register the receiver") { + LocalBroadcastManager.getInstance(requireContext()) + .registerReceiver(broadcastReceiverPush, IntentFilter(NotificationUtils.PUSH_ACTION)) + } + tryOrNull("Unable to register the receiver") { + LocalBroadcastManager.getInstance(requireContext()) + .registerReceiver(broadcastReceiverNotification, IntentFilter(NotificationUtils.DIAGNOSTIC_ACTION)) + } + } + + override fun onPause() { + super.onPause() + tryOrNull { + LocalBroadcastManager.getInstance(requireContext()) + .unregisterReceiver(broadcastReceiverPush) + } + tryOrNull { + LocalBroadcastManager.getInstance(requireContext()) + .unregisterReceiver(broadcastReceiverNotification) + } + } + + private val broadcastReceiverPush = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + testManager?.onDiagnosticPushReceived() + } + } + + private val broadcastReceiverNotification = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + testManager?.onDiagnosticNotificationClicked() + } } override fun onAttach(context: Context) { diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPinFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPinFragment.kt index 4a6965618e..37465258f6 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPinFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPinFragment.kt @@ -16,14 +16,13 @@ package im.vector.app.features.settings -import android.content.Intent import androidx.lifecycle.lifecycleScope import androidx.preference.Preference import androidx.preference.SwitchPreference import im.vector.app.R +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.features.navigation.Navigator import im.vector.app.features.notifications.NotificationDrawerManager -import im.vector.app.features.pin.PinActivity import im.vector.app.features.pin.PinCodeStore import im.vector.app.features.pin.PinMode import kotlinx.coroutines.launch @@ -67,17 +66,18 @@ class VectorSettingsPinFragment @Inject constructor( refreshPinCodeStatus() } } else { - navigator.openPinCode(this@VectorSettingsPinFragment, PinMode.CREATE) + navigator.openPinCode( + requireContext(), + pinActivityResultLauncher, + PinMode.CREATE + ) } true } } } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - if (requestCode == PinActivity.PIN_REQUEST_CODE) { - refreshPinCodeStatus() - } + private val pinActivityResultLauncher = registerStartForActivityResult { + refreshPinCodeStatus() } } diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsRootFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsRootFragment.kt index 3f3811ce76..79eb0216ee 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsRootFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsRootFragment.kt @@ -16,7 +16,6 @@ package im.vector.app.features.settings -import android.os.Build import im.vector.app.R import im.vector.app.core.preference.VectorPreference import javax.inject.Inject @@ -27,10 +26,7 @@ class VectorSettingsRootFragment @Inject constructor() : VectorSettingsBaseFragm override val preferenceXmlRes = R.xml.vector_settings_root override fun bindPref() { - // Tint icon on API < 24 - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { - tintIcons() - } + tintIcons() } private fun tintIcons() { diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt index 44cef8f974..6dd35a7d1d 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt @@ -39,6 +39,7 @@ import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.dialogs.ExportKeysDialog import im.vector.app.core.extensions.queryExportKeys +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.showPassword import im.vector.app.core.intent.ExternalIntentData import im.vector.app.core.intent.analyseIntent @@ -53,8 +54,8 @@ import im.vector.app.features.crypto.keys.KeysExporter import im.vector.app.features.crypto.keys.KeysImporter import im.vector.app.features.crypto.keysbackup.settings.KeysBackupManageActivity import im.vector.app.features.crypto.recover.BootstrapBottomSheet +import im.vector.app.features.crypto.recover.SetupMode import im.vector.app.features.navigation.Navigator -import im.vector.app.features.pin.PinActivity import im.vector.app.features.pin.PinCodeStore import im.vector.app.features.pin.PinMode import im.vector.app.features.raw.wellknown.getElementWellknown @@ -193,7 +194,7 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor( secureBackupCategory.isVisible = true secureBackupPreference.title = getString(R.string.settings_secure_backup_setup) secureBackupPreference.onPreferenceClickListener = Preference.OnPreferenceClickListener { - BootstrapBottomSheet.show(parentFragmentManager, initCrossSigningOnly = false, forceReset4S = false) + BootstrapBottomSheet.show(parentFragmentManager, SetupMode.NORMAL) true } } else { @@ -212,7 +213,7 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor( secureBackupCategory.isVisible = true secureBackupPreference.title = getString(R.string.settings_secure_backup_reset) secureBackupPreference.onPreferenceClickListener = Preference.OnPreferenceClickListener { - BootstrapBottomSheet.show(parentFragmentManager, initCrossSigningOnly = false, forceReset4S = true) + BootstrapBottomSheet.show(parentFragmentManager, SetupMode.PASSPHRASE_RESET) true } } else if (!info.megolmSecretKnown) { @@ -319,48 +320,46 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor( } } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - if (resultCode == Activity.RESULT_OK) { - when (requestCode) { - REQUEST_CODE_SAVE_MEGOLM_EXPORT -> { - val uri = data?.data - if (uri != null) { - activity?.let { activity -> - ExportKeysDialog().show(activity, object : ExportKeysDialog.ExportKeyDialogListener { - override fun onPassphrase(passphrase: String) { - displayLoadingView() + private val saveMegolmStartForActivityResult = registerStartForActivityResult { + val uri = it.data?.data ?: return@registerStartForActivityResult + if (it.resultCode == Activity.RESULT_OK) { + ExportKeysDialog().show(requireActivity(), object : ExportKeysDialog.ExportKeyDialogListener { + override fun onPassphrase(passphrase: String) { + displayLoadingView() - KeysExporter(session) - .export(requireContext(), - passphrase, - uri, - object : MatrixCallback { - override fun onSuccess(data: Boolean) { - if (data) { - requireActivity().toast(getString(R.string.encryption_exported_successfully)) - } else { - requireActivity().toast(getString(R.string.unexpected_error)) - } - hideLoadingView() - } + KeysExporter(session) + .export(requireContext(), + passphrase, + uri, + object : MatrixCallback { + override fun onSuccess(data: Boolean) { + if (data) { + requireActivity().toast(getString(R.string.encryption_exported_successfully)) + } else { + requireActivity().toast(getString(R.string.unexpected_error)) + } + hideLoadingView() + } - override fun onFailure(failure: Throwable) { - onCommonDone(failure.localizedMessage) - } - }) - } - }) - } - } + override fun onFailure(failure: Throwable) { + onCommonDone(failure.localizedMessage) + } + }) } - PinActivity.PIN_REQUEST_CODE -> { - doOpenPinCodePreferenceScreen() - } - REQUEST_E2E_FILE_REQUEST_CODE -> { - importKeys(data) - } - } + }) + } + } + + private val pinActivityResultLauncher = registerStartForActivityResult { + if (it.resultCode == Activity.RESULT_OK) { + doOpenPinCodePreferenceScreen() + } + } + + private val importKeysActivityResultLauncher = registerStartForActivityResult { + val data = it.data ?: return@registerStartForActivityResult + if (it.resultCode == Activity.RESULT_OK) { + importKeys(data) } } @@ -368,7 +367,10 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor( lifecycleScope.launchWhenResumed { val hasPinCode = pinCodeStore.hasEncodedPin() if (hasPinCode) { - navigator.openPinCode(this@VectorSettingsSecurityPrivacyFragment, PinMode.AUTH) + navigator.openPinCode( + requireContext(), + pinActivityResultLauncher, + PinMode.AUTH) } else { doOpenPinCodePreferenceScreen() } @@ -390,7 +392,7 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor( } exportPref.onPreferenceClickListener = Preference.OnPreferenceClickListener { - queryExportKeys(activeSessionHolder.getSafeActiveSession()?.myUserId ?: "", REQUEST_CODE_SAVE_MEGOLM_EXPORT) + queryExportKeys(activeSessionHolder.getSafeActiveSession()?.myUserId ?: "", saveMegolmStartForActivityResult) true } @@ -405,7 +407,12 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor( */ @SuppressLint("NewApi") private fun importKeys() { - activity?.let { openFileSelection(it, this, false, REQUEST_E2E_FILE_REQUEST_CODE) } + openFileSelection( + requireActivity(), + importKeysActivityResultLauncher, + false, + 0 + ) } /** @@ -413,12 +420,7 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor( * * @param intent the intent result */ - private fun importKeys(intent: Intent?) { - // sanity check - if (null == intent) { - return - } - + private fun importKeys(intent: Intent) { val sharedDataItems = analyseIntent(intent) val thisActivity = activity @@ -426,7 +428,7 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor( val sharedDataItem = sharedDataItems[0] val uri = when (sharedDataItem) { - is ExternalIntentData.IntentDataUri -> sharedDataItem.uri + is ExternalIntentData.IntentDataUri -> sharedDataItem.uri is ExternalIntentData.IntentDataClipData -> sharedDataItem.clipDataItem.uri else -> null } @@ -604,9 +606,4 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor( } }) } - - companion object { - private const val REQUEST_E2E_FILE_REQUEST_CODE = 123 - private const val REQUEST_CODE_SAVE_MEGOLM_EXPORT = 124 - } } diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsVoiceVideoFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsVoiceVideoFragment.kt index dba3c78220..05d1a1f60f 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsVoiceVideoFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsVoiceVideoFragment.kt @@ -23,6 +23,7 @@ import android.net.Uri import androidx.preference.Preference import androidx.preference.SwitchPreference import im.vector.app.R +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.preference.VectorPreference import im.vector.app.core.utils.getCallRingtoneName import im.vector.app.core.utils.getCallRingtoneUri @@ -57,19 +58,13 @@ class VectorSettingsVoiceVideoFragment : VectorSettingsBaseFragment() { } } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - - if (resultCode == Activity.RESULT_OK) { - when (requestCode) { - REQUEST_CALL_RINGTONE -> { - val callRingtoneUri: Uri? = data?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI) - val thisActivity = activity - if (callRingtoneUri != null && thisActivity != null) { - setCallRingtoneUri(thisActivity, callRingtoneUri) - mCallRingtonePreference.summary = getCallRingtoneName(thisActivity) - } - } + private val ringtoneStartForActivityResult = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + val callRingtoneUri: Uri? = activityResult.data?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI) + val thisActivity = activity + if (callRingtoneUri != null && thisActivity != null) { + setCallRingtoneUri(thisActivity, callRingtoneUri) + mCallRingtonePreference.summary = getCallRingtoneName(thisActivity) } } } @@ -82,10 +77,6 @@ class VectorSettingsVoiceVideoFragment : VectorSettingsBaseFragment() { putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_RINGTONE) activity?.let { putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, getCallRingtoneUri(it)) } } - startActivityForResult(intent, REQUEST_CALL_RINGTONE) - } - - companion object { - private const val REQUEST_CALL_RINGTONE = 999 + ringtoneStartForActivityResult.launch(intent) } } diff --git a/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewModel.kt index 56bd50d057..e08147d54f 100644 --- a/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewModel.kt @@ -21,12 +21,12 @@ import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.failure.isInvalidPassword -import org.matrix.android.sdk.api.session.Session import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModelAction +import org.matrix.android.sdk.api.MatrixCallback +import org.matrix.android.sdk.api.failure.isInvalidPassword +import org.matrix.android.sdk.api.session.Session data class DeactivateAccountViewState( val passwordShown: Boolean = false diff --git a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsFragment.kt index 711819f764..ebeac5aca1 100644 --- a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsFragment.kt @@ -41,8 +41,9 @@ class CrossSigningSettingsFragment @Inject constructor( private val viewModel: CrossSigningSettingsViewModel by fragmentViewModel() - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupRecyclerView() viewModel.observeViewEvents { when (it) { is CrossSigningSettingsViewEvents.Failure -> { @@ -62,11 +63,6 @@ class CrossSigningSettingsFragment @Inject constructor( (activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.encryption_information_cross_signing_state) } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - setupRecyclerView() - } - override fun invalidate() = withState(viewModel) { state -> controller.setData(state) } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceItem.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceItem.kt index 7affdbf25b..49840cdd64 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceItem.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceItem.kt @@ -23,14 +23,14 @@ import android.widget.TextView import androidx.core.view.isVisible import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass -import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel -import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo import im.vector.app.R import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.resources.ColorProvider import im.vector.app.core.utils.DimensionConverter import me.gujun.android.span.span +import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel +import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo /** * A list item for Device. diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheet.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheet.kt index c8b12e26a5..dbaa99e1df 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheet.kt @@ -17,6 +17,7 @@ package im.vector.app.features.settings.devices import android.os.Bundle import android.os.Parcelable +import android.view.View import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView import butterknife.BindView @@ -58,8 +59,8 @@ class DeviceVerificationInfoBottomSheet : VectorBaseBottomSheetDialogFragment(), override fun getLayoutResId() = R.layout.bottom_sheet_generic_list_with_title - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) recyclerView.configureWith( controller, showDivider = false, diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesAction.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesAction.kt index 98fed61340..2b0991ab4e 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesAction.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesAction.kt @@ -16,8 +16,8 @@ package im.vector.app.features.settings.devices -import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import im.vector.app.core.platform.VectorViewModelAction +import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo sealed class DevicesAction : VectorViewModelAction { object Refresh : DevicesAction() diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewEvents.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewEvents.kt index 14a4b32be2..60d7491603 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewEvents.kt @@ -17,10 +17,10 @@ package im.vector.app.features.settings.devices +import im.vector.app.core.platform.VectorViewEvents import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo -import im.vector.app.core.platform.VectorViewEvents /** * Transient events for Ignored users screen diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/TrustUtils.kt b/vector/src/main/java/im/vector/app/features/settings/devices/TrustUtils.kt index 54a81883f3..80cd91664d 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/TrustUtils.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/TrustUtils.kt @@ -17,8 +17,8 @@ package im.vector.app.features.settings.devices import androidx.annotation.DrawableRes -import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel import im.vector.app.R +import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel object TrustUtils { diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsEpoxyController.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsEpoxyController.kt index 66bf6d4ff4..cf93bc14e7 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsEpoxyController.kt @@ -21,15 +21,6 @@ import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized -import org.matrix.android.sdk.api.session.events.model.Event -import org.matrix.android.sdk.api.session.events.model.EventType -import org.matrix.android.sdk.api.session.events.model.toModel -import org.matrix.android.sdk.internal.crypto.model.event.OlmEventContent -import org.matrix.android.sdk.internal.crypto.model.event.SecretSendEventContent -import org.matrix.android.sdk.internal.crypto.model.rest.ForwardedRoomKeyContent -import org.matrix.android.sdk.internal.crypto.model.rest.GossipingToDeviceObject -import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyShareRequest -import org.matrix.android.sdk.internal.crypto.model.rest.SecretShareRequest import im.vector.app.R import im.vector.app.core.date.DateFormatKind import im.vector.app.core.date.VectorDateFormatter @@ -42,6 +33,15 @@ import im.vector.app.core.ui.list.genericFooterItem import im.vector.app.core.ui.list.genericItem import im.vector.app.core.ui.list.genericItemHeader import me.gujun.android.span.span +import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.events.model.EventType +import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.internal.crypto.model.event.OlmEventContent +import org.matrix.android.sdk.internal.crypto.model.event.SecretSendEventContent +import org.matrix.android.sdk.internal.crypto.model.rest.ForwardedRoomKeyContent +import org.matrix.android.sdk.internal.crypto.model.rest.GossipingToDeviceObject +import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyShareRequest +import org.matrix.android.sdk.internal.crypto.model.rest.SecretShareRequest import javax.inject.Inject class GossipingEventsEpoxyController @Inject constructor( diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailFragment.kt index f105a35021..e2c855a9e3 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailFragment.kt @@ -20,7 +20,6 @@ import android.os.Bundle import android.view.View import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState -import org.matrix.android.sdk.api.session.events.model.Event import im.vector.app.R import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith @@ -29,6 +28,7 @@ import im.vector.app.core.resources.ColorProvider import im.vector.app.core.utils.createJSonViewerStyleProvider import kotlinx.android.synthetic.main.fragment_generic_recycler.* import org.billcarsonfr.jsonviewer.JSonViewerDialog +import org.matrix.android.sdk.api.session.events.model.Event import javax.inject.Inject class GossipingEventsPaperTrailFragment @Inject constructor( diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt index 7a2700abb7..d903725b22 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt @@ -27,12 +27,12 @@ import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject -import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.events.model.Event import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.events.model.Event data class GossipingEventsPaperTrailState( val events: Async> = Uninitialized diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt index 3a6382a023..22093763e1 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt @@ -26,13 +26,13 @@ import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject -import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.internal.crypto.IncomingRoomKeyRequest -import org.matrix.android.sdk.internal.crypto.OutgoingRoomKeyRequest import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.internal.crypto.IncomingRoomKeyRequest +import org.matrix.android.sdk.internal.crypto.OutgoingRoomKeyRequest data class KeyRequestListViewState( val incomingRequests: Async> = Uninitialized, diff --git a/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersController.kt b/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersController.kt index 2a2fb21a7d..8080f2e032 100644 --- a/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersController.kt @@ -17,12 +17,12 @@ package im.vector.app.features.settings.ignored import com.airbnb.epoxy.EpoxyController -import org.matrix.android.sdk.api.session.user.model.User -import org.matrix.android.sdk.api.util.toMatrixItem import im.vector.app.R import im.vector.app.core.epoxy.noResultItem import im.vector.app.core.resources.StringProvider import im.vector.app.features.home.AvatarRenderer +import org.matrix.android.sdk.api.session.user.model.User +import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject class IgnoredUsersController @Inject constructor(private val stringProvider: StringProvider, diff --git a/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt index cc5c8b436c..98c9f8e4f1 100644 --- a/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt @@ -27,11 +27,11 @@ import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject +import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.platform.VectorViewModelAction import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.user.model.User -import im.vector.app.core.platform.VectorViewModel -import im.vector.app.core.platform.VectorViewModelAction import org.matrix.android.sdk.rx.rx data class IgnoredUsersViewState( diff --git a/vector/src/main/java/im/vector/app/features/settings/ignored/UserItem.kt b/vector/src/main/java/im/vector/app/features/settings/ignored/UserItem.kt index 3349acef10..286ebbb2a7 100644 --- a/vector/src/main/java/im/vector/app/features/settings/ignored/UserItem.kt +++ b/vector/src/main/java/im/vector/app/features/settings/ignored/UserItem.kt @@ -20,12 +20,12 @@ import android.widget.ImageView import android.widget.TextView import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass -import org.matrix.android.sdk.api.util.MatrixItem import im.vector.app.R import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.extensions.setTextOrHide import im.vector.app.features.home.AvatarRenderer +import org.matrix.android.sdk.api.util.MatrixItem /** * A list item for User. diff --git a/vector/src/main/java/im/vector/app/features/settings/push/PushGatewayItem.kt b/vector/src/main/java/im/vector/app/features/settings/push/PushGatewayItem.kt index 266bcc39c6..dc66e1983b 100644 --- a/vector/src/main/java/im/vector/app/features/settings/push/PushGatewayItem.kt +++ b/vector/src/main/java/im/vector/app/features/settings/push/PushGatewayItem.kt @@ -20,9 +20,9 @@ import android.widget.TextView import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass import com.airbnb.epoxy.EpoxyModelWithHolder -import org.matrix.android.sdk.api.session.pushers.Pusher import im.vector.app.R import im.vector.app.core.epoxy.VectorEpoxyHolder +import org.matrix.android.sdk.api.session.pushers.Pusher @EpoxyModelClass(layout = R.layout.item_pushgateway) abstract class PushGatewayItem : EpoxyModelWithHolder() { diff --git a/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysViewModel.kt index b1b2c43f4e..2c8b902188 100644 --- a/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysViewModel.kt @@ -24,12 +24,12 @@ import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject -import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.pushers.Pusher -import org.matrix.android.sdk.rx.RxSession import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.pushers.Pusher +import org.matrix.android.sdk.rx.RxSession data class PushGatewayViewState( val pushGateways: Async> = Uninitialized diff --git a/vector/src/main/java/im/vector/app/features/settings/push/PushRulesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/push/PushRulesViewModel.kt index ce42cbb12f..64ddc275eb 100644 --- a/vector/src/main/java/im/vector/app/features/settings/push/PushRulesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/push/PushRulesViewModel.kt @@ -18,11 +18,11 @@ package im.vector.app.features.settings.push import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext -import org.matrix.android.sdk.api.pushrules.rest.PushRule import im.vector.app.core.di.HasScreenInjector import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel +import org.matrix.android.sdk.api.pushrules.rest.PushRule data class PushRulesViewState( val rules: List = emptyList() diff --git a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsController.kt b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsController.kt index f07e31bb2e..cf811f1611 100644 --- a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsController.kt @@ -83,7 +83,7 @@ class ThreePidsSettingsController @Inject constructor( loadingText(stringProvider.getString(R.string.loading)) } } - is Fail -> { + is Fail -> { genericFooterItem { id("fail") text(data.threePids.error.localizedMessage) @@ -243,7 +243,7 @@ class ThreePidsSettingsController @Inject constructor( } when (threePid) { - is ThreePid.Email -> { + is ThreePid.Email -> { settingsInformationItem { id("info" + idPrefix + threePid.value) message(stringProvider.getString(R.string.account_email_validation_message)) diff --git a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsFragment.kt index 519b7132bf..81033281d8 100644 --- a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsFragment.kt @@ -59,7 +59,7 @@ class ThreePidsSettingsFragment @Inject constructor( viewModel.observeViewEvents { when (it) { - is ThreePidsSettingsViewEvents.Failure -> displayErrorDialog(it.throwable) + is ThreePidsSettingsViewEvents.Failure -> displayErrorDialog(it.throwable) ThreePidsSettingsViewEvents.RequestPassword -> askUserPassword() }.exhaustive } diff --git a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt index 2001c85a2e..41b6146bb1 100644 --- a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt @@ -141,13 +141,13 @@ class ThreePidsSettingsViewModel @AssistedInject constructor( override fun handle(action: ThreePidsSettingsAction) { when (action) { - is ThreePidsSettingsAction.AddThreePid -> handleAddThreePid(action) + is ThreePidsSettingsAction.AddThreePid -> handleAddThreePid(action) is ThreePidsSettingsAction.ContinueThreePid -> handleContinueThreePid(action) - is ThreePidsSettingsAction.SubmitCode -> handleSubmitCode(action) - is ThreePidsSettingsAction.CancelThreePid -> handleCancelThreePid(action) - is ThreePidsSettingsAction.AccountPassword -> handleAccountPassword(action) - is ThreePidsSettingsAction.DeleteThreePid -> handleDeleteThreePid(action) - is ThreePidsSettingsAction.ChangeUiState -> handleChangeUiState(action) + is ThreePidsSettingsAction.SubmitCode -> handleSubmitCode(action) + is ThreePidsSettingsAction.CancelThreePid -> handleCancelThreePid(action) + is ThreePidsSettingsAction.AccountPassword -> handleAccountPassword(action) + is ThreePidsSettingsAction.DeleteThreePid -> handleDeleteThreePid(action) + is ThreePidsSettingsAction.ChangeUiState -> handleChangeUiState(action) }.exhaustive } diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootRecyclerViewAdapter.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootRecyclerViewAdapter.kt index 1ccd7b6735..5b96bcb254 100644 --- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootRecyclerViewAdapter.kt +++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootRecyclerViewAdapter.kt @@ -51,12 +51,16 @@ class NotificationTroubleshootRecyclerViewAdapter(val tests: ArrayList { + TroubleshootTest.TestStatus.NOT_STARTED -> { titleText.setTextColor(ThemeUtils.getColor(context, R.attr.riotx_text_secondary)) progressBar.visibility = View.INVISIBLE statusIconImage.visibility = View.VISIBLE statusIconImage.setImageResource(R.drawable.unit_test) } - TroubleshootTest.TestStatus.RUNNING -> { + TroubleshootTest.TestStatus.WAITING_FOR_USER -> { + progressBar.visibility = View.INVISIBLE + statusIconImage.visibility = View.VISIBLE + val infoColor = ContextCompat.getColor(context, R.color.vector_info_color) + val drawable = ContextCompat.getDrawable(itemView.context, R.drawable.ic_notification_privacy_warning)?.apply { + ThemeUtils.tintDrawableWithColor(this, infoColor) + } + statusIconImage.setImageDrawable(drawable) + descriptionText.setTextColor(infoColor) + } + TroubleshootTest.TestStatus.RUNNING -> { progressBar.visibility = View.VISIBLE statusIconImage.visibility = View.INVISIBLE } - TroubleshootTest.TestStatus.FAILED -> { + TroubleshootTest.TestStatus.FAILED -> { progressBar.visibility = View.INVISIBLE statusIconImage.visibility = View.VISIBLE statusIconImage.setImageResource(R.drawable.unit_test_ko) @@ -90,7 +104,7 @@ class NotificationTroubleshootRecyclerViewAdapter(val tests: ArrayList { + TroubleshootTest.TestStatus.SUCCESS -> { progressBar.visibility = View.INVISIBLE statusIconImage.visibility = View.VISIBLE statusIconImage.setImageResource(R.drawable.unit_test_ok) diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootTestManager.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootTestManager.kt index 92e8eecb4d..7e7ca57243 100644 --- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootTestManager.kt +++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootTestManager.kt @@ -15,19 +15,27 @@ */ package im.vector.app.features.settings.troubleshoot +import android.content.Intent import android.os.Handler import android.os.Looper +import androidx.activity.result.ActivityResultLauncher import androidx.fragment.app.Fragment import kotlin.properties.Delegates class NotificationTroubleshootTestManager(val fragment: Fragment) { + private val testList = ArrayList() + + val testListSize: Int + get() = testList.size - val testList = ArrayList() var isCancelled = false + private set var currentTestIndex by Delegates.observable(0) { _, _, _ -> statusListener?.invoke(this) } + private set + val adapter = NotificationTroubleshootRecyclerViewAdapter(testList) var statusListener: ((NotificationTroubleshootTestManager) -> Unit)? = null @@ -35,13 +43,14 @@ class NotificationTroubleshootTestManager(val fragment: Fragment) { var diagStatus: TroubleshootTest.TestStatus by Delegates.observable(TroubleshootTest.TestStatus.NOT_STARTED) { _, _, _ -> statusListener?.invoke(this) } + private set fun addTest(test: TroubleshootTest) { testList.add(test) test.manager = this } - fun runDiagnostic() { + fun runDiagnostic(activityResultLauncher: ActivityResultLauncher) { if (isCancelled) return currentTestIndex = 0 val handler = Handler(Looper.getMainLooper()) @@ -60,7 +69,7 @@ class NotificationTroubleshootTestManager(val fragment: Fragment) { // Cosmetic: Start with a small delay for UI/UX reason (better animation effect) for non async tests handler.postDelayed({ if (fragment.isAdded) { - troubleshootTest.perform() + troubleshootTest.perform(activityResultLauncher) } }, 600) } else { @@ -72,28 +81,36 @@ class NotificationTroubleshootTestManager(val fragment: Fragment) { } } if (fragment.isAdded) { - testList.firstOrNull()?.perform() + testList.firstOrNull()?.perform(activityResultLauncher) } } - fun retry() { - for (test in testList) { - test.cancel() - test.description = null - test.quickFix = null - test.status = TroubleshootTest.TestStatus.NOT_STARTED + fun retry(activityResultLauncher: ActivityResultLauncher) { + testList.forEach { + it.cancel() + it.description = null + it.quickFix = null + it.status = TroubleshootTest.TestStatus.NOT_STARTED + } + runDiagnostic(activityResultLauncher) + } + + fun hasQuickFix(): Boolean { + return testList.any { test -> + test.status == TroubleshootTest.TestStatus.FAILED && test.quickFix != null } - runDiagnostic() } fun cancel() { isCancelled = true - for (test in testList) { - test.cancel() - } + testList.forEach { it.cancel() } } - companion object { - const val REQ_CODE_FIX = 9099 + fun onDiagnosticPushReceived() { + testList.forEach { it.onPushReceived() } + } + + fun onDiagnosticNotificationClicked() { + testList.forEach { it.onNotificationClicked() } } } diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestAccountSettings.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestAccountSettings.kt index df1ff80b31..0c3390d0b0 100644 --- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestAccountSettings.kt +++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestAccountSettings.kt @@ -15,12 +15,14 @@ */ package im.vector.app.features.settings.troubleshoot -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.pushrules.RuleIds -import org.matrix.android.sdk.api.pushrules.RuleKind +import android.content.Intent +import androidx.activity.result.ActivityResultLauncher import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.resources.StringProvider +import org.matrix.android.sdk.api.MatrixCallback +import org.matrix.android.sdk.api.pushrules.RuleIds +import org.matrix.android.sdk.api.pushrules.RuleKind import javax.inject.Inject /** @@ -30,7 +32,7 @@ class TestAccountSettings @Inject constructor(private val stringProvider: String private val activeSessionHolder: ActiveSessionHolder) : TroubleshootTest(R.string.settings_troubleshoot_test_account_settings_title) { - override fun perform() { + override fun perform(activityResultLauncher: ActivityResultLauncher) { val session = activeSessionHolder.getSafeActiveSession() ?: return val defaultRule = session.getPushRules().getAllRules() .find { it.ruleId == RuleIds.RULE_ID_DISABLE_ALL } @@ -47,15 +49,15 @@ class TestAccountSettings @Inject constructor(private val stringProvider: String if (manager?.diagStatus == TestStatus.RUNNING) return // wait before all is finished session.updatePushRuleEnableStatus(RuleKind.OVERRIDE, defaultRule, !defaultRule.enabled, - object : MatrixCallback { - override fun onSuccess(data: Unit) { - manager?.retry() - } + object : MatrixCallback { + override fun onSuccess(data: Unit) { + manager?.retry(activityResultLauncher) + } - override fun onFailure(failure: Throwable) { - manager?.retry() - } - }) + override fun onFailure(failure: Throwable) { + manager?.retry(activityResultLauncher) + } + }) } } status = TestStatus.FAILED diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestDeviceSettings.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestDeviceSettings.kt index 61c2fb120e..0d661e8b16 100644 --- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestDeviceSettings.kt +++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestDeviceSettings.kt @@ -15,6 +15,8 @@ */ package im.vector.app.features.settings.troubleshoot +import android.content.Intent +import androidx.activity.result.ActivityResultLauncher import im.vector.app.R import im.vector.app.core.resources.StringProvider import im.vector.app.features.settings.VectorPreferences @@ -27,7 +29,7 @@ class TestDeviceSettings @Inject constructor(private val vectorPreferences: Vect private val stringProvider: StringProvider) : TroubleshootTest(R.string.settings_troubleshoot_test_device_settings_title) { - override fun perform() { + override fun perform(activityResultLauncher: ActivityResultLauncher) { if (vectorPreferences.areNotificationEnabledForDevice()) { description = stringProvider.getString(R.string.settings_troubleshoot_test_device_settings_success) quickFix = null @@ -36,7 +38,7 @@ class TestDeviceSettings @Inject constructor(private val vectorPreferences: Vect quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_device_settings_quickfix) { override fun doFix() { vectorPreferences.setNotificationEnabledForDevice(true) - manager?.retry() + manager?.retry(activityResultLauncher) } } description = stringProvider.getString(R.string.settings_troubleshoot_test_device_settings_failed) diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestNotification.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestNotification.kt new file mode 100644 index 0000000000..6f25ecfe39 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestNotification.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2018 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.app.features.settings.troubleshoot + +import android.content.Context +import android.content.Intent +import androidx.activity.result.ActivityResultLauncher +import im.vector.app.R +import im.vector.app.core.resources.StringProvider +import im.vector.app.core.utils.startNotificationSettingsIntent +import im.vector.app.features.notifications.NotificationUtils +import javax.inject.Inject + +/** + * Checks if notifications can be displayed and clicked by the user + */ +class TestNotification @Inject constructor(private val context: Context, + private val notificationUtils: NotificationUtils, + private val stringProvider: StringProvider) + : TroubleshootTest(R.string.settings_troubleshoot_test_notification_title) { + + override fun perform(activityResultLauncher: ActivityResultLauncher) { + // Display the notification right now + notificationUtils.displayDiagnosticNotification() + description = stringProvider.getString(R.string.settings_troubleshoot_test_notification_notice) + + quickFix = object : TroubleshootQuickFix(R.string.open_settings) { + override fun doFix() { + startNotificationSettingsIntent(context, activityResultLauncher) + } + } + + status = TestStatus.WAITING_FOR_USER + } + + override fun onNotificationClicked() { + description = stringProvider.getString(R.string.settings_troubleshoot_test_notification_notification_clicked) + quickFix = null + status = TestStatus.SUCCESS + } +} diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestNotificationReceiver.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestNotificationReceiver.kt new file mode 100644 index 0000000000..7dec870d3d --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestNotificationReceiver.kt @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.settings.troubleshoot + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import androidx.localbroadcastmanager.content.LocalBroadcastManager + +class TestNotificationReceiver : BroadcastReceiver() { + + override fun onReceive(context: Context, intent: Intent) { + // Internal broadcast to any one interested + LocalBroadcastManager.getInstance(context).sendBroadcast(intent) + } +} diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestPushRulesSettings.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestPushRulesSettings.kt index 22b2331449..8dcf9ab6ce 100644 --- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestPushRulesSettings.kt +++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestPushRulesSettings.kt @@ -15,12 +15,14 @@ */ package im.vector.app.features.settings.troubleshoot -import org.matrix.android.sdk.api.pushrules.RuleIds -import org.matrix.android.sdk.api.pushrules.getActions +import android.content.Intent +import androidx.activity.result.ActivityResultLauncher import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.resources.StringProvider import im.vector.app.features.notifications.toNotificationAction +import org.matrix.android.sdk.api.pushrules.RuleIds +import org.matrix.android.sdk.api.pushrules.getActions import javax.inject.Inject class TestPushRulesSettings @Inject constructor(private val activeSessionHolder: ActiveSessionHolder, @@ -38,7 +40,7 @@ class TestPushRulesSettings @Inject constructor(private val activeSessionHolder: R.string.settings_messages_in_one_to_one, R.string.settings_messages_in_group_chat) - override fun perform() { + override fun perform(activityResultLauncher: ActivityResultLauncher) { val session = activeSessionHolder.getSafeActiveSession() ?: return val pushRules = session.getPushRules().getAllRules() var oneOrMoreRuleIsOff = false diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestSystemSettings.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestSystemSettings.kt index 92eae017f9..ee652288be 100644 --- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestSystemSettings.kt +++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestSystemSettings.kt @@ -15,7 +15,9 @@ */ package im.vector.app.features.settings.troubleshoot -import androidx.appcompat.app.AppCompatActivity +import android.content.Context +import android.content.Intent +import androidx.activity.result.ActivityResultLauncher import androidx.core.app.NotificationManagerCompat import im.vector.app.R import im.vector.app.core.resources.StringProvider @@ -25,11 +27,11 @@ import javax.inject.Inject /** * Checks if notifications are enable in the system settings for this app. */ -class TestSystemSettings @Inject constructor(private val context: AppCompatActivity, +class TestSystemSettings @Inject constructor(private val context: Context, private val stringProvider: StringProvider) : TroubleshootTest(R.string.settings_troubleshoot_test_system_settings_title) { - override fun perform() { + override fun perform(activityResultLauncher: ActivityResultLauncher) { if (NotificationManagerCompat.from(context).areNotificationsEnabled()) { description = stringProvider.getString(R.string.settings_troubleshoot_test_system_settings_success) quickFix = null @@ -39,7 +41,7 @@ class TestSystemSettings @Inject constructor(private val context: AppCompatActiv quickFix = object : TroubleshootQuickFix(R.string.open_settings) { override fun doFix() { if (manager?.diagStatus == TestStatus.RUNNING) return // wait before all is finished - startNotificationSettingsIntent(context, NotificationTroubleshootTestManager.REQ_CODE_FIX) + startNotificationSettingsIntent(context, activityResultLauncher) } } status = TestStatus.FAILED diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TroubleshootTest.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TroubleshootTest.kt index 7abec31ae4..76ba2378a0 100644 --- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TroubleshootTest.kt +++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TroubleshootTest.kt @@ -15,6 +15,8 @@ */ package im.vector.app.features.settings.troubleshoot +import android.content.Intent +import androidx.activity.result.ActivityResultLauncher import androidx.annotation.StringRes import kotlin.properties.Delegates @@ -23,6 +25,7 @@ abstract class TroubleshootTest(@StringRes val titleResId: Int) { enum class TestStatus { NOT_STARTED, RUNNING, + WAITING_FOR_USER, FAILED, SUCCESS } @@ -37,7 +40,7 @@ abstract class TroubleshootTest(@StringRes val titleResId: Int) { var manager: NotificationTroubleshootTestManager? = null - abstract fun perform() + abstract fun perform(activityResultLauncher: ActivityResultLauncher) fun isFinished(): Boolean = (status == TestStatus.FAILED || status == TestStatus.SUCCESS) @@ -49,4 +52,10 @@ abstract class TroubleshootTest(@StringRes val titleResId: Int) { open fun cancel() { } + + open fun onPushReceived() { + } + + open fun onNotificationClicked() { + } } diff --git a/vector/src/main/java/im/vector/app/features/share/IncomingShareAction.kt b/vector/src/main/java/im/vector/app/features/share/IncomingShareAction.kt index eef4e32dff..4e9f024b15 100644 --- a/vector/src/main/java/im/vector/app/features/share/IncomingShareAction.kt +++ b/vector/src/main/java/im/vector/app/features/share/IncomingShareAction.kt @@ -16,12 +16,13 @@ package im.vector.app.features.share -import org.matrix.android.sdk.api.session.room.model.RoomSummary import im.vector.app.core.platform.VectorViewModelAction +import org.matrix.android.sdk.api.session.room.model.RoomSummary sealed class IncomingShareAction : VectorViewModelAction { data class SelectRoom(val roomSummary: RoomSummary, val enableMultiSelect: Boolean) : IncomingShareAction() object ShareToSelectedRooms : IncomingShareAction() + data class ShareToRoom(val roomSummary: RoomSummary) : IncomingShareAction() data class ShareMedia(val keepOriginalSize: Boolean) : IncomingShareAction() data class FilterWith(val filter: String) : IncomingShareAction() data class UpdateSharedData(val sharedData: SharedData) : IncomingShareAction() diff --git a/vector/src/main/java/im/vector/app/features/share/IncomingShareController.kt b/vector/src/main/java/im/vector/app/features/share/IncomingShareController.kt index 912f4c50d7..35c6de24c7 100644 --- a/vector/src/main/java/im/vector/app/features/share/IncomingShareController.kt +++ b/vector/src/main/java/im/vector/app/features/share/IncomingShareController.kt @@ -18,12 +18,12 @@ package im.vector.app.features.share import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.mvrx.Incomplete -import org.matrix.android.sdk.api.session.room.model.RoomSummary import im.vector.app.R import im.vector.app.core.epoxy.loadingItem import im.vector.app.core.epoxy.noResultItem import im.vector.app.core.resources.StringProvider import im.vector.app.features.home.room.list.RoomSummaryItemFactory +import org.matrix.android.sdk.api.session.room.model.RoomSummary import javax.inject.Inject class IncomingShareController @Inject constructor(private val roomSummaryItemFactory: RoomSummaryItemFactory, diff --git a/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt b/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt index a75cfa4304..92e383f8d1 100644 --- a/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt +++ b/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt @@ -33,6 +33,7 @@ import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.exhaustive +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.features.attachments.AttachmentsHelper import im.vector.app.features.attachments.preview.AttachmentsPreviewActivity @@ -70,13 +71,28 @@ class IncomingShareFragment @Inject constructor( setupToolbar(incomingShareToolbar) attachmentsHelper = AttachmentsHelper(requireContext(), this).register() + viewModel.observeViewEvents { + when (it) { + is IncomingShareViewEvents.ShareToRoom -> handleShareToRoom(it) + is IncomingShareViewEvents.EditMediaBeforeSending -> handleEditMediaBeforeSending(it) + is IncomingShareViewEvents.MultipleRoomsShareDone -> handleMultipleRoomsShareDone(it) + }.exhaustive + } + val intent = vectorBaseActivity.intent val isShareManaged = when (intent?.action) { - Intent.ACTION_SEND -> { + Intent.ACTION_SEND -> { var isShareManaged = attachmentsHelper.handleShareIntent(requireContext(), intent) if (!isShareManaged) { isShareManaged = handleTextShare(intent) } + + // Direct share + if (intent.hasExtra(Intent.EXTRA_SHORTCUT_ID)) { + val roomId = intent.getStringExtra(Intent.EXTRA_SHORTCUT_ID)!! + sessionHolder.getSafeActiveSession()?.getRoomSummary(roomId)?.let { viewModel.handle(IncomingShareAction.ShareToRoom(it)) } + } + isShareManaged } Intent.ACTION_SEND_MULTIPLE -> attachmentsHelper.handleShareIntent(requireContext(), intent) @@ -100,13 +116,6 @@ class IncomingShareFragment @Inject constructor( sendShareButton.setOnClickListener { _ -> handleSendShare() } - viewModel.observeViewEvents { - when (it) { - is IncomingShareViewEvents.ShareToRoom -> handleShareToRoom(it) - is IncomingShareViewEvents.EditMediaBeforeSending -> handleEditMediaBeforeSending(it) - is IncomingShareViewEvents.MultipleRoomsShareDone -> handleMultipleRoomsShareDone(it) - }.exhaustive - } } private fun handleMultipleRoomsShareDone(viewEvent: IncomingShareViewEvents.MultipleRoomsShareDone) { @@ -118,20 +127,16 @@ class IncomingShareFragment @Inject constructor( private fun handleEditMediaBeforeSending(event: IncomingShareViewEvents.EditMediaBeforeSending) { val intent = AttachmentsPreviewActivity.newIntent(requireContext(), AttachmentsPreviewArgs(event.contentAttachmentData)) - startActivityForResult(intent, AttachmentsPreviewActivity.REQUEST_CODE) + attachmentPreviewActivityResultLauncher.launch(intent) } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - val hasBeenHandled = attachmentsHelper.onActivityResult(requestCode, resultCode, data) - if (!hasBeenHandled && resultCode == Activity.RESULT_OK && data != null) { - when (requestCode) { - AttachmentsPreviewActivity.REQUEST_CODE -> { - val sendData = AttachmentsPreviewActivity.getOutput(data) - val keepOriginalSize = AttachmentsPreviewActivity.getKeepOriginalSize(data) - viewModel.handle(IncomingShareAction.UpdateSharedData(SharedData.Attachments(sendData))) - viewModel.handle(IncomingShareAction.ShareMedia(keepOriginalSize)) - } - } + private val attachmentPreviewActivityResultLauncher = registerStartForActivityResult { + val data = it.data ?: return@registerStartForActivityResult + if (it.resultCode == Activity.RESULT_OK) { + val sendData = AttachmentsPreviewActivity.getOutput(data) + val keepOriginalSize = AttachmentsPreviewActivity.getKeepOriginalSize(data) + viewModel.handle(IncomingShareAction.UpdateSharedData(SharedData.Attachments(sendData))) + viewModel.handle(IncomingShareAction.ShareMedia(keepOriginalSize)) } } diff --git a/vector/src/main/java/im/vector/app/features/share/IncomingShareViewEvents.kt b/vector/src/main/java/im/vector/app/features/share/IncomingShareViewEvents.kt index 8b42384ec6..3f5a43e103 100644 --- a/vector/src/main/java/im/vector/app/features/share/IncomingShareViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/share/IncomingShareViewEvents.kt @@ -16,9 +16,9 @@ package im.vector.app.features.share +import im.vector.app.core.platform.VectorViewEvents import org.matrix.android.sdk.api.session.content.ContentAttachmentData import org.matrix.android.sdk.api.session.room.model.RoomSummary -import im.vector.app.core.platform.VectorViewEvents sealed class IncomingShareViewEvents : VectorViewEvents { data class ShareToRoom(val roomSummary: RoomSummary, diff --git a/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt b/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt index aa4d8ad03e..9014565e08 100644 --- a/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt @@ -22,17 +22,17 @@ import com.airbnb.mvrx.ViewModelContext import com.jakewharton.rxrelay2.BehaviorRelay import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject -import org.matrix.android.sdk.api.query.QueryStringValue -import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.content.ContentAttachmentData -import org.matrix.android.sdk.api.session.room.model.Membership -import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.toggle import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.attachments.isPreviewable import im.vector.app.features.attachments.toGroupedContentAttachmentData import im.vector.app.features.home.room.list.BreadcrumbsRoomComparator +import org.matrix.android.sdk.api.query.QueryStringValue +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.content.ContentAttachmentData +import org.matrix.android.sdk.api.session.room.model.Membership +import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.rx.rx import java.util.concurrent.TimeUnit @@ -96,6 +96,7 @@ class IncomingShareViewModel @AssistedInject constructor( when (action) { is IncomingShareAction.SelectRoom -> handleSelectRoom(action) is IncomingShareAction.ShareToSelectedRooms -> handleShareToSelectedRooms() + is IncomingShareAction.ShareToRoom -> handleShareToRoom(action) is IncomingShareAction.ShareMedia -> handleShareMediaToSelectedRooms(action) is IncomingShareAction.FilterWith -> handleFilter(action) is IncomingShareAction.UpdateSharedData -> handleUpdateSharedData(action) @@ -134,6 +135,11 @@ class IncomingShareViewModel @AssistedInject constructor( } } + private fun handleShareToRoom(action: IncomingShareAction.ShareToRoom) = withState { state -> + val sharedData = state.sharedData ?: return@withState + _viewEvents.post(IncomingShareViewEvents.ShareToRoom(action.roomSummary, sharedData, showAlert = false)) + } + private fun handleShareMediaToSelectedRooms(action: IncomingShareAction.ShareMedia) = withState { state -> (state.sharedData as? SharedData.Attachments)?.let { shareAttachments(it.attachmentData, state.selectedRoomIds, proposeMediaEdition = false, compressMediaBeforeSending = !action.keepOriginalSize) diff --git a/vector/src/main/java/im/vector/app/features/share/SharedData.kt b/vector/src/main/java/im/vector/app/features/share/SharedData.kt index 56932d45ab..02d00ae401 100644 --- a/vector/src/main/java/im/vector/app/features/share/SharedData.kt +++ b/vector/src/main/java/im/vector/app/features/share/SharedData.kt @@ -17,14 +17,14 @@ package im.vector.app.features.share import android.os.Parcelable -import org.matrix.android.sdk.api.session.content.ContentAttachmentData import kotlinx.android.parcel.Parcelize +import org.matrix.android.sdk.api.session.content.ContentAttachmentData -sealed class SharedData: Parcelable { +sealed class SharedData : Parcelable { @Parcelize - data class Text(val text: String): SharedData() + data class Text(val text: String) : SharedData() @Parcelize - data class Attachments(val attachmentData: List): SharedData() + data class Attachments(val attachmentData: List) : SharedData() } diff --git a/vector/src/main/java/im/vector/app/features/signout/hard/SignedOutActivity.kt b/vector/src/main/java/im/vector/app/features/signout/hard/SignedOutActivity.kt index 03fafe1e47..adb87a3bc5 100644 --- a/vector/src/main/java/im/vector/app/features/signout/hard/SignedOutActivity.kt +++ b/vector/src/main/java/im/vector/app/features/signout/hard/SignedOutActivity.kt @@ -19,11 +19,11 @@ package im.vector.app.features.signout.hard import android.content.Context import android.content.Intent import butterknife.OnClick -import org.matrix.android.sdk.api.failure.GlobalError import im.vector.app.R import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.features.MainActivity import im.vector.app.features.MainActivityArgs +import org.matrix.android.sdk.api.failure.GlobalError import timber.log.Timber /** diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutAction.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutAction.kt index b9f485ebbc..d4edd2bc99 100644 --- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutAction.kt +++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutAction.kt @@ -16,8 +16,8 @@ package im.vector.app.features.signout.soft -import org.matrix.android.sdk.api.auth.data.Credentials import im.vector.app.core.platform.VectorViewModelAction +import org.matrix.android.sdk.api.auth.data.Credentials sealed class SoftLogoutAction : VectorViewModelAction { // In case of failure to get the login flow diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutActivity.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutActivity.kt index 8a1c14f4f6..5fb7a3e315 100644 --- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutActivity.kt +++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutActivity.kt @@ -23,8 +23,6 @@ import androidx.core.view.isVisible import androidx.fragment.app.FragmentManager import com.airbnb.mvrx.Success import com.airbnb.mvrx.viewModel -import org.matrix.android.sdk.api.failure.GlobalError -import org.matrix.android.sdk.api.session.Session import im.vector.app.R import im.vector.app.core.di.ScreenComponent import im.vector.app.core.error.ErrorFormatter @@ -33,6 +31,8 @@ import im.vector.app.features.MainActivity import im.vector.app.features.MainActivityArgs import im.vector.app.features.login.LoginActivity import kotlinx.android.synthetic.main.activity_login.* +import org.matrix.android.sdk.api.failure.GlobalError +import org.matrix.android.sdk.api.session.Session import timber.log.Timber import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/sync/widget/SyncStateView.kt b/vector/src/main/java/im/vector/app/features/sync/widget/SyncStateView.kt index 6a524dba5d..e5e28dcabb 100755 --- a/vector/src/main/java/im/vector/app/features/sync/widget/SyncStateView.kt +++ b/vector/src/main/java/im/vector/app/features/sync/widget/SyncStateView.kt @@ -21,10 +21,10 @@ import android.util.AttributeSet import android.view.View import android.widget.FrameLayout import androidx.core.view.isVisible -import org.matrix.android.sdk.api.session.sync.SyncState import im.vector.app.R import im.vector.app.core.utils.isAirplaneModeOn import kotlinx.android.synthetic.main.view_sync_state.view.* +import org.matrix.android.sdk.api.session.sync.SyncState class SyncStateView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : FrameLayout(context, attrs, defStyle) { diff --git a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsActivity.kt b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsActivity.kt index 4fea176249..95468c12c4 100644 --- a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsActivity.kt +++ b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsActivity.kt @@ -20,13 +20,13 @@ import android.content.Context import android.content.Intent import androidx.appcompat.app.AlertDialog import com.airbnb.mvrx.viewModel -import org.matrix.android.sdk.api.session.terms.TermsService import im.vector.app.R import im.vector.app.core.di.ScreenComponent import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.replaceFragment import im.vector.app.core.platform.SimpleFragmentActivity +import org.matrix.android.sdk.api.session.terms.TermsService import javax.inject.Inject class ReviewTermsActivity : SimpleFragmentActivity() { @@ -73,7 +73,6 @@ class ReviewTermsActivity : SimpleFragmentActivity() { } companion object { - const val TERMS_REQUEST_CODE = 15000 private const val EXTRA_INFO = "EXTRA_INFO" fun intent(context: Context, serviceType: TermsService.ServiceType, baseUrl: String, token: String?): Intent { diff --git a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsFragment.kt b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsFragment.kt index 9f7b6cdeb0..10fcb65e12 100644 --- a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsFragment.kt @@ -22,7 +22,6 @@ import com.airbnb.mvrx.Loading import com.airbnb.mvrx.Success import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.withState -import org.matrix.android.sdk.api.session.terms.TermsService import im.vector.app.R import im.vector.app.core.epoxy.onClick import im.vector.app.core.extensions.cleanup @@ -32,6 +31,7 @@ import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.openUrlInChromeCustomTab import kotlinx.android.synthetic.main.fragment_review_terms.* +import org.matrix.android.sdk.api.session.terms.TermsService import javax.inject.Inject class ReviewTermsFragment @Inject constructor( diff --git a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsViewModel.kt b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsViewModel.kt index 6458646643..df822807ee 100644 --- a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsViewModel.kt @@ -24,12 +24,12 @@ import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject -import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.terms.GetTermsResponse -import org.matrix.android.sdk.internal.util.awaitCallback import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.terms.GetTermsResponse +import org.matrix.android.sdk.internal.util.awaitCallback import timber.log.Timber class ReviewTermsViewModel @AssistedInject constructor( diff --git a/vector/src/main/java/im/vector/app/features/terms/ServiceTermsArgs.kt b/vector/src/main/java/im/vector/app/features/terms/ServiceTermsArgs.kt index ec97c98627..50c9568204 100644 --- a/vector/src/main/java/im/vector/app/features/terms/ServiceTermsArgs.kt +++ b/vector/src/main/java/im/vector/app/features/terms/ServiceTermsArgs.kt @@ -16,8 +16,8 @@ package im.vector.app.features.terms import android.os.Parcelable -import org.matrix.android.sdk.api.session.terms.TermsService import kotlinx.android.parcel.Parcelize +import org.matrix.android.sdk.api.session.terms.TermsService @Parcelize data class ServiceTermsArgs( diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/DirectoryUsersController.kt b/vector/src/main/java/im/vector/app/features/userdirectory/DirectoryUsersController.kt index 1ade177010..e68d9855dd 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/DirectoryUsersController.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/DirectoryUsersController.kt @@ -21,10 +21,6 @@ import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized -import org.matrix.android.sdk.api.MatrixPatterns -import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.user.model.User -import org.matrix.android.sdk.api.util.toMatrixItem import im.vector.app.R import im.vector.app.core.epoxy.errorWithRetryItem import im.vector.app.core.epoxy.loadingItem @@ -32,6 +28,10 @@ import im.vector.app.core.epoxy.noResultItem import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.resources.StringProvider import im.vector.app.features.home.AvatarRenderer +import org.matrix.android.sdk.api.MatrixPatterns +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.user.model.User +import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject class DirectoryUsersController @Inject constructor(private val session: Session, diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/KnownUsersController.kt b/vector/src/main/java/im/vector/app/features/userdirectory/KnownUsersController.kt index f0bd4e67cf..4fbb9bbb41 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/KnownUsersController.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/KnownUsersController.kt @@ -21,9 +21,6 @@ import com.airbnb.epoxy.paging.PagedListEpoxyController import com.airbnb.mvrx.Async import com.airbnb.mvrx.Incomplete import com.airbnb.mvrx.Uninitialized -import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.user.model.User -import org.matrix.android.sdk.api.util.toMatrixItem import im.vector.app.R import im.vector.app.core.epoxy.EmptyItem_ import im.vector.app.core.epoxy.loadingItem @@ -31,6 +28,9 @@ import im.vector.app.core.epoxy.noResultItem import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.createUIHandler import im.vector.app.features.home.AvatarRenderer +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.user.model.User +import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject class KnownUsersController @Inject constructor(private val session: Session, diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserDirectoryFragment.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserDirectoryFragment.kt index f2e090d966..8787946bf4 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserDirectoryFragment.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserDirectoryFragment.kt @@ -21,7 +21,6 @@ import android.view.View import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.withState import com.jakewharton.rxbinding3.widget.textChanges -import org.matrix.android.sdk.api.session.user.model.User import im.vector.app.R import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith @@ -31,6 +30,7 @@ import im.vector.app.core.extensions.showKeyboard import im.vector.app.core.platform.VectorBaseFragment import kotlinx.android.synthetic.main.fragment_create_direct_room_directory_users.recyclerView import kotlinx.android.synthetic.main.fragment_user_directory.* +import org.matrix.android.sdk.api.session.user.model.User import javax.inject.Inject class UserDirectoryFragment @Inject constructor( diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserDirectoryUserItem.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserDirectoryUserItem.kt index 1ba82ed2a4..dd7b002872 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserDirectoryUserItem.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserDirectoryUserItem.kt @@ -23,12 +23,12 @@ import androidx.core.content.ContextCompat import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass import com.amulyakhare.textdrawable.TextDrawable -import org.matrix.android.sdk.api.util.MatrixItem import im.vector.app.R import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.resources.ColorProvider import im.vector.app.features.home.AvatarRenderer +import org.matrix.android.sdk.api.util.MatrixItem @EpoxyModelClass(layout = R.layout.item_known_user) abstract class UserDirectoryUserItem : VectorEpoxyModel() { diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserDirectoryViewModel.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserDirectoryViewModel.kt index fb84daecee..b8c42a0f08 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserDirectoryViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserDirectoryViewModel.kt @@ -25,7 +25,6 @@ import com.airbnb.mvrx.ViewModelContext import com.jakewharton.rxrelay2.BehaviorRelay import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject -import org.matrix.android.sdk.api.session.Session import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.toggle import im.vector.app.core.platform.VectorViewModel @@ -33,6 +32,7 @@ import im.vector.app.features.createdirect.CreateDirectRoomActivity import im.vector.app.features.invite.InviteUsersToRoomActivity import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers +import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.rx.rx import java.util.concurrent.TimeUnit diff --git a/vector/src/main/java/im/vector/app/features/webview/ConsentWebViewEventListener.kt b/vector/src/main/java/im/vector/app/features/webview/ConsentWebViewEventListener.kt index 7ac6a3344b..17e6fa4202 100644 --- a/vector/src/main/java/im/vector/app/features/webview/ConsentWebViewEventListener.kt +++ b/vector/src/main/java/im/vector/app/features/webview/ConsentWebViewEventListener.kt @@ -16,10 +16,10 @@ package im.vector.app.features.webview -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.session.Session import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.utils.weak +import org.matrix.android.sdk.api.MatrixCallback +import org.matrix.android.sdk.api.session.Session import timber.log.Timber private const val SUCCESS_URL_SUFFIX = "/_matrix/consent" diff --git a/vector/src/main/java/im/vector/app/features/webview/VectorWebViewActivity.kt b/vector/src/main/java/im/vector/app/features/webview/VectorWebViewActivity.kt index e49109b1d6..5b761d1b3a 100644 --- a/vector/src/main/java/im/vector/app/features/webview/VectorWebViewActivity.kt +++ b/vector/src/main/java/im/vector/app/features/webview/VectorWebViewActivity.kt @@ -21,11 +21,11 @@ import android.content.Intent import android.webkit.WebChromeClient import android.webkit.WebView import androidx.annotation.CallSuper -import org.matrix.android.sdk.api.session.Session import im.vector.app.R import im.vector.app.core.di.ScreenComponent import im.vector.app.core.platform.VectorBaseActivity import kotlinx.android.synthetic.main.activity_vector_web_view.* +import org.matrix.android.sdk.api.session.Session import javax.inject.Inject /** diff --git a/vector/src/main/java/im/vector/app/features/webview/WebViewEventListenerFactory.kt b/vector/src/main/java/im/vector/app/features/webview/WebViewEventListenerFactory.kt index 412f2e5033..64e08852b3 100644 --- a/vector/src/main/java/im/vector/app/features/webview/WebViewEventListenerFactory.kt +++ b/vector/src/main/java/im/vector/app/features/webview/WebViewEventListenerFactory.kt @@ -16,8 +16,8 @@ package im.vector.app.features.webview -import org.matrix.android.sdk.api.session.Session import im.vector.app.core.platform.VectorBaseActivity +import org.matrix.android.sdk.api.session.Session interface WebViewEventListenerFactory { diff --git a/vector/src/main/java/im/vector/app/features/webview/WebViewMode.kt b/vector/src/main/java/im/vector/app/features/webview/WebViewMode.kt index 742302c61e..edf98f66d8 100644 --- a/vector/src/main/java/im/vector/app/features/webview/WebViewMode.kt +++ b/vector/src/main/java/im/vector/app/features/webview/WebViewMode.kt @@ -16,8 +16,8 @@ package im.vector.app.features.webview -import org.matrix.android.sdk.api.session.Session import im.vector.app.core.platform.VectorBaseActivity +import org.matrix.android.sdk.api.session.Session /** * This enum indicates the WebView mode. It's responsible for creating a WebViewEventListener diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetAPICallback.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetAPICallback.kt index 3eaedd9f4d..ad17a5ae87 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetAPICallback.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetAPICallback.kt @@ -16,11 +16,11 @@ package im.vector.app.features.widgets +import im.vector.app.R +import im.vector.app.core.resources.StringProvider import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.widgets.WidgetPostAPIMediator import org.matrix.android.sdk.api.util.JsonDict -import im.vector.app.R -import im.vector.app.core.resources.StringProvider class WidgetAPICallback(private val postAPIMediator: WidgetPostAPIMediator, private val eventData: JsonDict, diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt index 37f1d3a192..d28fc134a9 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt @@ -23,7 +23,6 @@ import androidx.appcompat.widget.Toolbar import androidx.core.view.isVisible import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.viewModel -import org.matrix.android.sdk.api.session.events.model.Content import im.vector.app.R import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.addFragment @@ -34,6 +33,7 @@ import im.vector.app.features.widgets.permissions.RoomWidgetPermissionViewEvents import im.vector.app.features.widgets.permissions.RoomWidgetPermissionViewModel import im.vector.app.features.widgets.permissions.RoomWidgetPermissionViewState import kotlinx.android.synthetic.main.activity_widget.* +import org.matrix.android.sdk.api.session.events.model.Content import java.io.Serializable import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetArgsBuilder.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetArgsBuilder.kt index badec16a27..198278a794 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetArgsBuilder.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetArgsBuilder.kt @@ -16,8 +16,8 @@ package im.vector.app.features.widgets -import org.matrix.android.sdk.api.session.widgets.model.Widget import im.vector.app.core.di.ActiveSessionHolder +import org.matrix.android.sdk.api.session.widgets.model.Widget import javax.inject.Inject class WidgetArgsBuilder @Inject constructor(private val sessionHolder: ActiveSessionHolder) { diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetFragment.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetFragment.kt index 37ae89ab36..35d7867db3 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetFragment.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetFragment.kt @@ -35,18 +35,17 @@ import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.args import com.airbnb.mvrx.withState -import org.matrix.android.sdk.api.session.terms.TermsService import im.vector.app.R +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.OnBackPressed import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.openUrlInExternalBrowser -import im.vector.app.features.home.room.detail.widget.WidgetRequestCodes -import im.vector.app.features.terms.ReviewTermsActivity import im.vector.app.features.webview.WebViewEventListener import im.vector.app.features.widgets.webview.clearAfterWidget import im.vector.app.features.widgets.webview.setupForWidget import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.fragment_room_widget.* +import org.matrix.android.sdk.api.session.terms.TermsService import timber.log.Timber import java.net.URISyntaxException import javax.inject.Inject @@ -86,19 +85,18 @@ class WidgetFragment @Inject constructor() : VectorBaseFragment(), WebViewEventL viewModel.handle(WidgetAction.LoadFormattedUrl) } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - when (requestCode) { - ReviewTermsActivity.TERMS_REQUEST_CODE -> { - Timber.v("On terms results") - if (resultCode == Activity.RESULT_OK) { - viewModel.handle(WidgetAction.OnTermsReviewed) - } else { - vectorBaseActivity.finish() - } - } - WidgetRequestCodes.INTEGRATION_MANAGER_REQUEST_CODE -> { - viewModel.handle(WidgetAction.LoadFormattedUrl) - } + private val termsActivityResultLauncher = registerStartForActivityResult { + Timber.v("On terms results") + if (it.resultCode == Activity.RESULT_OK) { + viewModel.handle(WidgetAction.OnTermsReviewed) + } else { + vectorBaseActivity.finish() + } + } + + private val integrationManagerActivityResultLauncher = registerStartForActivityResult { + if (it.resultCode == Activity.RESULT_OK) { + viewModel.handle(WidgetAction.LoadFormattedUrl) } } @@ -146,7 +144,12 @@ class WidgetFragment @Inject constructor() : VectorBaseFragment(), WebViewEventL override fun onOptionsItemSelected(item: MenuItem): Boolean = withState(viewModel) { state -> when (item.itemId) { R.id.action_edit -> { - navigator.openIntegrationManager(this, state.roomId, state.widgetId, state.widgetKind.screenId) + navigator.openIntegrationManager( + requireContext(), + integrationManagerActivityResultLauncher, + state.roomId, + state.widgetId, + state.widgetKind.screenId) return@withState true } R.id.action_delete -> { @@ -261,7 +264,8 @@ class WidgetFragment @Inject constructor() : VectorBaseFragment(), WebViewEventL private fun displayTerms(displayTerms: WidgetViewEvents.DisplayTerms) { navigator.openTerms( - fragment = this, + context = requireContext(), + activityResultLauncher = termsActivityResultLauncher, serviceType = TermsService.ServiceType.IntegrationManager, baseUrl = displayTerms.url, token = displayTerms.token @@ -287,7 +291,8 @@ class WidgetFragment @Inject constructor() : VectorBaseFragment(), WebViewEventL private fun displayIntegrationManager(event: WidgetViewEvents.DisplayIntegrationManager) { navigator.openIntegrationManager( - fragment = this, + context = requireContext(), + activityResultLauncher = integrationManagerActivityResultLauncher, roomId = fragmentArgs.roomId, integId = event.integId, screen = event.integType diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetPostAPIHandler.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetPostAPIHandler.kt index 69c907b4e2..a4d759250d 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetPostAPIHandler.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetPostAPIHandler.kt @@ -19,8 +19,11 @@ package im.vector.app.features.widgets import android.text.TextUtils import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject +import im.vector.app.R +import im.vector.app.core.resources.StringProvider import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes import org.matrix.android.sdk.api.session.events.model.Content import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType @@ -31,9 +34,6 @@ import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.widgets.WidgetPostAPIMediator import org.matrix.android.sdk.api.util.JsonDict -import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes -import im.vector.app.R -import im.vector.app.core.resources.StringProvider import timber.log.Timber import java.util.ArrayList import java.util.HashMap diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewEvents.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewEvents.kt index a6a20a6717..34e5c794f7 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewEvents.kt @@ -16,11 +16,11 @@ package im.vector.app.features.widgets -import org.matrix.android.sdk.api.session.events.model.Content import im.vector.app.core.platform.VectorViewEvents +import org.matrix.android.sdk.api.session.events.model.Content sealed class WidgetViewEvents : VectorViewEvents { - data class Failure(val throwable: Throwable): WidgetViewEvents() + data class Failure(val throwable: Throwable) : WidgetViewEvents() data class Close(val content: Content? = null) : WidgetViewEvents() data class DisplayIntegrationManager(val integId: String?, val integType: String?) : WidgetViewEvents() data class OnURLFormatted(val formattedURL: String) : WidgetViewEvents() diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt index 9ecafb1020..1bc10e26d4 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt @@ -27,6 +27,10 @@ import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject +import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.resources.StringProvider +import im.vector.app.features.widgets.permissions.WidgetPermissionsHelper +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.Content @@ -40,10 +44,6 @@ import org.matrix.android.sdk.internal.util.awaitCallback import org.matrix.android.sdk.rx.mapOptional import org.matrix.android.sdk.rx.rx import org.matrix.android.sdk.rx.unwrap -import im.vector.app.core.platform.VectorViewModel -import im.vector.app.core.resources.StringProvider -import im.vector.app.features.widgets.permissions.WidgetPermissionsHelper -import kotlinx.coroutines.launch import timber.log.Timber import javax.net.ssl.HttpsURLConnection diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewState.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewState.kt index 3289ee8b54..845ee81a2d 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewState.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewState.kt @@ -20,9 +20,9 @@ import androidx.annotation.StringRes import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized +import im.vector.app.R import org.matrix.android.sdk.api.session.widgets.model.Widget import org.matrix.android.sdk.api.session.widgets.model.WidgetType -import im.vector.app.R enum class WidgetStatus { UNKNOWN, diff --git a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionActions.kt b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionActions.kt index 38344b80ae..ae02707b50 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionActions.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionActions.kt @@ -19,7 +19,7 @@ package im.vector.app.features.widgets.permissions import im.vector.app.core.platform.VectorViewModelAction sealed class RoomWidgetPermissionActions : VectorViewModelAction { - object AllowWidget: RoomWidgetPermissionActions() - object BlockWidget: RoomWidgetPermissionActions() - object DoClose: RoomWidgetPermissionActions() + object AllowWidget : RoomWidgetPermissionActions() + object BlockWidget : RoomWidgetPermissionActions() + object DoClose : RoomWidgetPermissionActions() } diff --git a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionBottomSheet.kt b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionBottomSheet.kt index 2c0aff3289..45ac9e7b1d 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionBottomSheet.kt @@ -23,7 +23,6 @@ import butterknife.OnClick import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.withState -import org.matrix.android.sdk.api.util.toMatrixItem import im.vector.app.R import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.withArgs @@ -31,6 +30,7 @@ import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.widgets.WidgetArgs import kotlinx.android.synthetic.main.bottom_sheet_room_widget_permission.* +import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject class RoomWidgetPermissionBottomSheet : VectorBaseBottomSheetDialogFragment() { diff --git a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt index ea55e04103..cb40e5672b 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt @@ -22,12 +22,12 @@ import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject -import org.matrix.android.sdk.api.extensions.orFalse -import org.matrix.android.sdk.api.query.QueryStringValue -import org.matrix.android.sdk.api.session.Session import im.vector.app.R import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.extensions.orFalse +import org.matrix.android.sdk.api.query.QueryStringValue +import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.widgets.model.WidgetType import org.matrix.android.sdk.internal.util.awaitCallback import org.matrix.android.sdk.rx.rx diff --git a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewState.kt b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewState.kt index 8d2e9b95b8..1cc14a91c2 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewState.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewState.kt @@ -19,8 +19,8 @@ package im.vector.app.features.widgets.permissions import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized -import org.matrix.android.sdk.api.session.widgets.model.Widget import im.vector.app.features.widgets.WidgetArgs +import org.matrix.android.sdk.api.session.widgets.model.Widget data class RoomWidgetPermissionViewState( val roomId: String, diff --git a/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt b/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt index 1c887f91be..48f83b6c54 100644 --- a/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt @@ -26,6 +26,12 @@ import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject +import im.vector.app.core.platform.EmptyAction +import im.vector.app.core.platform.EmptyViewEvents +import im.vector.app.core.platform.VectorViewModel +import io.reactivex.Observable +import io.reactivex.functions.Function4 +import io.reactivex.subjects.PublishSubject import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent @@ -37,12 +43,6 @@ import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupStateListener import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.internal.crypto.store.PrivateKeysInfo -import im.vector.app.core.platform.EmptyAction -import im.vector.app.core.platform.EmptyViewEvents -import im.vector.app.core.platform.VectorViewModel -import io.reactivex.Observable -import io.reactivex.functions.Function4 -import io.reactivex.subjects.PublishSubject import org.matrix.android.sdk.rx.rx import java.util.concurrent.TimeUnit diff --git a/vector/src/main/java/im/vector/app/features/workers/signout/SignoutBottomSheetActionButton.kt b/vector/src/main/java/im/vector/app/features/workers/signout/SignOutBottomSheetActionButton.kt similarity index 88% rename from vector/src/main/java/im/vector/app/features/workers/signout/SignoutBottomSheetActionButton.kt rename to vector/src/main/java/im/vector/app/features/workers/signout/SignOutBottomSheetActionButton.kt index 02ea2569e8..3b73455923 100644 --- a/vector/src/main/java/im/vector/app/features/workers/signout/SignoutBottomSheetActionButton.kt +++ b/vector/src/main/java/im/vector/app/features/workers/signout/SignOutBottomSheetActionButton.kt @@ -31,7 +31,7 @@ import im.vector.app.R import im.vector.app.core.extensions.setTextOrHide import im.vector.app.features.themes.ThemeUtils -class SignoutBottomSheetActionButton @JvmOverloads constructor( +class SignOutBottomSheetActionButton @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : LinearLayout(context, attrs, defStyleAttr) { @@ -80,11 +80,11 @@ class SignoutBottomSheetActionButton @JvmOverloads constructor( inflate(context, R.layout.item_signout_action, this) ButterKnife.bind(this) - val typedArray = context.obtainStyledAttributes(attrs, R.styleable.SignoutBottomSheetActionButton, 0, 0) - title = typedArray.getString(R.styleable.SignoutBottomSheetActionButton_actionTitle) ?: "" - leftIcon = typedArray.getDrawable(R.styleable.SignoutBottomSheetActionButton_leftIcon) - tint = typedArray.getColor(R.styleable.SignoutBottomSheetActionButton_iconTint, ThemeUtils.getColor(context, android.R.attr.textColor)) - textColor = typedArray.getColor(R.styleable.SignoutBottomSheetActionButton_textColor, ThemeUtils.getColor(context, android.R.attr.textColor)) + val typedArray = context.obtainStyledAttributes(attrs, R.styleable.SignOutBottomSheetActionButton, 0, 0) + title = typedArray.getString(R.styleable.SignOutBottomSheetActionButton_actionTitle) ?: "" + leftIcon = typedArray.getDrawable(R.styleable.SignOutBottomSheetActionButton_leftIcon) + tint = typedArray.getColor(R.styleable.SignOutBottomSheetActionButton_iconTint, ThemeUtils.getColor(context, android.R.attr.textColor)) + textColor = typedArray.getColor(R.styleable.SignOutBottomSheetActionButton_textColor, ThemeUtils.getColor(context, android.R.attr.textColor)) typedArray.recycle() diff --git a/vector/src/main/java/im/vector/app/features/workers/signout/SignOutBottomSheetDialogFragment.kt b/vector/src/main/java/im/vector/app/features/workers/signout/SignOutBottomSheetDialogFragment.kt index c17c1a1cf8..830e9beabc 100644 --- a/vector/src/main/java/im/vector/app/features/workers/signout/SignOutBottomSheetDialogFragment.kt +++ b/vector/src/main/java/im/vector/app/features/workers/signout/SignOutBottomSheetDialogFragment.kt @@ -18,7 +18,6 @@ package im.vector.app.features.workers.signout import android.app.Activity import android.app.Dialog -import android.content.Intent import android.os.Bundle import android.view.View import android.view.ViewGroup @@ -35,15 +34,18 @@ import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState import im.vector.app.R import im.vector.app.core.di.ScreenComponent import im.vector.app.core.dialogs.ExportKeysDialog import im.vector.app.core.extensions.queryExportKeys +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.features.crypto.keysbackup.setup.KeysBackupSetupActivity import im.vector.app.features.crypto.recover.BootstrapBottomSheet +import im.vector.app.features.crypto.recover.SetupMode +import kotlinx.android.synthetic.main.bottom_sheet_logout_and_backup.* +import org.matrix.android.sdk.api.MatrixCallback +import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState import timber.log.Timber import javax.inject.Inject @@ -56,21 +58,6 @@ class SignOutBottomSheetDialogFragment : VectorBaseBottomSheetDialogFragment(), @BindView(R.id.bottom_sheet_signout_backingup_status_group) lateinit var backingUpStatusGroup: ViewGroup - @BindView(R.id.setupRecoveryButton) - lateinit var setupRecoveryButton: SignoutBottomSheetActionButton - - @BindView(R.id.setupMegolmBackupButton) - lateinit var setupMegolmBackupButton: SignoutBottomSheetActionButton - - @BindView(R.id.exportManuallyButton) - lateinit var exportManuallyButton: SignoutBottomSheetActionButton - - @BindView(R.id.exitAnywayButton) - lateinit var exitAnywayButton: SignoutBottomSheetActionButton - - @BindView(R.id.signOutButton) - lateinit var signOutButton: SignoutBottomSheetActionButton - @BindView(R.id.bottom_sheet_signout_icon_progress_bar) lateinit var backupProgress: ProgressBar @@ -90,9 +77,6 @@ class SignOutBottomSheetDialogFragment : VectorBaseBottomSheetDialogFragment(), companion object { fun newInstance() = SignOutBottomSheetDialogFragment() - - private const val EXPORT_REQ = 0 - private const val QUERY_EXPORT_KEYS = 1 } init { @@ -117,11 +101,11 @@ class SignOutBottomSheetDialogFragment : VectorBaseBottomSheetDialogFragment(), viewModel.refreshRemoteStateIfNeeded() } - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) setupRecoveryButton.action = { - BootstrapBottomSheet.show(parentFragmentManager, initCrossSigningOnly = false, forceReset4S = false) + BootstrapBottomSheet.show(parentFragmentManager, SetupMode.NORMAL) } exitAnywayButton.action = { @@ -137,14 +121,18 @@ class SignOutBottomSheetDialogFragment : VectorBaseBottomSheetDialogFragment(), } } + signOutButton.action = { + onSignOut?.run() + } + exportManuallyButton.action = { withState(viewModel) { state -> - queryExportKeys(state.userId, QUERY_EXPORT_KEYS) + queryExportKeys(state.userId, manualExportKeysActivityResultLauncher) } } setupMegolmBackupButton.action = { - startActivityForResult(KeysBackupSetupActivity.intent(requireContext(), true), EXPORT_REQ) + setupBackupActivityResultLauncher.launch(KeysBackupSetupActivity.intent(requireContext(), true)) } viewModel.observeViewEvents { @@ -181,10 +169,10 @@ class SignOutBottomSheetDialogFragment : VectorBaseBottomSheetDialogFragment(), // we should show option to setup 4S setupRecoveryButton.isVisible = true setupMegolmBackupButton.isVisible = false - signOutButton.isVisible = false // We let the option to ignore and quit exportManuallyButton.isVisible = true exitAnywayButton.isVisible = true + signOutButton.isVisible = false } else if (state.keysBackupState == KeysBackupState.Unknown || state.keysBackupState == KeysBackupState.Disabled) { sheetTitle.text = getString(R.string.sign_out_bottom_sheet_warning_no_backup) backingUpStatusGroup.isVisible = false @@ -193,10 +181,10 @@ class SignOutBottomSheetDialogFragment : VectorBaseBottomSheetDialogFragment(), // we should show option to setup 4S setupRecoveryButton.isVisible = false setupMegolmBackupButton.isVisible = true - signOutButton.isVisible = false // We let the option to ignore and quit exportManuallyButton.isVisible = true exitAnywayButton.isVisible = true + signOutButton.isVisible = false } else { // so keybackup is setup // You should wait until all are uploaded @@ -212,11 +200,12 @@ class SignOutBottomSheetDialogFragment : VectorBaseBottomSheetDialogFragment(), backupCompleteImage.isVisible = true backupStatusTex.text = getString(R.string.keys_backup_info_keys_all_backup_up) - hideViews(setupMegolmBackupButton, exportManuallyButton, exitAnywayButton) + setupMegolmBackupButton.isVisible = false + exportManuallyButton.isVisible = false + exitAnywayButton.isVisible = false // You can signout signOutButton.isVisible = true } - KeysBackupState.WillBackUp, KeysBackupState.BackingUp -> { sheetTitle.text = getString(R.string.sign_out_bottom_sheet_warning_backing_up) @@ -227,18 +216,21 @@ class SignOutBottomSheetDialogFragment : VectorBaseBottomSheetDialogFragment(), backupCompleteImage.isVisible = false backupStatusTex.text = getString(R.string.sign_out_bottom_sheet_backing_up_keys) - hideViews(setupMegolmBackupButton, setupMegolmBackupButton, signOutButton, exportManuallyButton) + setupMegolmBackupButton.isVisible = false + exportManuallyButton.isVisible = false exitAnywayButton.isVisible = true + signOutButton.isVisible = false } KeysBackupState.NotTrusted -> { sheetTitle.text = getString(R.string.sign_out_bottom_sheet_warning_backup_not_active) // It's not trusted and we know there are unsaved keys.. backingUpStatusGroup.isVisible = false - exportManuallyButton.isVisible = true // option to enter pass/key setupMegolmBackupButton.isVisible = true + exportManuallyButton.isVisible = true exitAnywayButton.isVisible = true + signOutButton.isVisible = false } else -> { // mmm.. strange state @@ -252,21 +244,23 @@ class SignOutBottomSheetDialogFragment : VectorBaseBottomSheetDialogFragment(), when (state.hasBeenExportedToFile) { is Loading -> { signoutExportingLoading.isVisible = true - hideViews(setupRecoveryButton, - setupMegolmBackupButton, - exportManuallyButton, - backingUpStatusGroup, - signOutButton) + backingUpStatusGroup.isVisible = false + + setupRecoveryButton.isVisible = false + setupMegolmBackupButton.isVisible = false + exportManuallyButton.isVisible = false exitAnywayButton.isVisible = true + signOutButton.isVisible = false } is Success -> { if (state.hasBeenExportedToFile.invoke()) { sheetTitle.text = getString(R.string.action_sign_out_confirmation_simple) - hideViews(setupRecoveryButton, - setupMegolmBackupButton, - exportManuallyButton, - backingUpStatusGroup, - exitAnywayButton) + backingUpStatusGroup.isVisible = false + + setupRecoveryButton.isVisible = false + setupMegolmBackupButton.isVisible = false + exportManuallyButton.isVisible = false + exitAnywayButton.isVisible = false signOutButton.isVisible = true } } @@ -292,30 +286,26 @@ class SignOutBottomSheetDialogFragment : VectorBaseBottomSheetDialogFragment(), return dialog } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - - if (resultCode == Activity.RESULT_OK) { - if (requestCode == QUERY_EXPORT_KEYS) { - val uri = data?.data - if (resultCode == Activity.RESULT_OK && uri != null) { - activity?.let { activity -> - ExportKeysDialog().show(activity, object : ExportKeysDialog.ExportKeyDialogListener { - override fun onPassphrase(passphrase: String) { - viewModel.handle(SignoutCheckViewModel.Actions.ExportKeys(passphrase, uri)) - } - }) - } - } - } else if (requestCode == EXPORT_REQ) { - if (data?.getBooleanExtra(KeysBackupSetupActivity.MANUAL_EXPORT, false) == true) { - viewModel.handle(SignoutCheckViewModel.Actions.KeySuccessfullyManuallyExported) + private val manualExportKeysActivityResultLauncher = registerStartForActivityResult { + if (it.resultCode == Activity.RESULT_OK) { + val uri = it.data?.data + if (uri != null) { + activity?.let { activity -> + ExportKeysDialog().show(activity, object : ExportKeysDialog.ExportKeyDialogListener { + override fun onPassphrase(passphrase: String) { + viewModel.handle(SignoutCheckViewModel.Actions.ExportKeys(passphrase, uri)) + } + }) } } } } - private fun hideViews(vararg views: View) { - views.forEach { it.isVisible = false } + private val setupBackupActivityResultLauncher = registerStartForActivityResult { + if (it.resultCode == Activity.RESULT_OK) { + if (it.data?.getBooleanExtra(KeysBackupSetupActivity.MANUAL_EXPORT, false) == true) { + viewModel.handle(SignoutCheckViewModel.Actions.KeySuccessfullyManuallyExported) + } + } } } diff --git a/vector/src/main/java/im/vector/app/features/workers/signout/SignOutUiWorker.kt b/vector/src/main/java/im/vector/app/features/workers/signout/SignOutUiWorker.kt index d78b652113..53ea5c7887 100644 --- a/vector/src/main/java/im/vector/app/features/workers/signout/SignOutUiWorker.kt +++ b/vector/src/main/java/im/vector/app/features/workers/signout/SignOutUiWorker.kt @@ -16,11 +16,9 @@ package im.vector.app.features.workers.signout -import android.content.Context import androidx.appcompat.app.AlertDialog import androidx.fragment.app.FragmentActivity import im.vector.app.R -import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.extensions.cannotLogoutSafely import im.vector.app.core.extensions.vectorComponent import im.vector.app.features.MainActivity @@ -28,11 +26,8 @@ import im.vector.app.features.MainActivityArgs class SignOutUiWorker(private val activity: FragmentActivity) { - lateinit var activeSessionHolder: ActiveSessionHolder - - fun perform(context: Context) { - activeSessionHolder = context.vectorComponent().activeSessionHolder() - val session = activeSessionHolder.getActiveSession() + fun perform() { + val session = activity.vectorComponent().activeSessionHolder().getSafeActiveSession() ?: return if (session.cannotLogoutSafely()) { // The backup check on logout flow has to be displayed if there are keys in the store, and the keys backup state is not Ready val signOutDialog = SignOutBottomSheetDialogFragment.newInstance() diff --git a/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt b/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt index 5fd2f7c838..48426be723 100644 --- a/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt @@ -28,17 +28,17 @@ import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject +import im.vector.app.core.extensions.exhaustive +import im.vector.app.core.platform.VectorViewEvents +import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.platform.VectorViewModelAction +import im.vector.app.features.crypto.keys.KeysExporter import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupStateListener -import im.vector.app.core.extensions.exhaustive -import im.vector.app.core.platform.VectorViewEvents -import im.vector.app.core.platform.VectorViewModel -import im.vector.app.core.platform.VectorViewModelAction -import im.vector.app.features.crypto.keys.KeysExporter import org.matrix.android.sdk.rx.rx data class SignoutCheckViewState( diff --git a/vector/src/main/play/listings/pt_BR/title.txt b/vector/src/main/play/listings/pt_BR/title.txt deleted file mode 100644 index 70c9f7030b..0000000000 --- a/vector/src/main/play/listings/pt_BR/title.txt +++ /dev/null @@ -1 +0,0 @@ -Element (antigo Riot.im) diff --git a/vector/src/main/res/drawable/ic_add_to_home_screen_24dp.xml b/vector/src/main/res/drawable/ic_add_to_home_screen_24dp.xml new file mode 100644 index 0000000000..4a0a809578 --- /dev/null +++ b/vector/src/main/res/drawable/ic_add_to_home_screen_24dp.xml @@ -0,0 +1,12 @@ + + + + diff --git a/vector/src/main/res/drawable/ic_expand_less.xml b/vector/src/main/res/drawable/ic_expand_less.xml new file mode 100644 index 0000000000..92bc86da08 --- /dev/null +++ b/vector/src/main/res/drawable/ic_expand_less.xml @@ -0,0 +1,9 @@ + + + diff --git a/vector/src/main/res/drawable/ic_expand_more.xml b/vector/src/main/res/drawable/ic_expand_more.xml new file mode 100644 index 0000000000..22c401f0c3 --- /dev/null +++ b/vector/src/main/res/drawable/ic_expand_more.xml @@ -0,0 +1,9 @@ + + + diff --git a/vector/src/main/res/drawable/ic_fab_add_members.xml b/vector/src/main/res/drawable/ic_fab_add_members.xml new file mode 100644 index 0000000000..50768871ab --- /dev/null +++ b/vector/src/main/res/drawable/ic_fab_add_members.xml @@ -0,0 +1,30 @@ + + + + + + + + + diff --git a/vector/src/main/res/drawable/ic_invite_users.xml b/vector/src/main/res/drawable/ic_invite_users.xml deleted file mode 100644 index 64e5a3788d..0000000000 --- a/vector/src/main/res/drawable/ic_invite_users.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - diff --git a/vector/src/main/res/drawable/ic_search_no_results.xml b/vector/src/main/res/drawable/ic_search_no_results.xml new file mode 100644 index 0000000000..24c0f00131 --- /dev/null +++ b/vector/src/main/res/drawable/ic_search_no_results.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/drawable/ic_settings_18dp.xml b/vector/src/main/res/drawable/ic_settings_18dp.xml new file mode 100644 index 0000000000..4ca1725703 --- /dev/null +++ b/vector/src/main/res/drawable/ic_settings_18dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/vector/src/main/res/drawable/ic_settings_root_legacy.xml b/vector/src/main/res/drawable/ic_settings_root_legacy.xml deleted file mode 100644 index 9ad4d4a0f7..0000000000 --- a/vector/src/main/res/drawable/ic_settings_root_legacy.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - diff --git a/vector/src/main/res/drawable/ic_signout_18dp.xml b/vector/src/main/res/drawable/ic_signout_18dp.xml new file mode 100644 index 0000000000..80f2995bc0 --- /dev/null +++ b/vector/src/main/res/drawable/ic_signout_18dp.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/vector/src/main/res/layout/activity_image_media_viewer.xml b/vector/src/main/res/layout/activity_image_media_viewer.xml deleted file mode 100644 index cfcfa6702b..0000000000 --- a/vector/src/main/res/layout/activity_image_media_viewer.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/vector/src/main/res/layout/activity_jitsi.xml b/vector/src/main/res/layout/activity_jitsi.xml index e0359d220d..de0c0271bc 100644 --- a/vector/src/main/res/layout/activity_jitsi.xml +++ b/vector/src/main/res/layout/activity_jitsi.xml @@ -1,11 +1,12 @@ - + + android:orientation="vertical" + tools:ignore="UselessParent"> + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/layout/activity_video_media_viewer.xml b/vector/src/main/res/layout/activity_video_media_viewer.xml deleted file mode 100644 index c68577bcd5..0000000000 --- a/vector/src/main/res/layout/activity_video_media_viewer.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/vector/src/main/res/layout/bottom_sheet_bootstrap.xml b/vector/src/main/res/layout/bottom_sheet_bootstrap.xml index 0011bf1987..d3dfe09842 100644 --- a/vector/src/main/res/layout/bottom_sheet_bootstrap.xml +++ b/vector/src/main/res/layout/bottom_sheet_bootstrap.xml @@ -24,9 +24,10 @@ android:importantForAccessibility="no" android:scaleType="fitCenter" android:src="@drawable/ic_security_key_24dp" - android:tint="?riotx_text_primary" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> - - - - - + app:tint="?colorAccent" + tools:ignore="MissingPrefix" /> + app:tint="?colorAccent" + tools:ignore="MissingPrefix" /> + tools:ignore="MissingConstraints,MissingPrefix" + tools:src="@drawable/ic_edit" + app:tint="?riotx_text_primary" /> + tools:visibility="visible" + tools:ignore="MissingPrefix" /> + app:layout_constraintStart_toEndOf="@id/composerEditText" + tools:ignore="MissingPrefix" /> + app:layout_constraintStart_toEndOf="@id/attachmentButton" + tools:ignore="MissingPrefix" /> + tools:src="@drawable/ic_edit" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="@id/composer_related_message_preview" + tools:ignore="MissingPrefix" /> @@ -124,9 +124,7 @@ app:layout_constraintEnd_toStartOf="@+id/composerEditText" app:layout_constraintBottom_toBottomOf="@id/composer_avatar_view" app:layout_constraintStart_toEndOf="@+id/composer_avatar_view" - tools:src="@drawable/ic_shield_black" - tools:visibility="visible" - /> + tools:src="@drawable/ic_shield_black" /> @@ -136,9 +134,10 @@ android:layout_height="48dp" android:background="?android:attr/selectableItemBackground" android:src="@drawable/ic_attachment" - android:tint="?attr/colorAccent" + app:tint="?attr/colorAccent" app:layout_constraintEnd_toStartOf="@+id/sendButton" - app:layout_constraintTop_toBottomOf="parent" /> + app:layout_constraintTop_toBottomOf="parent" + tools:ignore="MissingPrefix" /> + app:layout_constraintVertical_bias="1" + tools:ignore="MissingPrefix" /> + tools:text="@tools:sample/lorem/random" /> \ No newline at end of file diff --git a/vector/src/main/res/layout/custom_action_item_layout_badge.xml b/vector/src/main/res/layout/custom_action_item_layout_badge.xml index f47eaa5d1a..4d1d398559 100644 --- a/vector/src/main/res/layout/custom_action_item_layout_badge.xml +++ b/vector/src/main/res/layout/custom_action_item_layout_badge.xml @@ -13,11 +13,12 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_integrations" - android:tint="@color/riotx_accent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="@color/riotx_accent" + tools:ignore="MissingPrefix" /> + app:tint="?attr/colorAccent" + tools:ignore="MissingPrefix" /> diff --git a/vector/src/main/res/layout/dialog_disclaimer_content.xml b/vector/src/main/res/layout/dialog_disclaimer_content.xml index 40f280b474..dd62741c87 100644 --- a/vector/src/main/res/layout/dialog_disclaimer_content.xml +++ b/vector/src/main/res/layout/dialog_disclaimer_content.xml @@ -26,7 +26,8 @@ android:layout_height="wrap_content" android:layout_margin="32dp" android:src="@drawable/ic_arrow_right" - android:tint="?riotx_text_secondary" /> + app:tint="?riotx_text_secondary" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="@id/exportDialogTil" + app:tint="?attr/colorAccent" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="@id/importDialogTil" + app:tint="?attr/colorAccent" + tools:ignore="MissingPrefix" /> + android:text="@string/no_sticker_application_dialog_content" + app:drawableBottomCompat="@drawable/stickerpack_rabbit" /> diff --git a/vector/src/main/res/layout/dialog_prompt_password.xml b/vector/src/main/res/layout/dialog_prompt_password.xml index 081ce9cdae..8d96d8c68e 100644 --- a/vector/src/main/res/layout/dialog_prompt_password.xml +++ b/vector/src/main/res/layout/dialog_prompt_password.xml @@ -56,8 +56,9 @@ android:background="?attr/selectableItemBackground" android:scaleType="center" android:src="@drawable/ic_eye" - android:tint="?attr/colorAccent" - tools:contentDescription="@string/a11y_show_password" /> + tools:contentDescription="@string/a11y_show_password" + app:tint="?attr/colorAccent" + tools:ignore="MissingPrefix" /> diff --git a/vector/src/main/res/layout/dialog_recovery_key_saved_info.xml b/vector/src/main/res/layout/dialog_recovery_key_saved_info.xml index 36c9a68077..b131d45495 100644 --- a/vector/src/main/res/layout/dialog_recovery_key_saved_info.xml +++ b/vector/src/main/res/layout/dialog_recovery_key_saved_info.xml @@ -1,5 +1,6 @@ + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="@+id/bootstrapAccountPasswordTil" + app:tint="?colorAccent" + tools:ignore="MissingPrefix" /> + android:textSize="12sp" + app:drawableStartCompat="@drawable/ic_alert_triangle" + app:drawableTint="@color/riotx_destructive_accent" /> @@ -71,10 +71,11 @@ android:background="?attr/selectableItemBackground" android:scaleType="center" android:src="@drawable/ic_eye" - android:tint="?colorAccent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/ssss_passphrase_enter_til" - app:layout_constraintTop_toTopOf="@+id/ssss_passphrase_enter_til" /> + app:layout_constraintTop_toTopOf="@+id/ssss_passphrase_enter_til" + app:tint="?colorAccent" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="@+id/bootstrapRecoveryKeyEnterTil" + app:tint="?colorAccent" + tools:ignore="MissingPrefix" /> + android:textSize="14sp" + app:drawableStartCompat="@drawable/ic_warning_badge" /> diff --git a/vector/src/main/res/layout/fragment_deactivate_account.xml b/vector/src/main/res/layout/fragment_deactivate_account.xml index a03f4dba00..db85c607e1 100644 --- a/vector/src/main/res/layout/fragment_deactivate_account.xml +++ b/vector/src/main/res/layout/fragment_deactivate_account.xml @@ -86,8 +86,9 @@ android:background="?attr/selectableItemBackground" android:scaleType="center" android:src="@drawable/ic_eye" - android:tint="?attr/colorAccent" - tools:contentDescription="@string/a11y_show_password" /> + tools:contentDescription="@string/a11y_show_password" + app:tint="?attr/colorAccent" + tools:ignore="MissingPrefix" /> diff --git a/vector/src/main/res/layout/fragment_home_drawer.xml b/vector/src/main/res/layout/fragment_home_drawer.xml index c32342a7a2..e5f595704d 100644 --- a/vector/src/main/res/layout/fragment_home_drawer.xml +++ b/vector/src/main/res/layout/fragment_home_drawer.xml @@ -4,6 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" + android:background="?riotx_background" android:clickable="true" android:focusable="true"> @@ -23,9 +24,10 @@ android:layout_height="@dimen/layout_touch_size" android:scaleType="center" android:src="@drawable/ic_settings_x" - android:tint="?colorAccent" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="?colorAccent" + tools:ignore="MissingPrefix" /> @@ -55,39 +58,71 @@ android:id="@+id/homeDrawerUserIdView" android:layout_width="0dp" android:layout_height="wrap_content" + android:layout_marginEnd="@dimen/layout_horizontal_margin" android:layout_marginBottom="17dp" android:maxLines="1" android:singleLine="true" android:textColor="?riotx_text_secondary" android:textSize="15sp" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toStartOf="@id/homeDrawerHeaderSettingsView" + app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="@+id/homeDrawerHeaderAvatarView" app:layout_constraintTop_toBottomOf="@+id/homeDrawerUsernameView" tools:text="@sample/matrix.json/data/mxid" /> - - - \ No newline at end of file + + + + + + + diff --git a/vector/src/main/res/layout/fragment_keys_backup_restore_from_key.xml b/vector/src/main/res/layout/fragment_keys_backup_restore_from_key.xml index cdbc6d11de..10f953bdf5 100644 --- a/vector/src/main/res/layout/fragment_keys_backup_restore_from_key.xml +++ b/vector/src/main/res/layout/fragment_keys_backup_restore_from_key.xml @@ -17,10 +17,11 @@ android:layout_height="wrap_content" android:layout_marginTop="36dp" android:src="@drawable/key_big" - android:tint="?riotx_text_primary" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="@id/keys_backup_key_enter_til" + app:tint="?attr/colorAccent" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="@+id/keys_backup_passphrase_enter_til" + app:tint="?colorAccent" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + app:layout_constraintVertical_chainStyle="spread" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="@id/keys_backup_passphrase_enter_til" + app:tint="?attr/colorAccent" + tools:ignore="MissingPrefix" /> + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + tools:src="@drawable/ic_logo_matrix_org" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + tools:contentDescription="@string/a11y_show_password" + app:tint="?attr/colorAccent" + tools:ignore="MissingPrefix" /> diff --git a/vector/src/main/res/layout/fragment_login_reset_password.xml b/vector/src/main/res/layout/fragment_login_reset_password.xml index 341435bdc4..5da5298b3d 100644 --- a/vector/src/main/res/layout/fragment_login_reset_password.xml +++ b/vector/src/main/res/layout/fragment_login_reset_password.xml @@ -84,8 +84,9 @@ android:background="?attr/selectableItemBackground" android:scaleType="center" android:src="@drawable/ic_eye" - android:tint="?attr/colorAccent" - tools:contentDescription="@string/a11y_show_password" /> + tools:contentDescription="@string/a11y_show_password" + app:tint="?attr/colorAccent" + tools:ignore="MissingPrefix" /> diff --git a/vector/src/main/res/layout/fragment_login_server_selection.xml b/vector/src/main/res/layout/fragment_login_server_selection.xml index c8b113204b..5f8c2681f0 100644 --- a/vector/src/main/res/layout/fragment_login_server_selection.xml +++ b/vector/src/main/res/layout/fragment_login_server_selection.xml @@ -148,11 +148,12 @@ android:layout_height="wrap_content" android:importantForAccessibility="no" android:src="@drawable/ic_logo_matrix_org" - android:tint="?riotx_text_primary" app:layout_constraintBottom_toTopOf="@+id/loginServerChoiceMatrixOrgText" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - app:layout_constraintVertical_chainStyle="packed" /> + app:layout_constraintVertical_chainStyle="packed" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + app:layout_constraintVertical_chainStyle="packed" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + tools:visibility="visible" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix,UnknownId" /> + android:background="?riotx_background" + android:paddingStart="36dp" + android:paddingTop="@dimen/layout_vertical_margin" + android:paddingEnd="36dp" + android:paddingBottom="@dimen/layout_vertical_margin"> - + + + + + app:layout_constraintTop_toBottomOf="@+id/loginSplashSpace1"> - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + diff --git a/vector/src/main/res/layout/fragment_room_member_list.xml b/vector/src/main/res/layout/fragment_room_member_list.xml new file mode 100644 index 0000000000..e144ddb6e3 --- /dev/null +++ b/vector/src/main/res/layout/fragment_room_member_list.xml @@ -0,0 +1,21 @@ + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/layout/fragment_room_setting_generic.xml b/vector/src/main/res/layout/fragment_room_setting_generic.xml index aa86ee342b..07744436ea 100644 --- a/vector/src/main/res/layout/fragment_room_setting_generic.xml +++ b/vector/src/main/res/layout/fragment_room_setting_generic.xml @@ -53,16 +53,53 @@ - + app:layout_constraintTop_toBottomOf="@+id/roomSettingsToolbar"> + + + + + + + + + + + + + + + diff --git a/vector/src/main/res/layout/fragment_search.xml b/vector/src/main/res/layout/fragment_search.xml new file mode 100644 index 0000000000..330e70d86b --- /dev/null +++ b/vector/src/main/res/layout/fragment_search.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/layout/fragment_ssss_access_from_key.xml b/vector/src/main/res/layout/fragment_ssss_access_from_key.xml index b6bdb2586a..8f8cd59eb1 100644 --- a/vector/src/main/res/layout/fragment_ssss_access_from_key.xml +++ b/vector/src/main/res/layout/fragment_ssss_access_from_key.xml @@ -15,11 +15,12 @@ android:layout_width="24dp" android:layout_height="24dp" android:layout_marginStart="16dp" - android:tint="?riotx_text_primary" + android:src="@drawable/ic_security_key_24dp" app:layout_constraintBottom_toBottomOf="@+id/ssss_restore_with_key" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@+id/ssss_restore_with_key" - android:src="@drawable/ic_security_key_24dp" /> + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + + + + app:layout_constraintTop_toBottomOf="@id/ssss_key_flow" + app:leftIcon="@drawable/ic_alert_triangle" + app:tint="@color/vector_error_color" + app:titleTextColor="?riotx_text_secondary" /> + \ No newline at end of file diff --git a/vector/src/main/res/layout/fragment_ssss_access_from_passphrase.xml b/vector/src/main/res/layout/fragment_ssss_access_from_passphrase.xml index e5482f0ec7..3c5ed1c757 100644 --- a/vector/src/main/res/layout/fragment_ssss_access_from_passphrase.xml +++ b/vector/src/main/res/layout/fragment_ssss_access_from_passphrase.xml @@ -16,10 +16,11 @@ android:layout_height="24dp" android:layout_marginStart="16dp" android:src="@drawable/ic_security_phrase_24dp" - android:tint="?riotx_text_primary" app:layout_constraintBottom_toBottomOf="@+id/ssss_restore_with_passphrase" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="@+id/ssss_restore_with_passphrase" /> + app:layout_constraintTop_toTopOf="@+id/ssss_restore_with_passphrase" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="@+id/ssss_passphrase_enter_til" + app:tint="?colorAccent" + tools:ignore="MissingPrefix" /> @@ -109,16 +111,33 @@ tools:ignore="MissingConstraints" /> + + + app:layout_constraintTop_toBottomOf="@id/ssss_passphrase_flow" + app:leftIcon="@drawable/ic_alert_triangle" + app:tint="@color/vector_error_color" + app:titleTextColor="?riotx_text_secondary" /> + \ No newline at end of file diff --git a/vector/src/main/res/layout/fragment_ssss_reset_all.xml b/vector/src/main/res/layout/fragment_ssss_reset_all.xml new file mode 100644 index 0000000000..d64750eef3 --- /dev/null +++ b/vector/src/main/res/layout/fragment_ssss_reset_all.xml @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/layout/item_bottom_sheet_action.xml b/vector/src/main/res/layout/item_bottom_sheet_action.xml index 242794b340..f082d6b1db 100644 --- a/vector/src/main/res/layout/item_bottom_sheet_action.xml +++ b/vector/src/main/res/layout/item_bottom_sheet_action.xml @@ -30,11 +30,12 @@ android:layout_centerVertical="true" android:layout_toEndOf="@id/actionStartSpace" android:scaleType="center" - android:tint="?riotx_text_secondary" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toEndOf="@+id/actionStartSpace" app:layout_constraintTop_toTopOf="parent" - tools:src="@drawable/ic_room_actions_notifications_all" /> + tools:src="@drawable/ic_room_actions_notifications_all" + app:tint="?riotx_text_secondary" + tools:ignore="MissingPrefix" /> + tools:visibility="visible" + app:tint="@color/riotx_accent" + app:tint="?colorAccent" + tools:ignore="MissingPrefix" /> diff --git a/vector/src/main/res/layout/item_bottom_sheet_message_status.xml b/vector/src/main/res/layout/item_bottom_sheet_message_status.xml index 742fa69a7b..fcb373fa0d 100644 --- a/vector/src/main/res/layout/item_bottom_sheet_message_status.xml +++ b/vector/src/main/res/layout/item_bottom_sheet_message_status.xml @@ -1,5 +1,6 @@ + tools:text="@string/unable_to_send_message" + app:drawableStartCompat="@drawable/ic_warning_badge" /> diff --git a/vector/src/main/res/layout/item_bottom_sheet_room_preview.xml b/vector/src/main/res/layout/item_bottom_sheet_room_preview.xml index bed1d5eb2f..74781468b2 100644 --- a/vector/src/main/res/layout/item_bottom_sheet_room_preview.xml +++ b/vector/src/main/res/layout/item_bottom_sheet_room_preview.xml @@ -64,9 +64,10 @@ android:contentDescription="@string/room_list_quick_actions_settings" android:scaleType="centerInside" android:src="@drawable/ic_room_actions_settings" - android:tint="?riotx_text_secondary" app:layout_constraintBottom_toBottomOf="@+id/bottomSheetRoomPreviewAvatar" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="@id/bottomSheetRoomPreviewAvatar" /> + app:layout_constraintTop_toTopOf="@id/bottomSheetRoomPreviewAvatar" + app:tint="?riotx_text_secondary" + tools:ignore="MissingPrefix" /> diff --git a/vector/src/main/res/layout/item_create_direct_room_user.xml b/vector/src/main/res/layout/item_create_direct_room_user.xml index fa7e742584..108c02cd02 100644 --- a/vector/src/main/res/layout/item_create_direct_room_user.xml +++ b/vector/src/main/res/layout/item_create_direct_room_user.xml @@ -32,8 +32,9 @@ android:layout_height="40dp" android:scaleType="centerInside" android:src="@drawable/ic_material_done" - android:tint="@android:color/white" - android:visibility="visible" /> + android:visibility="visible" + app:tint="@android:color/white" + tools:ignore="MissingPrefix" /> + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/layout/item_group.xml b/vector/src/main/res/layout/item_group.xml index c42d1e26e9..d99c990cc7 100644 --- a/vector/src/main/res/layout/item_group.xml +++ b/vector/src/main/res/layout/item_group.xml @@ -45,10 +45,11 @@ android:layout_height="wrap_content" android:layout_marginEnd="21dp" android:src="@drawable/ic_arrow_right" - android:tint="?riotx_text_primary" app:layout_constraintBottom_toTopOf="@+id/groupBottomSeparator" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="?attr/riotx_text_secondary" + tools:ignore="MissingPrefix" /> + android:visibility="visible" + app:tint="@android:color/white" + tools:ignore="MissingPrefix" /> + tools:contentDescription="@string/a11y_show_password" + app:tint="?attr/colorAccent" + tools:ignore="MissingPrefix" /> diff --git a/vector/src/main/res/layout/item_policy.xml b/vector/src/main/res/layout/item_policy.xml index b5a14b27db..fbf5e39987 100644 --- a/vector/src/main/res/layout/item_policy.xml +++ b/vector/src/main/res/layout/item_policy.xml @@ -54,9 +54,10 @@ android:paddingEnd="0dp" android:rotationY="@integer/rtl_mirror_flip" android:src="@drawable/ic_material_chevron_right_black" - android:tint="?riotx_android_secondary" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="?riotx_android_secondary" + tools:ignore="MissingPrefix" /> \ No newline at end of file diff --git a/vector/src/main/res/layout/item_profile_action.xml b/vector/src/main/res/layout/item_profile_action.xml index 6d3ba36d06..c928a5f59b 100644 --- a/vector/src/main/res/layout/item_profile_action.xml +++ b/vector/src/main/res/layout/item_profile_action.xml @@ -84,11 +84,12 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_arrow_right" - android:tint="?riotx_text_secondary" android:visibility="gone" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" - tools:visibility="visible" /> + tools:visibility="visible" + app:tint="?riotx_text_secondary" + tools:ignore="MissingPrefix" /> diff --git a/vector/src/main/res/layout/item_profile_matrix_item.xml b/vector/src/main/res/layout/item_profile_matrix_item.xml index 1c84a65691..f35141576c 100644 --- a/vector/src/main/res/layout/item_profile_matrix_item.xml +++ b/vector/src/main/res/layout/item_profile_matrix_item.xml @@ -80,10 +80,11 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_arrow_right" - android:tint="?riotx_text_secondary" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="?riotx_text_secondary" + tools:ignore="MissingPrefix" /> diff --git a/vector/src/main/res/layout/item_profile_matrix_item_progress.xml b/vector/src/main/res/layout/item_profile_matrix_item_progress.xml index 66cff4840e..69e4b40bb4 100644 --- a/vector/src/main/res/layout/item_profile_matrix_item_progress.xml +++ b/vector/src/main/res/layout/item_profile_matrix_item_progress.xml @@ -92,11 +92,12 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_arrow_right" - android:tint="?riotx_text_secondary" android:visibility="visible" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" - tools:visibility="visible" /> + tools:visibility="visible" + app:tint="?riotx_text_secondary" + tools:ignore="MissingPrefix" /> diff --git a/vector/src/main/res/layout/item_public_room.xml b/vector/src/main/res/layout/item_public_room.xml index ae8287fdc4..bb9d379f31 100644 --- a/vector/src/main/res/layout/item_public_room.xml +++ b/vector/src/main/res/layout/item_public_room.xml @@ -65,9 +65,7 @@ android:layout_height="wrap_content" android:layout_marginTop="2dp" android:layout_marginEnd="8dp" - android:drawableStart="@drawable/ic_user" android:drawablePadding="8dp" - android:drawableTint="?riotx_text_secondary" android:gravity="center_vertical" android:minWidth="56dp" android:textColor="?riotx_text_secondary" @@ -75,7 +73,9 @@ app:layout_constraintBottom_toTopOf="@+id/itemPublicRoomBottomSeparator" app:layout_constraintStart_toStartOf="@+id/itemPublicRoomName" app:layout_constraintTop_toBottomOf="@+id/itemPublicRoomTopic" - tools:text="148" /> + tools:text="148" + app:drawableTint="?riotx_text_secondary" + app:drawableStartCompat="@drawable/ic_user" /> + android:visibility="visible" + app:tint="@android:color/white" + tools:ignore="MissingPrefix" /> @@ -110,7 +111,9 @@ app:layout_constraintEnd_toStartOf="@+id/roomLastEventTimeView" app:layout_constraintStart_toEndOf="@+id/roomNameView" app:layout_constraintTop_toTopOf="@+id/roomNameView" - tools:visibility="visible" /> + tools:visibility="visible" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + tools:text="@string/room_participants_header_direct_chats" + app:drawableTint="?riotx_text_secondary" /> + tools:text="@sample/matrix.json/data/displayName" + app:drawableEndCompat="@drawable/ic_arrow_right" /> + + + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/layout/item_settings_three_pid.xml b/vector/src/main/res/layout/item_settings_three_pid.xml index fd3443ac17..a175788d86 100644 --- a/vector/src/main/res/layout/item_settings_three_pid.xml +++ b/vector/src/main/res/layout/item_settings_three_pid.xml @@ -13,11 +13,12 @@ android:layout_width="16dp" android:layout_height="16dp" android:scaleType="center" - android:tint="?riotx_text_secondary" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - tools:src="@drawable/ic_phone" /> + tools:src="@drawable/ic_phone" + app:tint="?riotx_text_secondary" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="@color/riotx_destructive_accent" + tools:ignore="MissingPrefix" /> \ No newline at end of file diff --git a/vector/src/main/res/layout/item_signout_action.xml b/vector/src/main/res/layout/item_signout_action.xml index 16d10a3ff0..10292beb55 100644 --- a/vector/src/main/res/layout/item_signout_action.xml +++ b/vector/src/main/res/layout/item_signout_action.xml @@ -1,5 +1,7 @@ + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> @@ -12,11 +13,11 @@ android:layout_marginTop="16dp" android:layout_marginBottom="16dp" android:background="?attr/riotx_keys_backup_banner_accent_color" - android:drawableStart="@drawable/error" android:drawablePadding="16dp" android:gravity="center|start" android:minHeight="80dp" android:padding="16dp" - tools:text="This room is continuation…" /> + tools:text="This room is continuation…" + app:drawableStartCompat="@drawable/error" /> \ No newline at end of file diff --git a/vector/src/main/res/layout/item_timeline_event_merged_utd_stub.xml b/vector/src/main/res/layout/item_timeline_event_merged_utd_stub.xml index 3f0c269e90..538a8930c4 100644 --- a/vector/src/main/res/layout/item_timeline_event_merged_utd_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_merged_utd_stub.xml @@ -25,13 +25,13 @@ android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginStart="8dp" - android:drawableStart="@drawable/ic_clock" android:drawablePadding="2dp" android:gravity="start" android:text="@string/notice_crypto_unable_to_decrypt_merged" android:textColor="?riotx_text_secondary" android:textSize="15sp" - app:drawableTint="?riotx_text_secondary" /> + app:drawableTint="?riotx_text_secondary" + app:drawableStartCompat="@drawable/ic_clock" /> + app:tint="?riotx_text_secondary" + tools:ignore="MissingPrefix" /> + app:tint="?colorAccent" + tools:ignore="MissingPrefix" /> - \ No newline at end of file + diff --git a/vector/src/main/res/layout/item_timeline_event_redacted_stub.xml b/vector/src/main/res/layout/item_timeline_event_redacted_stub.xml index 2d8cc7ff54..becbb3c89c 100644 --- a/vector/src/main/res/layout/item_timeline_event_redacted_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_redacted_stub.xml @@ -2,10 +2,10 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:drawableStart="@drawable/ic_trash_16" android:drawablePadding="8dp" android:gravity="center_vertical" android:text="@string/event_redacted" android:textColor="?riotx_text_primary_body_contrast" android:textSize="14sp" - app:drawableTint="?riotx_text_primary_body_contrast" /> + app:drawableTint="?riotx_text_primary_body_contrast" + app:drawableStartCompat="@drawable/ic_trash_16" /> diff --git a/vector/src/main/res/layout/item_timeline_event_verification_stub.xml b/vector/src/main/res/layout/item_timeline_event_verification_stub.xml index 41a5c37042..cdfbd0add2 100644 --- a/vector/src/main/res/layout/item_timeline_event_verification_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_verification_stub.xml @@ -1,5 +1,6 @@ + tools:text="@string/verification_request" + app:drawableStartCompat="@drawable/ic_shield_black" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> \ No newline at end of file diff --git a/vector/src/main/res/layout/item_uploads_file.xml b/vector/src/main/res/layout/item_uploads_file.xml index 58edcc78a4..5c40327d95 100644 --- a/vector/src/main/res/layout/item_uploads_file.xml +++ b/vector/src/main/res/layout/item_uploads_file.xml @@ -13,10 +13,11 @@ android:layout_height="wrap_content" android:layout_marginStart="@dimen/layout_horizontal_margin" android:src="@drawable/ic_file" - android:tint="?riotx_text_primary" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="?colorAccent" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="?colorAccent" + tools:ignore="MissingPrefix" /> \ No newline at end of file diff --git a/vector/src/main/res/layout/item_verification_action.xml b/vector/src/main/res/layout/item_verification_action.xml index c6b66a7397..c591bb8234 100644 --- a/vector/src/main/res/layout/item_verification_action.xml +++ b/vector/src/main/res/layout/item_verification_action.xml @@ -20,13 +20,14 @@ android:layout_width="48dp" android:layout_height="48dp" android:scaleType="center" - android:tint="?riotx_text_primary" android:visibility="gone" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:src="@drawable/ic_share" - tools:visibility="visible" /> + tools:visibility="visible" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + tools:src="@drawable/ic_arrow_right" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> diff --git a/vector/src/main/res/layout/item_verification_wait.xml b/vector/src/main/res/layout/item_verification_wait.xml index 6cfc497b16..675af87cbf 100644 --- a/vector/src/main/res/layout/item_verification_wait.xml +++ b/vector/src/main/res/layout/item_verification_wait.xml @@ -1,6 +1,7 @@ + app:layout_constraintTop_toBottomOf="@+id/use_latest_riot" + app:tint="?vctr_notice_secondary" + tools:ignore="MissingPrefix" /> + app:layout_constraintTop_toBottomOf="@+id/use_latest_riot" + app:tint="?vctr_notice_secondary" + tools:ignore="MissingPrefix" /> + tools:ignore="MissingConstraints,MissingPrefix" + app:tint="?riotx_text_primary" /> + app:srcCompat="@drawable/ic_back_24dp" + app:tint="@color/white" + tools:ignore="MissingPrefix" /> + app:srcCompat="@drawable/ic_share" + app:tint="@color/white" + tools:ignore="MissingPrefix" /> + app:srcCompat="@drawable/ic_play_arrow" + app:tint="@color/white" + tools:ignore="MissingPrefix" /> + app:drawableTint="@color/white" + app:drawableStartCompat="@drawable/ic_call" /> + tools:text="@string/ongoing_conference_call" + app:drawableStartCompat="@drawable/ic_call" /> - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vector/src/main/res/layout/view_call_controls.xml b/vector/src/main/res/layout/view_call_controls.xml index 94757c2c72..dd04581d84 100644 --- a/vector/src/main/res/layout/view_call_controls.xml +++ b/vector/src/main/res/layout/view_call_controls.xml @@ -23,8 +23,8 @@ android:focusable="true" android:padding="16dp" android:src="@drawable/ic_call" - android:tint="@color/white" - tools:ignore="MissingConstraints" /> + tools:ignore="MissingConstraints,MissingPrefix" + app:tint="@color/white" /> + tools:ignore="MissingConstraints,MissingPrefix" + app:tint="@color/white" /> + tools:ignore="MissingConstraints,MissingPrefix" + app:tint="?attr/riotx_text_primary" /> + tools:ignore="MissingConstraints,MissingPrefix" + tools:src="@drawable/ic_microphone_on" + app:tint="?attr/riotx_text_primary" /> + tools:ignore="MissingConstraints,MissingPrefix" + app:tint="@color/white" /> + tools:ignore="MissingConstraints,MissingPrefix" + app:tint="?attr/riotx_text_primary" /> + tools:ignore="MissingConstraints,MissingPrefix" + app:tint="?attr/riotx_text_primary" /> + tools:src="@drawable/ic_paperclip" + app:tint="?vctr_notice_secondary" + tools:ignore="MissingPrefix" /> \ No newline at end of file diff --git a/vector/src/main/res/layout/view_jump_to_read_marker.xml b/vector/src/main/res/layout/view_jump_to_read_marker.xml index 019480e6fe..06db3f7af7 100644 --- a/vector/src/main/res/layout/view_jump_to_read_marker.xml +++ b/vector/src/main/res/layout/view_jump_to_read_marker.xml @@ -1,5 +1,6 @@ + android:textColor="@color/white" + app:drawableStartCompat="@drawable/arrow_up_circle" /> + app:layout_constraintTop_toTopOf="parent" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> + tools:src="@drawable/ic_noun_party_popper" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> - - diff --git a/vector/src/main/res/menu/home.xml b/vector/src/main/res/menu/home.xml index 3ec3565e8a..389c518471 100644 --- a/vector/src/main/res/menu/home.xml +++ b/vector/src/main/res/menu/home.xml @@ -3,6 +3,12 @@ xmlns:app="http://schemas.android.com/apk/res-auto"> de DE - Nachrichten "Raum Einstellungen Benutzerdetails Historisch - Akzeptiere Ablehnen Anruf beenden - OK Abbrechen @@ -48,7 +45,6 @@ Trotzdem senden oder Einladen - Abmelden Sprachanruf @@ -61,27 +57,22 @@ Schließen In Zwischenablage kopiert Deaktivieren - Bestätigung Warnung - Home Favoriten Personen Räume - Raumnamen filtern Favoriten filtern Personen filtern Raumnamen filtern - Einladungen Niedrige Priorität - Konversationen Lokales Adressbuch @@ -89,17 +80,15 @@ Keine Konversationen Element wurde nicht erlaubt, auf lokale Kontakte zuzugreifen Keine Ergebnisse - Räume Raum-Verzeichnis Keine Räume Keine öffentl. Räume verfügbar - 1 Benutzer - %d Benutzer + %d Benutzer/in + %d Benutzer/innen - Logdateien übermitteln Absturzberichte übermitteln Screenshot übermitteln @@ -112,10 +101,8 @@ Der Fehlerbericht konnte nicht übermittelt werden (%s) Fortschritt (%s%%) Die Anwendung ist während der letzten Benutzung abgestürzt. Möchtest du das Fehler-melden-Fenster öffnen? - Sende Datei nach Gelesen - Raum betreten Benutzername Konto erstellen @@ -124,14 +111,11 @@ Home-Server-URL Identitätsserver-URL Suchen - Neuen Chat starten Sprachanruf starten Videoanruf starten - Dateien senden Foto oder Video aufnehmen - Anmelden Konto erstellen @@ -178,7 +162,6 @@ Dein Passwort wurde zurückgesetzt. \n \nDu wurdest aus allen Sitzungen abgemeldet und wirst keine Push-Benachrichtigungen mehr erhalten. Um die Push-Benachrichtigungen wieder zu aktivieren, musst du dich auf jedem Gerät erneut anmelden. - URL muss mit \'http[s]://\' beginnen Login unmöglich: Netzwerkfehler @@ -187,7 +170,6 @@ Registrierung unmöglich Registrierung fehlgeschlagen: E-Mail-Adresse nicht verifizierbar Bitte eine gültige URL eingeben - Ungültige Zugangsdaten Der verwendete Zugangstoken ist unbekannt Fehlerhaftes JSON @@ -195,35 +177,27 @@ Es wurden zu viele Anfragen gesendet Dieser Benutzername wird bereits verwendet Der Link in der E-Mail wurde noch nicht geöffnet - - Lesebestätigungsliste - - Sende als Original Groß Mittel Klein - Download abbrechen? Upload abbrechen? %d s %1$dm:%2$ds - Gestern Heute - Raumname Raum-Thema - Anruf verbunden Verbindungsaufbau… @@ -233,16 +207,13 @@ Eingehender Video-Anruf Eingehender Sprachanruf Anruf aktiv… - Die Gegenseite hat den Anruf nicht angenommen. Medien-Verbindung fehlgeschlagen Kann Kamera nicht initialisieren Anruf woanders entgegengenommen - Foto oder Video aufnehmen Video kann nicht aufgenommen werden - Information Element benötigt die Berechtigung, auf deine Fotos und Videos zugreifen zu können, um Anhänge zu senden und zu speichern.\n\nBitte erlaube den Zugriff im nächsten Dialog, um Dateien von deinem Gerät zu versenden. @@ -260,25 +231,20 @@ Bitte erlaube den Zugriff im nächsten Dialog, um den Anruf durchzuführen.Element kann dein Adressbuch durchsuchen, um andere Matrix-Nutzer anhand ihrer Email-Adresse und Telefonnummer zu finden. Wenn du der Nutzung deines Adressbuchs zu diesem Zweck zustimmst, erlaube den Zugriff im nächsten Popup-Fenster. Element kann dein Adressbuch durchsuchen, um andere Matrix-Nutzer anhand ihrer E-Mail-Adresse und Telefonnummer zu finden. \nStimmst du der Nutzung deines Adressbuchs zu diesem Zweck zu\? - Entschuldige. Die Aktion wurde aufgrund fehlender Berechtigungen nicht ausgeführt - Gespeichert In Downloads speichern? Ja Nein Fortsetzen - Entfernen Betreten Vorschau Ablehnen - Zur ersten ungelesenen Nachricht springen. - Du wurdest von %s in diesen Raum eingeladen "Diese Einladung wurde an %s gesendet. Es ist nicht mit diesem Konto verknüpft. @@ -286,33 +252,28 @@ Du kannst dich mit einem anderen Konto anmelden oder diese E-Mail-Adresse zu die Du möchtest auf %s zugreifen. Möchtest du den Raum betreten, um an der Diskussion teilzunehmen? einen Raum Dies ist eine Vorschau dieses Raums. Raum-Interaktionen wurden deaktiviert. - Neuer Chat Mitglied hinzufügen 1 Mitglied - Raum verlassen Bist du sicher, dass du den Raum verlassen möchtest? Bist du sicher, dass du %s aus diesem Chat entfernen möchtest? Erstellen - Online Offline Untätig - ADMIN-WERKZEUGE ANRUFE DIREKT-CHATS SITZUNGEN - Einladen Diesen Raum verlassen Aus diesem Raum entfernen Verbannen Verbannung aufheben - Zum normalen Benutzer herabstufen + Zum/r normalen Benutzer/in herabstufen Zum Moderator machen Zum Admin machen Alle Nachrichten dieses Nutzers verbergen @@ -320,20 +281,15 @@ Du kannst dich mit einem anderen Konto anmelden oder diese E-Mail-Adresse zu die Nutzer-ID, Name oder E-Mail-Adresse Erwähnen Sitzungsliste anzeigen - Du wirst diese Änderung nicht rückgängig machen können, da der Benutzer dasselbe Berechtigungslevel wie du selbst erhalten wirst. -Bist du sicher? - + Du wirst diese Änderung nicht rückgängig machen können, da der/die Benutzer!n dasselbe Berechtigungslevel wie du selbst erhalten wirst. Bist du sicher\? "Bist du sicher, dass du %s in diesen Chat einladen willst?" - Mit ID einladen LOKALE KONTAKTE (%d) Nur Matrix-Benutzer - Benutzer per ID einladen Bitte gib eine oder mehrere E-Mail-Adressen oder eine Matrix-ID ein E-Mail or Matrix-ID - Suchen %s schreibt… @@ -350,7 +306,6 @@ Bist du sicher? Nicht gesendete Nachrichten löschen Datei nicht gefunden Du bist nicht berechtigt, in diesen Raum zu schreiben - Vertrauen Nicht vertrauen @@ -363,7 +318,6 @@ Bist du sicher? Das Zertifikat unterscheidet sich von dem Zertifikat, dem dein Gerät ursprünglich vertraut hat. Dies ist SEHR UNGEWÖHNLICH. Es wird empfohlen, dass du dieses neue Zertifikat NICHT AKZEPTIERST. Das Zertifikat hat sich von einem ursprünglich vertrauenswürdigem Zertifikat in ein nicht vertrauenswürdiges Zertifikat geändert. Eventuell wurde das Zertifikat des Servers erneuert. Bitte erkundige dich beim Server-Administrator, welcher Fingerprint als vertrauenswürdig gilt. Akzeptiere das Zertifikat nur dann, wenn der Server-Administrator einen Fingerprint veröffentlicht hat, der mit dem obigen übereinstimmt. - Raum-Details Personen @@ -372,7 +326,6 @@ Bist du sicher? Ungültige ID. Eine E-Mail-Adresse oder eine Matrix-ID (\'@localpart:domain\') ist erforderlich EINGELADEN TEILNEHMER - Grund für das Melden dieses Inhalts Möchtest du alle Nachrichten dieses Nutzers verbergen? @@ -380,7 +333,6 @@ Bist du sicher? Beachte: Diese Aktion wird die App neu starten und einige Zeit brauchen. Upload abbrechen Download abbrechen - Suchen Raum-Mitglieder filtern @@ -389,7 +341,6 @@ Beachte: Diese Aktion wird die App neu starten und einige Zeit brauchen.NACHRICHTEN PERSONEN DATEIEN - BEITRETEN VERZEICHNIS @@ -402,18 +353,15 @@ Beachte: Diese Aktion wird die App neu starten und einige Zeit brauchen.Raum beitreten Einem Raum beitreten Raum-ID oder Raum-Alias eingeben - Verzeichnis durchsuchen Verzeichnis wird durchsucht… - Favorit Niedrige Priorität Direkter Chat Konversation verlassen Vergessen - Nachrichten Einstellungen @@ -422,9 +370,7 @@ Beachte: Diese Aktion wird die App neu starten und einige Zeit brauchen.Nutzungshinweise von Drittanbietern Urheberrechtserklärung Datenschutzerklärung - - Profilbild Anzeigename E-Mail-Adresse @@ -433,22 +379,18 @@ Beachte: Diese Aktion wird die App neu starten und einige Zeit brauchen.Telefonnummer hinzufügen Zeige App-Infos in den Systemeinstellungen. App-Info - Benachrichtigungen für diesen Account aktivieren Benachrichtigungen für diese Sitzung aktivieren Bildschirm für 3 Sekunden aktivieren - Nachrichten in direkten Chats Nachrichten in Gruppen-Chats Wenn ich in einen Raum eingeladen werde Anrufe Nachrichten von Bots - Hintergrundsynchronisierung Hintergrundsynchronisierung aktivieren Timeout für Synchronisierungsanfragen Verzögerung zwischen jeder Synchronisierung - Version OLM Version Nutzungsbedingungen @@ -457,8 +399,6 @@ Beachte: Diese Aktion wird die App neu starten und einige Zeit brauchen.Datenschutzerklärung Cache leeren - - Nutzereinstellungen Benachrichtigungen Ignorierte Benutzer @@ -484,19 +424,15 @@ Um fortzufahren, gib dein Passwort ein. Authentifizierung Passwort: Absenden - Angemeldet als Home-Server Identitätsserver - Verifizierung ausstehend Bitte prüfe deinen E-Mail-Posteingang und klicke auf den in der E-Mail enthaltenen Link. Klicke anschließend auf Fortsetzen. Verifizieren der E-Mail-Adresse nicht möglich. Bitte prüfe deine E-Mails und klicke auf den enthaltenen Link. Anschließend klicke auf Fortsetzen. - Diese E-Mail-Adresse wird bereits verwendet. Diese E-Mail-Adresse wurde nicht gefunden. Diese Telefonnummer wird bereits verwendet. - Passwort ändern Aktuelles Passwort Neues Passwort @@ -506,13 +442,9 @@ Um fortzufahren, gib dein Passwort ein. Alle Nachrichten von %s anzeigen? Beachte: Diese Aktion wird die App neu starten und einige Zeit brauchen. - Bist du sicher, dass du dieses Benachrichtigungsziel entfernen möchtest? - Bist du sicher, dass du %1$s %2$s entfernen möchtest? - Wähle ein Land - Land Bitte wähle ein Land Mobilfunknummer @@ -523,19 +455,16 @@ Beachte: Diese Aktion wird die App neu starten und einige Zeit brauchen.Fehler beim Verifizieren der Telefonnummer Code - Raumbild Raumname Thema Raum-Markierung Markiert als: - Favorit Niedrige Priorität Keine - Zugriff und Sichtbarkeit Diesen Raum im Raum-Verzeichnis anzeigen @@ -543,22 +472,18 @@ Beachte: Diese Aktion wird die App neu starten und einige Zeit brauchen.Lesbarkeit des Chatverlaufs Wer kann den Chatverlauf lesen? Wer kann auf diesen Raum zugreifen? - Jeder Nur Mitglieder (ab dem Zeitpunkt, an dem diese Option ausgewählt wurde) Nur Mitglieder (ab dem Zeitpunkt, an dem sie eingeladen wurden) Nur Mitglieder (ab dem Zeitpunkt, an dem sie beigetreten sind) - Um einen Link zu einem Raum erstellen zu können, muss dieser eine Adresse haben. Nur eingeladene Personen Alle, die den Raum-Link kennen (ausgenommen Gäste) Alle, die den Raum-Link kennen (auch Gäste) - Verbannte Benutzer - Erweitert Interne ID dieses Raumes @@ -570,34 +495,26 @@ Beachte: Diese Aktion wird die App neu starten und einige Zeit brauchen.Du musst dich abmelden, um die Verschlüsselung aktivieren zu können. Nur für verifizierte Sitzungen verschlüsseln Niemals verschlüsselte Nachrichten an unverifizierte Sitzungen in diesem Raum von dieser Sitzung senden. - Dieser Raum hat keine lokalen Adressen Neue Adresse (z. B. #foo:matrix.org) - Ungültiges Adressformat \'%s\' entspricht nicht dem Format von Adressen Du wirst keine Hauptadresse mehr angegeben haben. Hauptadressen-Warnung - Als Hauptadresse setzen Als Hauptadresse aufheben Raum-ID kopieren Raumadresse kopieren - Verschlüsselung ist in diesem Raum aktiviert. Verschlüsselung ist in diesem Raum deaktiviert. Verschlüsselung aktivieren \n(Warnung: Kann nicht wieder deaktiviert werden!) - Verzeichnis - %s versuchte einen bestimmten Punkt in diesem Chatverlauf zu laden, konnte ihn aber nicht finden. - Ende-zu-Ende-Verschlüsselungs-Informationen - Ereignisinformation Nutzer-ID Curve25519-Identitätsschlüssel @@ -605,7 +522,6 @@ Beachte: Diese Aktion wird die App neu starten und einige Zeit brauchen.Algorithmus Sitzungs-ID Entschlüsselungsfehler - Informationen der Absendersitzung Öffentlicher Name Öffentlicher Name @@ -613,7 +529,6 @@ Beachte: Diese Aktion wird die App neu starten und einige Zeit brauchen.Sitzungsschlüssel Verifizierungsstatus Ed25519-Fingerabdruck - Ende-zu-Ende-Verschlüsselungs-Raumschlüssel exportieren Raumschlüssel exportieren Schlüssel in lokale Datei exportieren @@ -623,31 +538,25 @@ Beachte: Diese Aktion wird die App neu starten und einige Zeit brauchen.Die Ende-zu-Ende-Raumschlüssel wurden in \'%s\' gespeichert. Achtung: Diese Datei wird vielleicht gelöscht, wenn die App deinstalliert wird. - Ende-zu-Ende-Raumschlüssel importieren Raumschlüssel importieren Schlüssel aus lokaler Datei importieren Importieren Nur zu verifizierten Sitzungen verschlüsseln Von dieser Sitzung aus keine verschlüsselten Nachrichten an nicht-verifizierte Sitzungen senden. - Nicht verifiziert Verifiziert Auf der Blockierliste - unbekannte Sitzung Nichts - Bestätigen Verifiz. widerrufen Sperren Zulassen - Sitzung verifizieren Vergleiche die folgenden Zeichen mit den Einstellungen in der Sitzung des/der anderen Nutzer!n und bestätige: Falls sie nicht übereinstimmen, wurde die Kommunikation vielleicht kompromittiert. Ich bestätige, dass die Schlüssel übereinstimmen - Raum enthält unbekannte Sitzungen Dieser Raum enthält unbekannte Sitzungen, die noch nicht verifiziert wurden. @@ -655,7 +564,6 @@ Achtung: Diese Datei wird vielleicht gelöscht, wenn die App deinstalliert wird. \nWir empfehlen, den Verifizierungsprozess für jedes Gerät zu durchlaufen, bevor du fortfährst. Du kannst die Nachricht aber auch ohne Verifizierung senden, wenn du das vorziehst. \n \nUnbekannte Sitzungen: - Raum-Verzeichnis auswählen Der Server kann nicht verfügbar oder überlastet sein @@ -663,31 +571,23 @@ Achtung: Diese Datei wird vielleicht gelöscht, wenn die App deinstalliert wird. Home-Server-URL Alle Räume auf dem %s-Server Alle nativen %s-Räume - Suche nach historischen - Nutzer-Oberfläche Sprache Wähle Sprache - Starte beim Systemstart Medien-Cache leeren Medien behalten - Für alle Nachrichten Zeitstempel anzeigen - 3 Tage 1 Woche 1 Monat Unbegrenzt - Offline - Nutzer-Verzeichnis BENUTZER-VERZEICHNIS (%s) Datensparsamer Modus - Schriftgröße Klein Normal @@ -697,14 +597,11 @@ Achtung: Diese Datei wird vielleicht gelöscht, wenn die App deinstalliert wird. Sehr klein Riesig Design - Zeige Zeitstempel im 12-Stunden-Format - Um Widgets in diesem Raum zu verwalten, ist eine Berechtigung erforderlich Widget konnte nicht erstellt werden Konferenzgespräche mit jitsi durchführen Soll das Widget wirklich aus diesem Raum gelöscht werden? - Widget konnte nicht erstellt werden. Berechtigungslevel muss eine positive ganze Zahl sein. @@ -719,10 +616,8 @@ Achtung: Diese Datei wird vielleicht gelöscht, wenn die App deinstalliert wird. Helles Design Dunkles Design Schwarzes Design - Synchronisiere… Auf Ereignisse lauschen - Nachrichten, die meinen Anzeigenamen enthalten Nachrichten, die meinen Benutzernamen enthalten Du hast die neue Sitzung \'%s\' hinzugefügt, die jetzt Verschlüsselungs-Schlüssel anfordert. @@ -731,99 +626,72 @@ Achtung: Diese Datei wird vielleicht gelöscht, wenn die App deinstalliert wird. Anruf Laute Benachrichtigungen Lautlose Benachrichtigungen - Foto aufnehmen Video aufnehmen - Systemeigene Kamera verwenden - Ohne Verifizierung teilen Anfrage ignorieren - Anonymisierte Analysedaten Fehlerbericht - Warnung! Konferenzgespräche werden noch entwickelt und sind vielleicht nicht zuverlässig. - Befehlsfehler Unbekanntes Kommando: %s - Aus Laut - Verschlüsselte Nachricht - Community-Details - Lädt… - Schließen Communities - Community-Namen filtern - Einladen Communities Keine Gruppen - Sicher, dass du einen neuen Chat mit %s starten möchtest\? Sicher, dass du einen Sprachanruf starten möchtest\? Sicher, dass du einen Videoanruf starten möchtest\? - Gruppenliste - Ein Bann führt zu einem Ausschluss eines Nutzers von diesem Raum und verhindert einen erneuten Beitritt. - Alle Nachrichten (laut) Alle Nachrichten Nur Erwähnungen Stumm URL-Vorschau im Chat Vibriere beim Erwähnen eines Nutzers - Benachrichtigungen Dieser Raum zeigt für keine Community Avatare an Neue Community-ID (z.B. +foo:matrix.org) Ungültige Community-ID \'%s\' ist keine valide Community-ID - - Erstellen Community erstellen Community-Name Beispiel Community-ID Beispiel - Startseite Personen Räume Keine Benutzer - Räume Beigetreten Eingeladen Filter Gruppen-Mitglieder Filter Gruppen-Räume - Der Community-Administrator hat keine lange Beschreibung für diese Community zur Verfügung gestellt. - Du wurdest von %2$s aus %1$s gekickt Du wurdest von %2$s aus %1$s verbannt Grund: %1$s Erneut beitreten Raum vergessen Startbildschirm-Verknüpfung hinzufügen - Community Avatare - Schütteln, um einen Fehler zu melden - Aktionen Mitglieder auflisten Synchronisiere… @@ -839,7 +707,6 @@ Achtung: Diese Datei wird vielleicht gelöscht, wenn die App deinstalliert wird. 1 neue Nachricht %d neue Nachrichten - 1 Raum %d Räume @@ -864,17 +731,13 @@ Achtung: Diese Datei wird vielleicht gelöscht, wenn die App deinstalliert wird. 1 aktives Widget %d aktive Widgets - Profilbild - 1 Mitgliederänderung %d Mitgliederänderungen - %1$s in %2$s - Öffne Header Profilbild für Lesebestätigungen Profilbild für Bemerkungen @@ -887,32 +750,25 @@ Achtung: Diese Datei wird vielleicht gelöscht, wenn die App deinstalliert wird. • Der Nachrichteninhalt der Benachrichtigung wird sicher vom Matrix-Home-Server abgerufen • Benachrichtigungen enthalten Metadaten und Nachrichteninhalte • Benachrichtigungen werden den Nachrichteninhalt nicht anzeigen - Benachrichtungs-Datenschutz Element kann im Hintergrund laufen um deine Benachrichtigungen sicher und privat zu verwalten. Dies kann den Energieverbrauch beeinflussen. Berechtigung gewähren Wähle eine andere Option - Sende einen Sticker - Sende Sticker Du hast aktuell keine Stickerpakete aktiviert. \n \nMöchtest du welche hinzufügen\? - Account deaktivieren Deaktiviere meinen Account - Sende Analysedaten Element sammelt anonyme Analysedaten um uns zu helfen, die App zu verbessern. Bitte aktive Analysedaten um uns zu helfen Element zu verbessern. Ja, ich möchte helfen! - Ein benötigter Parameter fehlt. Ein Parameter ist nicht valide. Um %1$s weiter zu verwenden, musst die Geschäftsbedingungen begutachten und ihnen zustimmen. Jetzt prüfen - Account deaktiveren Dies wir dein Konto permanent unbenutzbar machen. Du wirst dich nicht anmelden können und keiner wird denselben Nutzernamen erneut registrieren können. Dies wird dein Konto aus allen Räumen austreten lassen, an denen es teilnimmt und es wird deine Kontoangaben vom Identitätsserver löschen. Diese Aktion ist unumkehrbar. \n @@ -922,37 +778,25 @@ Achtung: Diese Datei wird vielleicht gelöscht, wenn die App deinstalliert wird. Bitte alle Nachrichten, die ich gesendet habe, löschen, wenn mein Account deaktiviert wird (Warnung: Unterhaltungen werden für zukünftige Nutzer unvollständig erscheinen) Um fortzufahren, bitte Passwort eingeben: Account deaktivieren - Drittanbieter-Lizenzen - Download Sprechen Leeren Verschlüsselungsschlüssel von deinen anderen Sitzungen erneut anfragen. - Schlüsselanfrage gesendet. - Anfrage gesendet Bitte öffne Element auf einem anderen Gerät, das die Nachricht entschlüsseln kann, damit es die Schlüssel an diese Sitzung senden kann. - Hier tippen… - Sprachnachricht senden - fortfahren mit… Es wurde keine externe Anwendung gefunden, um diese Aktion auszuführen. - Sende Sprachnachrichten - Bitte gib dein Passwort ein. - Schreibe bitte auf Englisch, wenn möglich. Sende verschlüsselte Antwort… Sende unverschlüsselte Antwort… Zeige Medien vor dem Senden - Du bist aktuell kein Mitglied einer Community. - Benutze die Enter-Taste der Tastatur zum Senden Zeigt Aktionen Bannt Benutzer mit angegebener ID @@ -967,12 +811,10 @@ Achtung: Diese Datei wird vielleicht gelöscht, wenn die App deinstalliert wird. Ändert deinen Anzeigenamen (De-)Aktiviert Markdown Um das Matrix-App-Management zu reparieren - Dieser Raum wurde ersetzt und ist nicht länger aktiv Die Konversation wird hier fortgesetzt Dieser Raum ist die Fortsetzung einer anderen Konversation Klicke hier um die älteren Nachrichten zu sehen - Nicht berechtigt, diese Aktion durchzuführen. 1s @@ -990,14 +832,11 @@ Achtung: Diese Datei wird vielleicht gelöscht, wenn die App deinstalliert wird. 1T %dT - Jetzt %1$s %1$s seit %2$s - "%1$s, " %1$s und %2$s %1$s %2$s - 1 ausgewählt %d ausgewählt @@ -1006,58 +845,43 @@ Achtung: Diese Datei wird vielleicht gelöscht, wenn die App deinstalliert wird. 1 Mitglied %d Mitglieder - 1 Raum %d Räume Systembenachrichtigungen - Ressourcen-Limit Kontaktiere Administrator - kontaktiere deinen Service-Administrator - Dieser Home-Server hat einen seiner Ressourcen-Limits erreicht, sodass einige Nutzer sich nicht anmelden können. Dieser Home-Server hat einen seiner Ressourcen-Limits überschritten. - Dieser Home-Server hat sein Limit an monatlich aktiven Nutzern erreicht, sodass einige Nutzer sich nicht anmelden können. Dieser Home-Server hat sein Limit an monatlich aktiven Nutzern erreicht. - Bitte %s um dieses Limit anheben zu lassen. Bitte %s um diesen Dienst weiter zu nutzen. - Fehler - Raummitglieder bei Bedarf nachladen Verbessere Performanz, indem Raum-Mitglieder erst beim ersten Ansehen geladen werden. Dein Home-Server unterstützt noch nicht das Nachladen von Raummitgliedern. Versuche es später. - Entschuldige, ein Fehler trat auf - Version %s Status.im-Design - Bitte eine Passphrase erstellen um exportierte Schlüssel zu verschlüsseln. Du musst dieselbe Passphrase eingeben um die Schlüssel importieren zu können. Erzeuge Passphrase Passphrasen stimmen nicht überein aufklappen Zusammenklappen - Zeige Infobereich Immer Für Nachrichten und Fehler Nur für Fehler - %1$s: %1$s: %2$s +%d %d+ - Trotzdem anrufen Entfernen Grund - Linkvorschau im Chat aktivieren, falls dein Home-Server diese Funktion unterstützt. Sende Schreibbenachrichtigungen Lasse andere Benutzer wissen, dass du tippst. @@ -1069,11 +893,9 @@ Achtung: Diese Datei wird vielleicht gelöscht, wenn die App deinstalliert wird. Passwort Starte die System-Kamera anstelle der angepassten Kamera. Diese Option erfordert eine externe Anwendung um Sprachnachrichten aufzuzeichnen. - Das Kommando \"%s\" braucht mehr Parameter oder einige Parameter sind inkorrekt. Markdown wurde aktiviert. Markdown wurde deaktiviert. - Zeige Betreten- und Verlassen-Ereignisse Zeige Konto-Ereignisse Enthält Änderungen des Profilbilds und des Anzeigenamens. @@ -1081,62 +903,49 @@ Achtung: Diese Datei wird vielleicht gelöscht, wenn die App deinstalliert wird. Nutze den Standard-Klingelton von Element für eingehende Anrufe Klingelton für eingehende Anrufe Wähle Klingelton für Anrufe: - Akzeptieren - Bitte prüfe und akzeptiere die Richtlinien dieses Home-Servers: - Tests ausführen Läuft… (%1$d von %2$d) Einer oder mehrere Tests sind fehlgeschlagen. Versuche vorgeschlagene Lösung(en). Einer oder mehrere Tests sind fehlgeschlagen. Bitte sende einen Fehlerbericht, damit dies untersucht werden kann. - Systemeinstellungen. Benachrichtigungen sind in den Systemeinstellungen aktiviert. Notifications sind in den Systemeinstellungen deaktiviert. Bitte überprüfe die Systemeinstellungen. Öffne Einstellungen - Kontoeinstellungen. Benachrichtigungen sind für dein Konto eingeschaltet. Notifications sind für dein Konto deaktiviert. Bitte überprüfe die Kontoeinstellungen. Aktiviere - Sitzungseinstellungen. Benachrichtigungen sind für diese Sitzung aktiviert. Benachrichtigungen sind für diese Sitzung nicht aktiviert. \nBitte überprüfe die Einstellungen für Element. Aktiviere - Element benutzt Google Play Dienste um Push-Nachrichten zu übermitteln, doch scheinen sie nicht korrekt konfiguriert zu sein: %1$s Repariere Play-Dienste - Firebase-Token FCM-Token erfolgreich abgefragt: \n%1$s Abfragen des FCM-Tokens fehlgeschlagen: \n%1$s - FCM-Token erfolgreich beim Home-Sserver registriert. FCM-Token konnte nicht beim Home-Server registriert werden: %1$s - Benachrichtigungsdienst Benachrichtigungsdienst läuft. Benachrichtigungsdienst läuft nicht. Versuche die Anwendung neuzustarten. Starte Dienst - Automatischer Neustart des Benachrichtigungsdienstes Dienst wurde automatisch gestoppt und erneut gestartet. Dienst konnte nicht neugestartet werden - Starte beim Hochfahren Dienst wird starten, wenn das Gerät neu gestartet wird. Dieser Dienst wird nicht starten, wenn das Gerät neu gestartet wird. Du wirst keine Benachrichtigungen bekommen bis Element einmal geöffnet wurde. Aktiviere das Starten beim Hochfahren - Überprüfe Hintergrund-Einschränkungen Hintergrund-Einschränkungen sind für Element deaktiviert. Dieser Test sollte über mobile Daten ausgeführt werden (kein WLAN). %1$s @@ -1144,7 +953,6 @@ Versuche die Anwendung neuzustarten. \nDie App wird aggressiv eingeschränkt, während sie im Hintergrund arbeiten möchte. Dies könnte Benachrichtigungen beeinflussen. \n%1$s Einschränkungen deaktivieren - Batterieoptimierung Element wird nicht von Batterieoptimierungen beeinflusst. Fehler bei Benachrichtigungen finden @@ -1155,50 +963,36 @@ Versuche die Anwendung neuzustarten. Token-Registrierung Wenn ein Benutzer ein abgestecktes Gerät mit ausgeschaltetem Bildschirm eine Weile nicht bewegt, wechselt es in den Doze-Modus. Dies hindert Apps daran, auf das Netzwerk zuzugreifen und verzögert die Ausführung von Aufgaben, Synchronisierungen und Standard-Alarmen. Ignoriere Optimierungen - Hintergrundverbindung "Element muss eine Hintergrundverbindung (nur geringe Belastung) aufrechterhalten, um verlässliche Benachrichtigungen zu erhalten. \nAuf dem nächsten Bildschirm wirst Du gefragt, ob Du Element erlauben möchtest im Hintergrund zu laufen. Bitte akzeptieren." Berechtigung gewähren - Beim Verifizieren deiner E-Mail-Adresse trat ein Fehler auf. - Beim Verifizieren deiner Telefonnummer trat ein Fehler auf. Weitere Information: %s - Keine validen Google-Play-Dienste gefunden. Benachrichtigungen könnten nicht richtig funktionieren. - Videogespräch aktiv… - Schlüssel-Sicherung Schlüssel-Sicherung verwenden Schlüsselsicherung ist nicht abgeschlossen. Bitte warten… - Überspringen Fertig - Erweiterte Benachrichtigungseinstellungen Angepasste Einstellungen. Beachte, dass einige Nachrichtentypen leise sind (erzeugen eine Benachrichtigung aber keinen Ton). Einige Benachrichtigungen sind in deinen erweiterten Einstellungen deaktiviert. Konnte angepasste Regeln nicht laden. Bitte erneut versuchen. Einstellungen überprüfen - Konto hinzufügen - Laute Benachrichtigungen einstellen Anrufbenachrichtigung einstellen Lautlose Benachrichtigungen einstellen Wähle LED-Farbe, Vibration, Ton… - - Stumm Bitte eine Passphrase eingeben Passphrase ist zu schwach - Bitte lösche die Passphrase, wenn Element einen Wiederherstellungs-Schlüssel erzeugen soll. Keine Matrix-Sitzung verfügbar - Verliere nie wieder verschlüsselte Nachrichten Setze Passphrase Erledigt @@ -1211,21 +1005,15 @@ Versuche die Anwendung neuzustarten. Sicherung gestartet Bist du sicher\? Wiederherstellungsschlüssel eingeben - Nachrichtenwiederherstellung - Stelle Backup wieder her: Historie entschlüsseln Von Sicherung wiederherstellen Sicherung löschen - Lösche Sicherung… Konnte Sicherung nicht löschen (%s) - Lösche Sicherung - Präferenz der Benachrichtigungen nach Ereignis - [%1$s] Dieser Fehler ist außerhalb von Element passiert. Google sagt, dass dieses Gerät zu viele Apps registriert hat um FCM zu nutzen. Der Fehler taucht nur auf, wenn sehr viele Apps installiert sind. Er sollte also den Durchschnittsnutzer nicht betreffen. [%1$s] @@ -1234,33 +1022,25 @@ Dieser Fehler ist außerhalb von Element passiert. Google sagt, dass dieses Ger Dieser Fehler ist außerhalb von Element passiert. Es gibt kein Google-Konto auf dem Gerät. Bitte füge ein Google-Konto hinzu. Verwaltung der Krypto-Schlüssel Schlüssel-Sicherung verwalten - Nachrichten in verschlüsselten Räumen sind mit Ende-zu-Ende-Verschlüsselung gesichert. Nur du und der/die Empfänger!nnen haben die Schlüssel um diese Nachrichten zu lesen. \n \nSichere deine Schlüssel, um sie nicht zu verlieren. Der Wiederherstellungsschlüssel wurde nach \'%s\' gespeichert. \n \nWarnung: Diese Datei wird gelöscht, wenn die Anwendung deinstalliert wird. - Wiederherstellungsschlüssel aus Passphrase generieren. Dies kann mehrere Sekunden brauchen. Deine Verschlüsselungsschlüssel werden nun im Hintergrund auf deinem Home-Server gesichert. Die initiale Sicherung kann mehrere Minuten dauern. - - Du verlierst möglicherweise den Zugang zu deinen Nachrichten, wenn du dich abmeldest oder das Gerät verlierst. - Rufe Backup-Version ab… Nutze deine Wiederherstellungspassphrase, um deinen verschlüsselten Chatverlauf lesen zu können nutze deinen Wiederherstellungsschlüssel Wenn du deine Wiederherstellungspassphrase nicht weist, kannst du %s. - Nutze deinen Wiederherstellungsschlüssel, um deinen verschlüsselten Chatverlauf lesen zu können Hast du deinen Wiederherstellungsschlüssel verloren\? Du kannst einen neuen in den Einstellungen einrichten. Sicherung konnte mit dieser Passphrase nicht entschlüsselt werden. Bitte stelle sicher, dass du die korrekte Wiederherstellungspassphrase eingegeben hast. Netzwerkfehler: Bitte überprüfe die Verbindung und versuche es erneut. - Gib deinen Wiederherstellungsschlüssel ein Sicherung konnte mit diesem Wiederherstellungsschlüssel nicht entschlüsselt werden. Bitte stelle sicher, dass du den korrekten Wiederherstellungsschlüssel eingegeben hast. - Sicherung wiederhergestellt %s ! Backup mit %d Schlüssel wiederhergestellt. @@ -1270,16 +1050,11 @@ Dieser Fehler ist außerhalb von Element passiert. Es gibt kein Google-Konto auf %d neuer Schlüssel wurde dieser Sitzung hinzugefügt. %d neue Schlüssel wurden dieser Sitzung hinzugefügt. - Konnte letzte Version der Wiederherstellungsschlüssel nicht laden (%s). Sitzungsverschlüsselung ist nicht aktiviert - - Die Schlüsselsicherung wurde für diese Sitzung korrekt eingerichtet. Die Schlüsselsicherung ist in dieser Sitzung nicht aktiv. Deine Schlüssel werden von dieser Sitzung nicht gesichert. - - Sicherung hat eine Signatur von einer unbekannten Sitzung mit der ID %s. Sicherung hat eine valide Sicherung von dieser Sitzung. Die Sicherung hat eine gültige Signatur von der verifizierten Sitzung %s. @@ -1287,10 +1062,8 @@ Dieser Fehler ist außerhalb von Element passiert. Es gibt kein Google-Konto auf Die Sicherung hat eine ungültige Signatur von der verifizierten Sitzung %s Die Sicherung hat eine ungültige Signatur von der nicht verifizierten Sitzung %s Abfrage der Vertrauensinformationen für die Sicherung fehlgeschlagen (%s). - Um die Schlüsselsicherung für diese Sitzung zu verwenden, stelle sie jetzt mit deiner Passphrase oder deinem Wiederherstellungsschlüssel wieder her. Deine gesicherten Verschlüsselungsschlüssel vom Server löschen\? Du wirst deinen Wiederherstellungsschlüssel nicht mehr nutzen können, um deinen verschlüsselten Chatverlauf zu lesen. - Beim Ausloggen gehen deine verschlüsselten Nachrichten verloren Schlüssel-Sicherung wird durchgeführt. Wenn du dich jetzt ausloggst, gehen deine verschlüsselten Nachrichten verloren. Schlüssel-Sicherung sollte bei allen Sitzungen aktiviert sein, um den Verlust verschlüsselter Nachrichten zu verhindern. @@ -1300,20 +1073,15 @@ Dieser Fehler ist außerhalb von Element passiert. Es gibt kein Google-Konto auf Sicher\? Sicherung Alle verschlüsselten Nachrichten gehen verloren, wenn Du dich ausloggst ohne eine Sicherung der Schlüssel gemacht zu haben. - Bleiben Abbrechen - Sicher, dass du dich ausloggen möchtest\? - Wiederherstellung verschlüsselter Nachrichten Bitte gib einen Benutzernamen ein. Der datensparsame Modus sorgt dafür, dass die Präsenz- und Tipp-Benachrichtigungen ausgefiltert werden. - Richte Schlüsselsicherung ein (Erweitert) Schlüssel manuell exportieren - Schütze deine Sicherung mit einer Passphrase. Eine verschlüsselte Kopie deiner Schlüssel wird auf deinem Home-Server gespeichert. Schütze deine Sicherung mit einer Passphrase. Für maximale Sicherheit sollte diese *nicht* dein Konto-Passwort sein. Erstelle Sicherung @@ -1333,46 +1101,36 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Ich war es Verliere nie mehr verschlüsselte Nachrichten Richte die Schlüsselsicherung ein - Verliere nie wieder verschlüsselte Nachrichten Benutze Schlüsselsicherung - Neue sichere Schlüssel für Nachrichten Verwalte Schlüsselsicherung - Sichere Schlüssel… - Alle Schlüssel sind gesichert Sichere %d Schlüssel… Sichere %d Schlüssel… - Version Algorithmus Signatur - Berechne Wiederherstellungsschlüssel… Lade Schlüssel herunter… Importiere Schlüssel… Ignorieren - Mit Single-Sign-On anmelden Diese URL ist nicht erreichbar, bitte prüfen Dein Gerät nutzt eine veraltetes TLS-Sicherheitsprotokoll, das anfällig für Angriffe ist. Zu deiner Sicherheit wirst du nicht in der Lage sein, dich zu verbinden Schicke Nachricht mit Eingabetaste Eingabetaste der Bildschirmtastatur schickt die Nachricht ab, statt einen Zeilenumbruch zu erzeugen - Passwort aktualisieren Das Passwort ist ungültig Passwörter stimmen nicht überein - Ungültige Antwort beim Entdecken des Home-Servers Serveroptionen vervollständigen Element hat eine benutzerdefinierte Serverkonfiguration für die Domäne deines Benutzernamens gefunden \"%1$s\": \n%2$s Nutze Konfiguration - Initialisiere Dienst Medien Standard-Komprimierung @@ -1380,7 +1138,6 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Standard-Medienquelle Wählen Auslöseton abspielen - Als gelesen markieren Die App braucht sich nicht im Hintergrund mit dem Home-Server verbinden, dies sollte die Akkunutzung reduzieren @@ -1391,18 +1148,14 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine %d Nachricht %d Nachrichten - Neues Ereignis Raum Neue Nachrichten Neue Einladung Ich ** Fehler beim Senden - bitte Raum öffnen - Entschuldigung, Konferenzanrufe mit Jitsi werden auf älteren Geräten (mit älteren Android-Versionen als 5.0) nicht unterstützt - Sitzung verifizieren - Unbekannte IP-Adresse Eine neue Sitzung fordert Verschlüsselungsschlüssel an. \nSitzungsname: %1$s @@ -1412,86 +1165,67 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine \nSitzungsname: %1$s \nZuletzt gesehen: %2$s \nWenn du nicht eine andere Sitzung angemeldet hast, ignoriere diese Anfrage. - Verifizieren Teilen Ignorieren - Auf deinem Home-Server existiert bereits eine Sicherung Ersetzen Stopp - Überprüfe Sicherungsstatus Du wurdest abgemeldet, da deine Anmeldedaten falsch oder abgelaufen sind. - Verifizieren durch Vergleichen eines kurzen Textes. Starte Verifizierung Eingehende Verifizierungsanfrage Du hast eine eingehende Verifizierungsanfrage erhalten. Anfrage ansehen Warte auf Bestätigung des/r anderen Nutzer*in… - Verifiziert! Du hast diese Sitzung erfolgreich verifiziert. Sichere Nachrichten mit diesem Benutzer sind Ende-zu-Ende verschlüsselt und können nicht von Dritten mitgelesen werden. Verstanden - Schlüssel-Verifizierung Anfrage abgebrochen Die Verifizierung wird abgebrochen. \nGrund: %s - Interaktive Sitzungs-Verifizierung Verifizierungsanfrage %s möchte deine Sitzung verifizieren - Der Benutzer hat die Verifizierung abgebrochen Der Verifizierungsprozess ist abgelaufen Die Sitzung hat eine unerwartete Nachricht erhalten Eine ungültige Nachricht wurde empfangen Unbekannter Fehler - - Bearbeiten Antworten - Wiederholen Trete einem Raum bei um die App zu benutzen. Hat dir eine Einladung gesendet Eingeladen von %s - Du bist auf dem neuesten Stand! Du hast keine weiteren, ungelesenen Nachrichten Willkommen zu Hause! Unterhaltungen Räume Deine Räume werden hier angezeigt - Reaktionen Zustimmen Mag ich Reaktion hinzufügen Reaktionen ansehen Reaktionen - Ereignis von Benutzer gelöscht Ereignis moderiert durch Raum-Administrator Zuletzt bearbeitet von %1$s am %2$s - - Neuen Raum erstellen Kein Netzwerk. Bitte überprüfe deine Internetverbindung. Ändern Netzwerk wechseln Bitte warten… Alle Communities - Für diesen Raum kann keine Vorschau angezeigt werden Die Vorschau von öffentlichen Räumen wird von Element noch nicht unterstützt - Räume Direktnachrichten - Neuer Raum ERSTELLEN Raumname @@ -1499,22 +1233,17 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Jeder wird diesem Raum beitreten können Raumverzeichnis Veröffentliche diesen Raum ins Raumverzeichnis - Integrationsmanager - Kein Integrationsmanager konfiguriert. Schlüsselaustausch anfragen Es sieht so aus, als hättest du bereits ein Setup-Schlüssel-Backup von einer anderen Sitzung. Möchtest du es durch das, was du gerade erstellt hast, ersetzen\? Für maximale Sicherheit empfehlen wir, dies persönlich zu tun, oder ein anderes vertrautes Kommunikationsmedium zu nutzen. Überprüfe diese Sitzung, um sie als vertrauenswürdig zu markieren. Sitzungen von anderen Nutzer*innen zu vertrauen gibt dir zusätzliche Sicherheit bei der Verwendung von Ende-zu-Ende verschlüsselten Nachrichten. Durch Verifizieren dieser Sitzung wird sie bei dir und deinem Gegenüber als vertrauenswürdig markiert. - Verifiziere diese Sitzung, indem du bestätigst, dass das folgende Emoji auf dem Bildschirm deines Gegenübers angezeigt wird Verifiziere diese Sitzung, indem du bestätigst, dass die folgenden Zahlen auf dem Bildschirm deines Gegenübers angezeigt werden - Es ist nichts aufgetaucht\? Noch nicht alle Clients unterstützen die interaktive Verifikation. Nutze die alte Verifikation.. Verwende die alte Verifizierungsmethode. - Die Sitzung kennt diese Transaktion nicht Die Hash-Verpflichtung stimmte nicht überein Die SAS stimmte nicht überein @@ -1525,13 +1254,10 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Fehlerhaftes Ereignis, kann nicht angezeigt werden Beim Abrufen der Vertrauensinformationen ist ein Fehler aufgetreten Beim Abrufen der Schlüsselsicherungsdaten ist ein Fehler aufgetreten - Matrix SDK Version Sonstige Hinweise Dritter Du siehst diesen Raum bereits! - Schnelle Reaktionen - Allgemein Einstellungen Sicherheit & Privatsphäre @@ -1539,79 +1265,55 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Push-Regeln Keine Push-Regeln definiert Keine registrierten Push-Gateways - Sprache & Video Hilfe & Über - - Token registrieren - Mache einen Vorschlag Bitte schreibe unten deine Anmerkungen. Beschreibe hier deine Anmerkung Versteckte Ereignisse in der Zeitleiste anzeigen - Direkte Nachrichten - Warten… Miniaturbild wird verschlüsselt… Datei wird verschlüsselt… (bearbeitet) - Nachrichtenbearbeitung Keine Änderungen gefunden - Gespräche filtern… Sende eine neue Direktnachricht Das Raumverzeichnis anzeigen - Link in die Zwischenablage kopiert - Mit Matrix-ID hinzufügen Raum erstellen… Bearbeitungsverlauf anzeigen - Die andere Partei hat die Überprüfung abgebrochen. \n%s Die Sitzung kann sich nicht auf eine Schlüsselvereinbarung, eine Hash-, eine MAC- oder eine SAS-Methode einigen - E2E-Schlüssel aus der Datei \"%1$s\" importieren. - Vielen Dank, der Vorschlag wurde erfolgreich gesendet Der Vorschlag konnte nicht gesendet werden (%s) - Miniaturbild wird gesendet (%1$s / %2$s) Datei wird gesendet (%1$s / %2$s) - Datei %1$s wird heruntergeladen … Die Datei %1$s wurde heruntergeladen! - - Kannst du nicht finden, wonach du suchst\? Erstelle einen neuen Raum Name oder ID (#beispiel:matrix.org) - Aktiviere Wischen, um in der Zeitleiste zu antworten - Kein Ergebnis gefunden. Verwende \'Mit Matrix-ID hinzufügen\', um auf dem Server zu suchen. Beginne mit der Eingabe, um Ergebnisse zu erhalten Filtern nach Benutzername oder ID… - Raum betreten… - Ablehnen - App-ID: Überprüfung Keine Widerrufen Trennen Kein Integrationsserver konfiguriert. - Anruf aufgrund eines falsch konfigurierten Servers fehlgeschlagen Versuche es mit %s Nicht erneut fragen - Richte eine E-Mail für die Kontowiederherstellung ein. Optional, kannst du später einrichten, dass Personen dich über diese Adresse finden. Richte eine Telefonnummer ein. Später kannst du einrichten, dass Personen dich über diese finden. Lege eine E-Mail-Adresse für die Kontowiederherstellung fest. Später kann optional eine E-Mail-Adresse oder eine Telefonnummer dazu verwendet werden, um von anderen Personen gefunden zu werden. @@ -1621,19 +1323,14 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Optimiert für die Echtzeit Keine Hintergrundsynchronisation Fehler beim Aktualisieren der Einstellungen. - - Bevorzugtes Synchronisationsintervall Auffindbarkeit Öffentlicher Name (sichtbar für Personen, mit denen du kommunizierst) Der öffentliche Name der Sitzung ist für Personen sichtbar, mit denen du kommunizierst Um fortzufahren, musst du die Nutzungsbedingungen akzeptieren. - Du verwendest keinen Identitätsserver Es ist kein Identitätsserver konfiguriert. Du musst dein Kennwort zurücksetzen. - Du versuchst anscheinend, eine Verbindung zu einem anderen Home-Server herzustellen. Möchtest du dich abmelden\? - Push-Key: App-Anzeigename: Url: @@ -1641,10 +1338,7 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Nutzungsbedingungen überprüfen Für andere auffindbar sein Verwende Bots, Bridges, Widgets und Sticker-Pakete - Gelesen von - - Identitätsserver Verbindung zum Identitätsserver trennen Identitätsserver konfigurieren @@ -1652,11 +1346,9 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Auffindbare E-Mail-Adressen Erkennungsoptionen werden angezeigt, sobald du eine E-Mail hinzugefügt hast. ausstehend - Gib einen neuen Identitätsserver ein Konnte keine Verbindung zum Home-Server herstellen Latn - Bitte frage den/die Administrator/in deines Home-Servers (%1$s) nach der Einrichtung eines TURN-Servers, damit Anrufe zuverlässig funktionieren. \n \nAlternativ kann ein öffentlicher Server auf %2$s genutzt werden. Dies wird jedoch weniger zuverlässig sein und deine IP-Adresse wird gegenüber diesem Server preisgegeben. Du kannst den Server auch in den Einstellungen anpassen. @@ -1667,8 +1359,6 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Passwort bestätigen Du kannst dies nicht auf einem mobilen Element tun Authentifizierung benötigt - - Hintergrund-Synchronisierungsmodus Element wird sich im Hintergrund auf eine Art synchronisieren die Ressourcen des Geräts (Akku) schont. \nAbhängig vom Ressourcen-Status deines Geräts kann dein System die Synchronisierung verschieben. @@ -1693,22 +1383,17 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Dein Design Widget-ID Raum-ID - - Dieses Widget möchte folgende Ressourcen benutzen: Erlauben Alle blockieren Kamera benutzen Mikrofon benutzen Lese DRM-geschützte Medien - Du wirst nicht über eingehende Nachrichten benachrichtigt, wenn die App im Hintergrund ist. Verwalte deine Erkennungseinstellungen. Zugriff für mich zurückziehen - Sitzungsname: Format: - Du nutzt aktuell %1$s um vorhandene Kontakte zu finden und um von dir bekannten Kontakten gefunden zu werden. Du benutzt aktuell keinen Identitätsserver. Um zu entdecken und um von dir bekannten Kontakten entdeckt zu werden, richte unten einen ein. Auffindbare Telefonnummern @@ -1716,21 +1401,15 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Identitätsserver hat keine Nutzungsbedingungen Der Identitätsserver den du ausgewählt hast, hat keine Nutzungsbedingungen. Fahre nur fort, wenn du dem/r Besitzer!n des Dienstes vertraust Eine Textnachricht wurde an %s gesendet. Bitte gib den Verifizierungscode ein, den sie enthält. - Aktiviere ausführliche Logs. Ausführliche Logs werden der Entwicklung der App dadurch helfen, dass mehr Informationen übertragen werden, wenn du einen Fehlerbericht sendest. Auch wenn dies aktiviert ist, werden keine Nachrichteninhalte oder andere privaten Daten aufgezeichnet. - - Bitte erneut versuchen, nachdem du die Nutzungsbedingungen deines Home-Servers akzeptiert hast. - Bei Benutzung könnten Cookies gesetzt werden und es könnten Daten mit %s geteilt werden: Bei Benutzung könnten Daten mit %s geteilt werden: Optionen zum Finden werden erscheinen, sobald du eine Telefonnummer hinzugefügt hast. Wir haben dir eine Bestätigungsmail an %s gesendet. Prüfe dein Postfach und klicke auf den Bestätigungslink Es sieht aus, als würde der Server zu viel Zeit benötigen, um zu antworten. Der Grund kann eine schlechte Verbindung oder ein Fehler mit dem Server sein. Bitte versuche es später erneut. - Anhang senden - Navigationsmenü öffnen Raumerstellungsmenü öffnen Schließe das Raumerstellungsmenü… @@ -1740,7 +1419,6 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Passwort anzeigen Passwort verstecken Zum Ende springen - gelesen von %1$s, %2$s und %3$s gelesen von %1$s und %2$s gelesen von %s @@ -1748,9 +1426,7 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine gelesen von einem Nutzer gelesen von %d Nutzern - Die Datei \'%1$s\' (%2$s) ist zu groß, um sie hochzuladen. Das Limit ist %3$s. - Beim Abrufen des Anhangs ist ein Fehler aufgetreten. Datei Kontakt @@ -1765,7 +1441,6 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Meldegrund MELDEN NUTZER IGNORIEREN - Inhalt gemeldet Dieser Inhalt wurde gemeldet. \n @@ -1778,15 +1453,11 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Dieser Inhalt wurde als unangebracht gemeldet \n \nWenn du keine weiteren Inhalte dieses Nutzers mehr sehen möchtest, kannst du ihn blockieren, um seine Nachrichten auszublenden - Element benötigt Berechtigungen, um deine E2E Schlüssel zu speichern. \n \nBitte erlaube den Zugriff im nächsten Pop-Up sodass du deine Schlüssel manuell exportieren kannst. - Aktuell besteht keine Netzwerkverbindung - Nutzer ignorieren - Alle Nachrichten (laut) Alle Nachrichten Nur Erwähnungen @@ -1796,17 +1467,12 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine %1$s hat keine Änderungen gemacht Sendet die Nachricht als Spoiler Du ignorierst keine Nutzer - Halte auf einem Raum um mehr Optionen anzuzeigen - - %1$s hat den Raum für jeden, der den Link hat, öffentlich gemacht. Ungelesene Nachrichten - Privat oder in Gruppen mit Leuten chatten Halte Gespräche mittels Verschlüsselung privat Beginne - Wähle einen Server Genau wie bei E-Mails haben Accounts ein Zuhause, auch wenn du mit jedem kommunizieren kannst Folge Millionen anderen kostenlos auf dem größten öffentlichen Server @@ -1814,14 +1480,11 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Mehr erfahren Andere Benutzerdefinierte & erweiterte Einstellungen - Fortfahren Eine Trennung von deinem Identitätsserver würde bedeuten, dass du weder von anderen Nutzern gefunden werden, noch diese per E-Mail oder Telefonnummer einladen kannst. Du teilst deine Email Adressen oder Telefonnummern momentan auf dem Identitätsserver %1$s. Du wirst dich erneut mit %2$s verbinden müssen, um mit dem Teilen aufzuhören. Stimme den Nutzungsbedingungen des Identitätsservers (%s) zu, um zu erlauben per E-Mail oder Telefonnummer gefunden zu werden. - Zu teilende Daten nicht verarbeitbar - Erweitere & individualisiere dein Benutzererlebnis Verbinde mit %1$s Mit Element Matrix Services verbinden @@ -1830,7 +1493,6 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Registrieren Anmelden Mit einmaligem Anmelden fortfahren - Element Matrix Services Adresse Adresse Ein Fehler beim Laden der Seite %1$s (%2$d) ist aufgetreten @@ -1838,32 +1500,24 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Die Anwendung kann kein neues Benutzerkonto auf diesem Server erstellen. \n \nMöchtest du dich über eine Web-Anwendung anmelden\? - Diese E-Mail-Adresse ist mit keinem Benutzerkonto verknüpft. - Passwort auf %1$s zurücksetzen E-Mail Neues Passwort - Achtung! Eine Änderung deines Passworts wird alle Ende-zu-Ende-Verschlüsselungsschlüssel zurücksetzen. Dein verschlüsselter Chatverlauf wird dadurch unlesbar. Richte eine Schlüsselsicherung ein oder exportiere deine Raumschlüssel von einer anderen Sitzung bevor du dein Passwort zurücksetzt. Fortfahren - Diese E-Mail-Adresse ist mit keinem Benutzerkonto verknüpft - Prüfe deinen Posteingang Eine Bestätigungsemail wurde an %1$s versendet. Klicke auf den Link um dein neues Passwort zu bestätigen. Sobald du dem enthaltenen Link gefolgt bist, klicke unten. Ich habe meine E-Mail-Adresse bestätigt - Erfolgreich! Dein Passwort wurde zurückgesetzt. Zurück zur Anmeldung - Dein Passwort wurde noch nicht geändert. \n \nMöchtest du die Passwortänderung stoppen\? - E-Mail-Adresse angeben E-Mail E-Mail (optional) @@ -1879,24 +1533,20 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Dein Benutzerkonto ist noch nicht erstellt. \n \nMöchtest du die Registrierung stoppen\? - matrix.org auswählen modular auswählen Benutzerdefinierten Server auswählen Bedingungen akzeptieren um fortzufahren - Bitte überprüfe deine E-Mails Wir haben dir eine E-Mail an %1$s gesendet. \nBitte öffne den darin enthaltenen Link, um mit der Benutzerkontoerstellung fortzufahren. Der eingegebene Code ist nicht korrekt. Bitte überprüfe deine Eingabe. Beginne zu Tippen um eine Reaktion zu finden. - %1$s hat den Raum auf \"nur-einladen\" gestellt. Es ist deine Konversation. Mache sie dir zu eigen. Premium-Hosting für Organisationen Gib die Adresse des Modular Element oder Servers ein, den du verwenden möchtest Gibt die Adresse eines Servers oder eines Element ein, zu dem du dich verbinden möchtest - Die Anwendung kann sich nicht bei diesem Home-Server anmelden. Der Home-Server unterstützt die folgenden Anmeldetypen: %1$s. \n \nMöchtest du dich mit einem Webclient anmelden\? @@ -1906,17 +1556,13 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Warnung Lege eine E-Mail-Adresse fest, um dein Konto wiederherzustellen. Später kannst du optional zulassen, dass Personen dich anhand dieser E-Mail-Adresse entdecken. Weiter - Lege Telefonnummer fest Lege eine Telefonnummer fest, damit Personen dich anhand dieser entdecken können. Bitte verwende das internationale Format. Weiter - Weiter - Internationale Telefonnummern müssen mit \'+\' beginnen Die Telefonnummer scheint ungültig zu sein. Bitte prüfen - Anmelden bei %1$s Benutzername Weiter @@ -1924,14 +1570,11 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Bitte löse das Captcha Veralteter Home-Server Auf diesem Home-Server läuft eine zu alte Version, um eine Verbindung herzustellen. Bitten deine Home-Server-Administration um ein Upgrade. - Es wurden zu viele Anfragen gesendet. Versuche es erneut in %1$d Sekunde… Es wurden zu viele Anfragen gesendet. Versuche es erneut in %1$d Sekunden… - Gesehen von - Du bist abgemeldet Dies kann verschiedene Gründe haben: \n @@ -1941,7 +1584,6 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine \n \n• Die Administration deines Servers hat deinen Zugriff aus Sicherheitsgründen ungültig gemacht. Melde dich erneut an - Du bist abgemeldet Anmelden Deine Home-Server-Administration (%1$s) hat dich von deinem Konto %2$s (%3$s) abgemeldet. @@ -1953,7 +1595,6 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine \n \nDeaktiviere diese Option, wenn dieses Gerät nicht mehr verwendet wird oder sich bei einem anderen Konto angemeldet werden soll. Alle Daten löschen - Daten löschen Alle aktuell auf diesem Gerät gespeicherten Daten löschen\? \nMelde dich erneut an, um auf deine Kontodaten und Nachrichten zuzugreifen. @@ -1961,12 +1602,9 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Daten löschen Die aktuelle Sitzung gehört dem/der Benutzer!n%1$s. Die angegebenen Anmeldeinformationen sind von Benutzer!n %2$s. Dies wird nicht von Element unterstützt. \nBitte zuerst die Daten löschen und dann erneut anmelden. - matrix.to-Link fehlerhaft Die Beschreibung ist zu kurz - Initiale Synchronisierung… - Alle meine Sitzungen anzeigen Erweiterte Einstellungen Entwicklungsmodus @@ -1978,26 +1616,19 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Einstellungen Aktuelle Sitzung Andere Sitzungen - Zeigt nur die ersten Ergebnisse, gib mehr Buchstaben ein… - Ausfallsicher Element kann häufiger abstürzen, wenn ein unerwarteter Fehler auftritt - Stellt einer Klartextnachricht ¯\\_(ツ)_/¯ voran - Aktiviere Verschlüsselung Nach der Aktivierung kann die Verschlüsselung nicht deaktiviert werden. - Deine E-Mail-Domain ist nicht berechtigt, sich auf diesem Server zu registrieren - Nicht vertrauenswürdige Anmeldung Sie stimmen überein Sie stimmen nicht überein Verifiziere diese/n Benutzer!n, indem du bestätigst, dass diese einzigartigen Emoji in derselben Reihenfolge auf dem Bildschirm deines Gegenübers angezeigt werden. Für ultimative Sicherheit verwende ein anderes vertrauenswürdiges Kommunikationsmittel oder mache es persönlich. Suche nach dem grünen Schild, um sicherzustellen, dass ein/e Benutzer!n vertrauenswürdig ist. Vertraue allen Benutzer!nnen in einem Raum, um sicherzustellen, dass der Raum sicher ist. - Nicht sicher Eine der folgenden Möglichkeiten kann beeinträchtigt sein: \n @@ -2005,12 +1636,10 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine \n - Der Home-Server mit dem dein Gegenüber verbunden ist \n - Deine oder die Internetverbindung des Gegenüber \n - Dein Gerät oder das Gerät des Gegenüber - Video. Bild. Audio Datei - Warten… %s brach ab Du hast abgebrochen @@ -2018,25 +1647,17 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Du hast akzeptiert Verifizierung gesendet Verifizierung angefragt - - Verifiziere diese Sitzung Manuelle Verifizierung - Ich - Scanne den Code mit dem Gerät des Gegenüber für eine gegenseitige Überprüfung Scanne ihren/seinen Code Kann nicht scannen Wenn ihr nicht am selben Ort seid, vergleicht Emoji stattdessen - Verifizieren via Emoji-Vergleich - Mit Emoji verifizieren Wenn du den obigen Code nicht scannen kannst, verifiziert, indem ihr eine kurze, eindeutige Auswahl an Emoji vergleicht. - QR-Code-Bild - %s verifizieren %s verifiziert Warte auf %s… @@ -2059,52 +1680,37 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Uploads Raum verlassen Verlasse den Raum… - Administratoren Moderierende benutzerdefiniert Eingeladen Nutzer!n - Admin in %1$s Moderation in %1$s Springen & als gelesen markieren - Element kann keine Ereignisse vom Typ \'%1$s\' Element beherrscht keine Nachrichten vom Typ \'%1$s\' Element ist beim verarbeiten des Ereignisinhalts mit der ID \'%1$s\' auf ein Problem gestoßen - Nicht ignorieren - Diese Sitzung kann diese Verifizierung nicht mit deinen anderen Sitzungen teilen. \nDie Überprüfung wird lokal gespeichert und in einer zukünftigen Version der App freigegeben. - Neueste Räume Andere Räume - Sendet die angegebene Nachricht in Regenbogenfarben Sendet das angegebene Emote in Regenbogenfarben - Zeitleiste - Nachrichteneditor - Aktivieren Ende-zu-Ende-Verschlüsselung Einmal aktiviert kann die Verschlüsselung nicht rückgängig gemacht werden. - Verschlüsselung aktivieren\? Nach der Aktivierung kann die Verschlüsselung für einen Raum nicht deaktiviert werden. In einem verschlüsselten Raum gesendete Nachrichten können vom Server nicht gesehen werden, nur von den Teilnehmenden des Raums. Durch die Verschlüsselung funktionieren viele Bots und Bridges möglicherweise nicht ordnungsgemäß. Verschlüsselung aktivieren - Um sicher zu gehen, verifiziere %s, indem ein einmaligen Code überprüft wird. Um sicher zu sein, tut dies persönlich oder verwendet einen anderen Kommunikationsweg. - Vergleiche die einzigartigen Emoji und stell sicher, dass sie in derselben Reihenfolge angezeigt werden. Vergleiche den Code mit dem Code auf dem Bildschirm deines Gegenübers. Nachrichten mit diesem Gegenüber sind Ende-zu-Ende verschlüsselt und können nicht von Dritten gelesen werden. Deine neue Sitzung ist jetzt verifiziert. Sie hat Zugriff auf deine verschlüsselten Nachrichten, und andere Benutzer!nnen sehen sie als vertrauenswürdig an. - - Cross-Signing Cross-Signing ist aktiviert \nPrivate Schlüssel auf dem Gerät. @@ -2114,55 +1720,38 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Cross-Signing ist aktiviert \nSchlüssel sind nicht vertrauenswürdig Cross-Signing ist nicht aktiviert - - Aktive Sitzungen Zeige alle Sitzungen Sitzungen verwalten Diese Sitzung abmelden - Keine kryptografischen Informationen verfügbar - Diese Sitzung ist für sichere Nachrichtenübertragung vertrauenswürdig, da du sie überprüft hast: Verifiziere diese Sitzung, um sie als vertrauenswürdig zu markieren, und gewähren ihr Zugriff auf verschlüsselte Nachrichten. Wenn du dich nicht bei dieser Sitzung angemeldet hast, ist dein Konto möglicherweise gefährdet: - Eine aktive Sitzung %d aktive Sitzungen - Verifiziere diese Sitzung Andere Benutzer!nnen vertrauen ihr möglicherweise nicht Vollständige Sicherheit - Nutze eine vorhandene Sitzung um diese Sitzung zu verifizieren und ihr Zugriff auf verschlüsselte Nachrichten zu gewähren. - - Verifizieren Verifiziert Warnung - Sitzungen konnten nicht abgerufen werden Sitzungen Vertraut Nicht vertraut - Diese Sitzung ist für sichere Nachrichtenübertragung vertrauenswürdig, weil %1$s (%2$s) sie verifiziert hat: %1$s (%2$s) hat sich in einer neuen Sitzung angemeldet: Bis diese/r Benutzer!n dieser Sitzung vertraut, werden an und von ihr/ihm gesendete Nachrichten mit Warnungen gekennzeichnet. Alternativ kannst du dies manuell überprüfen. - - Initialisiere Cross-Signing Schlüssel zurücksetzen - QR-Code - Fast geschaft! Zeigt %s dasselbe Schild an\? Ja Nein - Verbindung zum Server wurde unterbrochen - Entwicklungswerkzeuge Kontodaten @@ -2177,52 +1766,38 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Erstellt eine einfache Umfrage Nutze eine Wiederherstellungsmethode Wenn du auf keine existierende Sitzung zugreifen kannst - Neue Anmeldung - Kann keine Geheimnisse im Speicher finden Gib die geheime Speicherpassphrase ein Warnung: Du solltest nur von einem vertrauenswürdigen Gerät auf den geheimen Speicher zugreifen - Entfernen… Möchtest du diesen Anhang an %1$s senden\? Sende Bild in Originalgröße Sende Bilder in Originalgröße - Entfernen bestätigen Möchtest du dieses Ereignis wirklich entfernen (löschen)\? Beachte, dass beim Löschen eines Raumnamens oder einer Themenänderung die Änderung rückgängig gemacht werden kann. Grund hinzufügen Grund für das Editieren - Ereignis gelöscht von Benutzer!n, Grund: %1$s Ereignis vom Raumadministration moderiert, Grund: %1$s - Schlüssel sind bereits aktuell! - Spoiler Benutzerdefiniert (%1$d) in %2$s - Element Android - Schlüsselanforderungen - Schalte den verschlüsselten Nachrichtenverlauf frei - Neu laden - Neue Anmeldung. Warst du das\? Tippe für eine Überprüfung & Verifikation Benutze diese Sitzung um deine neue zu verfizieren, damit sie auf verschlüsselte Nachrichten zugreifen kann. Das war ich nicht Dein Account ist möglicherweise komprimittiert - Wenn du abbrichst, wirst du auf diesem Gerät keine verschlüsselten Nachrichten lesen können, und andere Benutzer werden ihm nicht vertrauen Wenn du abbrichst, wirst du auf deinem neuen Gerät keine verschlüsselten Nachrichten lesen können, und andere Benutzer werden ihm nicht vertrauen Du wirst %1$s (%2$s) nicht verifizieren, wenn du jetzt abbrichst. Beginne in deren Nutzerprofil erneut. - Eines der folgenden könnte kom­pro­mit­tie­rt sein: \n \n- Dein Passwort @@ -2231,29 +1806,20 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine \n- Die Internetverbindung, die von den Geräten genutzt wird \n \nWir empfehlen dir dein Passwort & Wiederherstellungsschlüssel in den Einstellungen sofort zu ändern. - Verifiziere deine Geräte in den Einstellungen. Verifizierung abgebrochen - Generiere einen Nachrichtenschlüssel - Bestätige %s - Gibe dein %s ein um fortzufahren. - Gib deine %s für eine Bestätigung erneut ein. Benutze dein Accountpasswort nicht mehrfach. - - Dies könnte einige Sekunden dauern, gedulde dich bitte. Wiederherstellung einrichten. Dein Wiederherstellungsschlüssel Geschafft! Bewahre ihn sicher auf Abschließen - Benutze diesen %1$s als Sicherheit für den Fall, dass du deine %2$s vergisst. - Veröffentliche erstellte Identitätsschlüssel Generiere sicheren Schlüssel von der Passphrase Definiere SSSS Standardschlüssel @@ -2261,65 +1827,46 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Synchronisiere Benutzerschlüssel Synchronisiere selbstsignierenden Schlüssel Richte Schlüsselbackup ein - - Deine %2$s & %1$s sind nun eingerichtet. \n \nBewahre sie sicher auf! Du wirst sie benötigen, um verschlüsselte Nachrichten und sichere Informationen freizuschalten, wenn du alle deine aktive Sitzungen verlierst. - Speichere ihn auf einem USB-Stick oder auf einem Sicherungslaufwerk Import der Schlüssel fehlgeschlagen - Benachrichtigungskonfiguration Nachrichten, die @raum enthalten Verschlüsselte Nachrichten in Gruppenchats Setze die Benachrichtigungspräferenz abhängig vom Ereignistyp - Sendet eine Nachricht als einfachen Text, ohne sie als Markdown zu interpretieren - Inkorrekter Benutzername und/oder Passwort. Das eingegebene Passwort beginnt oder endet mit Leerzeichen, bitte kontrolliere es. - Nachrichtenschlüssel Kontopasswort - Wiederherstellungs-Passphrase Druck es aus und speichere es an einem sicheren Ort Kopier es in deinen persönlichen Cloud-Speicher - Verschlüsselung ist nicht aktiviert Dies kann nicht von einem mobilen Gerät erfolgen - Wenn Räume verbessert werden Verschlüsselung aktiviert Nachrichten in diesem Raum sind Ende-zu-Ende verschlüsselt. Erfahre mehr & verifiziere Benutzer in deren Profil. Die Verschlüsselung in diesem Raum wird nicht unterstützt - Warte auf %s… - %s setzen Fehlerbehebung %s hat den Raum erstellt und konfiguriert. - Fast geschafft! Zeigt das andere Gerät das gleiche Schild an\? Fast geschafft! Warte auf Bestätigung… Verschlüsselte Nachrichten in 1:1 Chats Nachricht… - Verifiziere dich & andere, um eure Chats zu schützen - Gib zum Fortfahren deinen %s ein Datei benutzen - Dies ist kein gültiger Wiederherstellungsschlüssel Bitte gib deinen Wiederherstellungsschlüssel ein - Verschlüsselte Nachrichten und Verifizierungen mit einer %s absichern und entsperren. Das setzen eines Wiederherstellungspassworts ermöglicht das sichern & entsperren von verschlüsselten Nachrichten und Verifizierungen. \n \nWenn du kein Nachrichtenpasswort setzen willst, erzeuge stattdessen einen Nachrichtenschlüssel. Das setzen eines Wiederherstellungspassworts ermöglicht das sichern & entsperren von verschlüsselten Nachrichten und Verifizierungen. - - Verschlüsselungsupgrade verfügbar %s eingeben Wiederherstellungspasswort @@ -2331,19 +1878,15 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Generiere SSSS Schlüssel aus dem Wiederherstellungsschlüssel Speichere Schlüsselbackup Schlüssel in SSSS %1$s (%2$s) - Gib dein Passwort für das Schlüsselbackup ein, um fortzufahren. nutze deinen Schlüsselbackup Wiederherstellungsschlüssel Wenn du dein Schlüsselbackup Passwort nicht weißt, kannst du %s. Schlüsselbackup Wiederherstellungsschlüssel - Verhindere Screenshots innerhalb der Anwendung Das Aktivieren dieser Einstellung setzt das FLAG_SECURE in allen Aktivitäten. Starte die Anwendung neu, damit die Änderung wirksam wird. - Datei wurde der Galerie hinzugefügt Datei konnte nicht zur Galerie hinzugefügt werden Neues Benutzerpasswort festlegen… - Nutze die neueste Version von Element auf deinen anderen Geräten, Element Web, Element Desktop, Element iOS, Element für Android oder einen anderen cross-signing fähigen Matrix client Element Web \nElement Desktop @@ -2358,30 +1901,25 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Wähle deinen Wiederherstellungsschüssel, gib ihn ein oder füge ihn aus der Zwischenablage ein Sicherung konnte mit diesem Wiederherstellungsschlüssel nicht entschlüsselt werden. Bitte stelle sicher, dass du den korrekten Wiederherstellungsschlüssel eingegeben hast. Konnte nicht auf gesicherten Speicher zugreifen - Unverschlüsselt Verschlüsselt von einem unbekannten Gerät Überprüfe, wo du angemeldet bist Verifiziere alle deine Sitzungen, um sicherzustellen, dass dein Konto & deine Nachrichten sicher sind Bestätige neue Anmeldung zu deinem Konto: %1$s - Verifiziere manuell mit einem Text Verifiziere Anmeldung Verifiziere interaktiv mit Emojis Bestätige deine Identität, indem du diesen Login von einer deiner anderen Sitzungen verifizierst, um Zugriff auf deine verschlüsselten Nachrichten zu erhalten. Als vertraut markieren - Bitte wähle einen Benutzernamen. Bitte wähle ein Passwort. Überprüfe diesen Link genau Dieser Link %1$s bringt dich zu einer anderen Seite: %2$s. \n \nWillst du wirklich fortfahren\? - Konnte Direktnachricht nicht erzeugen. Prüfe die Nutzer, die du einladen willst und versuche es erneut. %1$s: %2$s %1$s: %2$s %3$s - Nutzer!n hinzufügen EINLADEN Benutzer werden eingeladen… @@ -2393,20 +1931,15 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Einladungen gesendet an %1$s und %2$d weitere Benutzer Wir konnten den Benutzer nicht einladen. Bitte überprüfe den Benutzernamen, welchen du einladen möchtest und versuche es erneut. - Pause Kopieren Benachrichtigungen Element-Anruf fehlgeschlagen Abspielen Ablehnen - - Erfolg - Echtzeitverbindung konnte nicht hergestellt werden. \nBitte den/die Administrator/in deines Home-Servers, einen TURN-Server so zu konfigurieren, dass Anrufe zuverlässig funktionieren. - Wähle Audiogerät aus Telefonie Lautsprecher @@ -2417,18 +1950,14 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Hintere Kamera Deaktiviere HD-Qualität Aktiviere HD-Qualität - SSL-Fehler: Die Identität des Gegenüber ist noch nicht verifiziert. SSL Fehler. Aktiver Anruf (%s) Zum Anruf zurückkehren - Einladung zurückziehen Möchtest du dich zurückstufen\? Du kannst die Zurückstufung nicht rückgängig machen und du wirst die Rechte nur mit einem anderen berechtigten Benutzer im Raum zurückerlangen können. Zurückstufen - - Benutzer ignorieren Durch das Ignorieren werden für dich alle Nachrichten des Nutzers ausgeblendet. \nDu kannst die Aktion jederzeit in den allgemeinen Einstellungen rückgängig machen. @@ -2444,7 +1973,6 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Grund für den Bann Bann des Benutzers aufheben Das Aufheben des Bannes wird dem Benutzer erlauben dem Raum wieder beizutreten. - Sicheres Backup Verwalten Backup einrichten @@ -2453,49 +1981,36 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Absicherung um den Zugriffsverlust auf verschlüsselte Nachrichten & Daten zu verhindern, indem die Schlüssel für die Entschlüsselung auf dem Server gesichert werden. Generiere einen neuen Sicherheitsschlüssel oder setze eine neue Sicherheitspassphrase für dein existierendes Backup. Dieses wird deinen aktuellen Schlüssel oder deine aktuelle Phrase ersetzen. - Integrationen sind deaktiviert Aktiviere \'Erlaube Integrationen\' in den Einstellungen um dies zu machen. - %d gebannter Benutzer %d gebannte Benutzer - Schlüssel erfolgreich exportiert - ANSICHT Aktive Widgets - - Der Sicherheitsschlüssel ist gespeichert worden. - Backup Absicherung gegen den Zugriffsverlust auf verschlüsselte Nachrichten & Daten - Richte Backup ein - Nachricht gelöscht Zeige gelöschte Nachrichten Zeige einen Platzhalter für gelöschte Nachrichten Dedizierten Tab für ungelesene Nachrichten zur Hauptansicht hinzufügen - Wir haben dir eine Bestätigungsmail an %s gesendet. Bitte prüfe deine E-Mails und klicke auf den Bestätigungslink Der Verifizierungscode ist nicht korrekt. - MEDIEN Es gibt in diesem Raum keine Medien DATEIEN %1$s um %2$s Es gibt in diesem Raum keine Dateien - Füge zu Favoriten hinzu Entferne von Favoriten Du hast keine Änderungen gemacht Du hast den Raum für alle, die den Link kennen, zugänglich gemacht. Du hast den Raumbeitritt auf Einladungen beschränkt. Gib die Adresse des Servers ein, den du benutzen möchtest - Wenn du deine Matrixkennung und dein Passwort weißt, kannst du alternativ diese Methode nutzen: Einloggen mit Matrix-ID Einloggen mit Matrix-ID @@ -2504,30 +2019,22 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Wenn du dein Passwort nicht weißt, gehe zurück um es zurücksetzen zu lassen. Dies ist keine gültige Benutzerkennung. Erwartetes Format: \'@benutzer:homeserver.org\' Es konnte kein gültiger Home-Server gefunden werden. Bitte prüfe deine Kennung - Sticker - Administrative Aktionen Standard in %1$s Dein Serveradministrator hat in privaten Räumen & Direktnachrichten Ende-zu-Ende Verschlüsselung standardmäßig deaktiviert. Flugzeugmodus ist aktiv - Gib eine Sicherheitsphrase ein, die nur du kennst. Diese wird benutzt um deine Daten auf dem Server geheim zu halten. - Wenn du jetzt abbrichst und den Zugriff zu deinen Sitzungen verlierst, kannst du verschlüsselte Nachrichten & Daten verlieren. \n \nDu kannst auch ein Backup einrichten & deine Schlüssel in den Einstellungen verwalten. - Du hast den Raum erstellt und konfiguriert. - Dieser Account ist deaktiviert worden. - Aktiviere Cross Signing Konnte Mediendatei nicht speichern Aktuelle Sprache Andere verfügbare Sprachen Lade verfügbare Sprachen… - Öffne AGBs von %s Trenne Verbingung zu Identitätsserver %s\? Dieser Identitätsserver ist veraltet. Element unterstützt nur API V2. @@ -2537,7 +2044,6 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Deiner Privatssphäre wegen unterstützt Element nur das Senden gehashter Emailaddressen und Telefonnummern. Die Assoziierung ist fehlgeschlagen. Für diese Kennung gibt es aktuell keine Assoziierung. - Dein Homeserver (%1$s) schlägt %2$s als Identitätsserver vor Benutze %1$s Alternativ kannst du die URL eines beliebigen anderen Identitätsservers angeben @@ -2550,31 +2056,24 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Aktiviere Mikrophon Stoppe Kamera Starte Kamera - Richte Backup ein - Backup Absicherung um den Zugriffsverlust auf verschlüsselte Nachrichten & Daten zu verhindern, indem die Schlüssel für die Entschlüsselung auf dem Server gesichert werden. Benutze einen Sicherheitsschlüssel Generiere einen Sicherheitsschlüssel, welcher z.B. in einem Passwortmanager oder in einem Tresor sicher aufbewahrt werden sollte. Benutze Sicherheitsphrase Gib eine geheime Phrase ein, die nur du kennst und generiere einen Schlüssel als Backup. - Speichere deinen Sicherheitsschlüssel Bewahre deinen Sicherheitsschlüssel irgendwo sicher auf, wie z.B. in einem Passwortmanager oder in einem Tresor. - Setze Sicherheitsphrase Gib eine Sicherheitsphrase ein, welche nur du kennst und deine Daten auf dem Server geheim halten soll. Sicherheitsphrase Gib deine Sicherheitsphrase zur Bestätigung erneut ein. - Speichere deinen Sicherheitsschlüssel Bewahre deinen Sicherheitsschlüssel irgendwo sicher auf, wie z.B. in einem Passwortmanager oder in einem Tresor. - Raumname Thema Du hast die Raumeinstellungen erfolgreich geändert - Du kannst auf diese Nachricht nicht zugreifen Warte auf diese Nachricht. Das könnte eine Weile dauern Kann nicht entschlüsselt werden @@ -2583,17 +2082,12 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Du kannst auf diese Nachricht nicht zugreifen, weil der Sender deiner Sitzung nicht vertraut Du kannst auf diese Nachricht nicht zugreifen, weil der Sender absichtlich die Schlüssel nicht gesendet hat Warte auf Verschlüsselungshistorie - Riot heißt nun Element! Wir sind begeistert unsere Namensänderung mitteilen zu können! Deine App ist auf dem neusten Stand und du bist mit deinem Account angemeldet. VERSTANDEN MEHR ERFAHREN - element - - Speichere Wiederherstellungsschlüssel in - Füge über Kontaktliste hinzu Deine Kontaktliste ist leer Kontaktliste @@ -2601,13 +2095,10 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Ermittle deine Kontakte… Deine Kontaktliste ist leer Kontaktliste - Einladung zurücknehmen Einladung zu %1$s zurücknehmen\? - Gebannt von %1$s Aufheben des Banns fehlgeschlagen - Push-Benachrichtigungen sind deaktiviert Gehe zu deinen Einstellungen um Push-Benachrichtigungen zu aktivieren Nutze eine PIN für mehr Sicherheit @@ -2621,7 +2112,6 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Aktiviere PIN Wenn du deine PIN zurücksetzen möchtest, tippe \"PIN vergessen\" um dich abzumelden und sie anschließend zurückzusetzen. Bestätige PIN um die PIN zu deaktivieren - \"Nicht entschlüsselbar\"-Fehler im Chatverlauf zusammenfassen Verhindere versehentliche Anrufe Bitte um Bestätigung, bevor du einen Anruf tätigst Konfiguration @@ -2638,13 +2128,11 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine %1$d/%2$d Schlüssel erfolgreich importiert. %1$d/%2$d Schlüssel erfolgreich importiert. - Verwalte Integrationen Keine aktiven Widgets Der Raum wurde erstellt, aber manche Einladungen wurden aus folgendem Grund nicht versendet: \n \n%s - Von %1$s, %2$s und %3$d anderem gelesen Von %1$s, %2$s und %3$d anderen gelesen @@ -2654,7 +2142,7 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Falscher code, %d verbleibende Versuche Warnung! Letzter Versuch bevor du ausgeloggt wirst! - Zu viele Fehler, du bist ausgeloggt worden + Zu viele Fehler. Du wurdest ausgeloggt Diese Telefonnummer ist bereits registriert. Deinem Konto wurde keine Telefonnummer hinzugefügt E-mail-Adressen @@ -2662,26 +2150,28 @@ Verwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie eine Telefonnummern %s entfernen\? Stelle sicher, dass du auf den Link in der E-Mail geklickt hast, die wir dir gesendet haben. - E-Mail-Adressen und Telefonnummern Verwalte E-Mails und Telefonnummern, die mit deinem Matrix-Konto verknüpft sind - Code Verwende das internationale Format (Telefonnummer muss mit \'+\' beginnen) Bestätige deine Identität, indem du dieses Login verifizierst, um Zugriff auf verschlüsselte Nachrichten zu erhalten. Leider ist dieser Vorgang für Konten, die über Single Sign-On verbunden sind, noch nicht möglich. - Raum, indem du gebannt wurdest, kann nicht geöffnet werden. Raum kann nicht gefunden werden. Stelle sicher, dass er existiert. %d Sekunde %d Sekunden - Zeige Status-Ereignisse der Raum-Mitglieder Bezieht Einladungs-/Beitritts-/Verlassen-/Entfernen-/Verbannen-Ereignisse und Avatar-/Anzeigenamen-Wechsel mit ein. Umfrage Bot-Schaltflächen Reagierte mit: %s - Der Link war nicht korrekt - + Der Link war fehlerhaft + Du bist nicht berechtigt, einen Anruf in diesem Raum zu starten + Ergebnis der Überprüfung + Kontodaten vom Typ %1$s löschen\? +\n +\nVorsicht! Es kann zu unerwartetem Verhalten führen. + Zurücksetzen + \ No newline at end of file diff --git a/vector/src/main/res/values-el/strings.xml b/vector/src/main/res/values-el/strings.xml index 499f4a0549..87d1089b7a 100644 --- a/vector/src/main/res/values-el/strings.xml +++ b/vector/src/main/res/values-el/strings.xml @@ -31,7 +31,7 @@ Αποστολή πληροφοριών Έναρξη συνομιλίας Ο διακομιστής είναι μη διαθέσιμος ή υπερφορτωμένος - Προβολή του αποκρυπτογραφημένου κώδικα + Προβολή αποκρυπτογραφημένου κώδικα Προβολή κώδικα Σήμερα @@ -69,11 +69,11 @@ Αργότερα Μπροστά - Διαγαρφή + Διαγραφή Μετονομασία Αναφορά περιεχομένου Ενεργή κλήση - βίντεο + Βίντεο Κάποια χαρακτηριστικά μπορεί να μην είναι διαθέσιμα λόγω ελλιπών δικαιωμάτων… Αυτή η ενέργεια δεν είναι δυνατή λόγω ελλιπών δικαιωμάτων. Πληροφορίες συσκευής @@ -151,8 +151,8 @@ %1$s : %1$s: %2$s +%d - ήχος - Η κλήση δεν ήταν δυνατή, παρακαλώ δοκιμάστε αργότερα + Ήχος + Δεν είναι δυνατή η έναρξη της κλήσης, δοκιμάστε αργότερα Η κλήση δεν ήταν δυνατή Πάντα Για μηνύματα και σφάλματα @@ -338,4 +338,29 @@ Άτομα Αναζήτηση ατόμων ΑΤΟΜΑ + Λτνκ + + Αρχικοποίηση υπηρεσίας + Αναμονή για συμβάντα + Δημιουργία αντιγράφων ασφαλείας κλειδιών + Χρησιμοποιήστε το αντίγραφο ασφαλείας κλειδιών + Επαλήθευση συνεδρίας + + Το αντίγραφο ασφαλείας κλειδιών δεν έχει ολοκληρωθεί, παρακαλώ περιμένετε… + Θα χάσετε τα κρυπτογραφημένα μηνύματά σας εάν αποσυνδεθείτε τώρα + Δημιουργία αντίγραφου κλειδιού ασφαλείας σε εξέλιξη. Εάν αποσυνδεθείτε τώρα, θα χάσετε την πρόσβαση στα κρυπτογραφημένα μηνύματά σας. + Δεν θέλω τα κρυπτογραφημένα μηνύματά μου + Δημιουργία αντιγράφων ασφαλείας κλειδιών… + Χρησιμοποιήστε το αντιγραφο κλειδιού ασφαλείας + Είστε σίγουρος; + Δημιουργία αντίγραφου ασφαλείας + Θα χάσετε την πρόσβαση στα κρυπτογραφημένα μηνύματά σας, εκτός εάν δημιουργήσετε αντίγραφα ασφαλείας των κλειδιών σας πριν αποσυνδεθείτε. + Αφαίρεση + Κατεβάστε + Διαμοιρασμός + Καθαρισμός + Ανάκληση + Αποσύνδεση + Παύση + Прямі повідомлення diff --git a/vector/src/main/res/values-es/strings.xml b/vector/src/main/res/values-es/strings.xml index 4687fffa77..c5ca0d5f1a 100644 --- a/vector/src/main/res/values-es/strings.xml +++ b/vector/src/main/res/values-es/strings.xml @@ -1811,7 +1811,7 @@ La visibilidad de mensajes en Matrix es similar a la del correo electrónico. Qu Empieza Selecciona un servidor - Como el correo electrónico, las cuantas tienen un hogar, aunque se puede hablar con cualquiera + Como el correo electrónico, las cuentas tienen un hogar, aunque se puede hablar con cualquiera Alojamiento de pago para organizaciones Saber más Otro @@ -1930,7 +1930,6 @@ La visibilidad de mensajes en Matrix es similar a la del correo electrónico. Qu Desbanear usuario y permitir entrar a la sala nuevamente. nombre_session: - Error al descifrar el mensaje en la línea de tiempo Adicionar pestaña dedicada para notificaciones no leidas en la pantalla principal. Descripcion muy corta diff --git a/vector/src/main/res/values-et/strings.xml b/vector/src/main/res/values-et/strings.xml index 5fd84ffb47..24acbc8dbc 100644 --- a/vector/src/main/res/values-et/strings.xml +++ b/vector/src/main/res/values-et/strings.xml @@ -1,20 +1,17 @@ - + et EE Latn - Hele teema Tume teema Must teema Status.im teema - Käivitan teenuse Sünkroniseerin… Kuulan, kas leidub sündmusi Lärmakad teavitused Vaiksed teavitused - Sõnumid Jututuba Seadistused @@ -26,10 +23,8 @@ Võtmete varundus Kasuta võtmete varundust Verifitseeri sessioon - Võtmete varundus pole veel valmis, oota natuke… Krüptitud sõnum - Kui sa logid nüüd välja, siis sa kaotad ligipääsu kõikidele krüptitud sõnumitele Parasjagu varundan võtmeid. Kui sa logid nüüd välja, siis sa kaotad ligipääsu kõikidele oma krüptitud sõnumitele. Turvaline võtmete varundus peaks olema aktiivne kõikides sinu sessioonides, sest see hoiab ära krüptitud sõnumitele ligipääsu kadumise. @@ -39,11 +34,8 @@ Kas sa oled kindel\? Varunda Kui sa enne väljalogimist ei varunda oma võtmeid, siis sa kaotad ligipääsu oma krüptitud sõnumitele. - Kolmandate osapoolte litsentsid - Laeme… - Sobib Tühista Salvesta @@ -92,7 +84,6 @@ Eira Vaata üle Keeldu - Välju Tegevused Logi välja @@ -108,27 +99,22 @@ Sulge Kopeeritud lõikelauale Lülita välja - Kinnitus Hoiatus Viga - Kodu Lemmikud Inimesed Jututoad Kogukonnad - Filtreeri jututubasid Filtreeri lemmikuid Filtreeri inimesi Filtreeri jututubasid Filtreeri kogukondasid - Kutsed Vähetähtis Süsteemi hoiatused - Vestlused Kohalik aadressiraamat Kasutajate kataloog @@ -137,20 +123,17 @@ Sa pole Element\'ile andnud ligipääsu kohalikele kontaktidele Tulemusi ei ole Isikutuvastusserver ei ole seadistatud. - Jututoad Jututubade loend Jututube ei leidu Avalikke jututube ei leidu - 1 kasutaja + %d kasutaja %d kasutajat - Kutse Kogukonnad Gruppe ei leidu - Saada logikirjed Saada kokkujooksmise logikirjed Saada ekraanipilt @@ -162,14 +145,11 @@ Ma märkan, et pahameelega raputad oma telefoni. Kas sa tahaksid saata veateate\? See rakendus jooksis viimati kokku. Kas sa tahaksid saata selle kohta veateate\? Veateate saatmiseks raputa väga kõvasti - Veateate saatmine õnnestus Veateate saatmine ei õnnestunud (%s) Saatmise kulg (%s%%) - Saada Loetud - Liitu jututoaga Kasutajanimi Loo konto @@ -178,13 +158,10 @@ Koduserveri aadress Isikutuvastusserveri aadress Otsi - Alusta uut vestlust Algata häälkõne Algata videokõne - Edasta häält - Kas oled kindel, et soovid alustada vestlust kasutajaga %s\? Kas oled kindel, et soovid algatada häälkõnet\? Kas oled kindel, et soovid algatada videokõnet\? @@ -194,20 +171,16 @@ \nAlternatiivina võid sa kasutada avalikku TURN serverit %2$s, kuid see ei pruugi olla sama töökindel ning sa jagad oma IP-aadressi selle serveriga. TURN-serveri kasutamist võid sa määrata ka seadistustes. Proovi kasutada serverit %s Ära küsi minult uuesti - Saada faile Saada kleepse Tee foto või video Tee foto Tee video - Sul ei ole hetkel ühtegi kleepsupakki kasutusel. \n \nKas lisame nüüd mõne\? - kasuta rakendust… Vabandust, aga selle tegevuse jaoks ei leidu ühtegi välist rakendust. - Logi sisse Loo konto Saada @@ -260,7 +233,6 @@ \n \nSa oled välja logitud kõikidest oma seni kasutusel olnud sessioonidest ega saa enam teateid ega sõnumeid. Nende uuesti lugemiseks palun logi igas seadmes tagasi Matrix\'i võrku. Palun loe läbi ja nõustu koduserveri kasutusjuhendiga: - Serveri aadressi alguses peab olema kas https:// või http:// Sisselogimine ei õnnestu: võrguühenduse viga Ei õnnestu sisse logida @@ -272,7 +244,6 @@ See ei ole toimiv Matrix\'i serveri aadress Sellel aadressil ei leidu koduserverit, palun kontrolli aadressi õigsust Sinu seade kasutab aegunud versiooni TLS-protokollist ning seal leiduvate turvanõrkuste tõttu ja sinu turvalisuse tagamiseks ühendust serveriga ei algatata - Vigane kasutajanimi või salasõna Vigane JSON Ei sisaldanud korrektset JSON\'it @@ -281,16 +252,12 @@ Esita Peata Loobu - - Kopeeri Õnnestus - Teavitused Kõne ebaõnnestus Reaalajas ühenduse loomine ei õnnestunud. \nPalu oma koduserveri %@ haldajat, et ta seadistaks kõnede kindlamaks toimimiseks TURN serveri. - Vali heliseade Telefon Kõlar @@ -301,51 +268,38 @@ Tagakaamera Lülita kõrglahutusega video välja Lülita kõrglahutusega video sisse - Logi sisse ühekordse sisselogimise abil SSL-ühenduse viga. Sisestatud tunnusluba ei ole õige E-posti teel saadetud linki pole veel klõpsitud - Küsi oma muudest sessioonidest krüptimisvõtmed uuesti. - Võtmete jagamise päring on saadetud. - Päring on saadetud Palun käivita Element mõnes muus seadmes, mis suudab neid sõnumeid dekrüptoda ja seega saata krüptovõtmeid siia sessiooni. - Lugemisteatiste loend - Gruppide loend - - 1 liikmelisuse muutus + %d liikmelisuse muutus %d liikmelisuse muutust - Saada pilt Algses suuruses Suurena Keskmisena Väikesena - Kas katkestame allalaadimise\? Kas katkestame üleslaadimise\? %d sekundit %1$d min %2$d sek - Eile Täna - Jututoa nimi Jututoa teema - Kõned Kasuta saabuvate kõnede jaoks Element\'i vaikimisi helinat Kasuta kõnehõlbustusserverit Saabuva kõne helin Vali helin kõnede jaoks: - Kõne Vastuvõetud kõne Kõne ühendus on loomisel… @@ -358,14 +312,11 @@ Videokõne on pooleli… Käsilolev kõne (%s) Pöördu tagasi kõne juurde - Teine osapool ei võtnud kõnet vastu. Kaamerat ei õnnestu kasutusele võtta kõnele vastati muust seadmest - Tee foto või video Video salvestamine ei õnnestu - Lisateave õiguste kohta Element vajab õigusi sinu foto- ja videokataloogide lugemiseks ning manuste salvestamiseks. \n @@ -382,21 +333,17 @@ \n \nKõnede tegemiseks palun anna järgmisel lehel vajalikud õigused. Vabandust. Kuna vastavaid õigusi ei olnud, siis toimingut ei saanud teha - Salvestatud Kas salvestame allalaetud failide kausta\? JAH EI Jätka - Eemalda Liitu Eelvaade Hülga - Sünkroniseerin… Mine esimese lugemata sõnumi juurde. - Sa oled kutsutud siia jututuppa %s poolt Kutse saadeti %s e-posti aadressile, aga see ei ole seotud antud kasutajakontoga. \nSa kas peaksid logima sisse teise kontoga või lisama selle e-posti aadressi oma kasutajakontole. @@ -409,23 +356,20 @@ Võrreldes selle sertifikaadiga, mida sinu nutiseade seni usaldas, on praegune sertifikaat muutunud. See on VÄGA EBATAVALINE. Me soovitame, et ÄRA NÕUSTU selle uue sertifikaadiga. Senise usaldusväärse sertifikaadi asemel kasutab server nüüd mitteusaldusväärset sertifikaati. See võib tähendada et haldaja on seda serveris muutnud. Et võrrelda viimase kehtiva sertifikaadi sõrmejälge, palun võta haldajaga ühendust. NÕUSTU sertifikaadiga vaid siis, kui serveri haldaja antud sõrmejälg klapib sellega, mida sa hetkel siin näed. - Jututoa üksikasjad Inimesed Failid Seadistused - 1 valitud + %d valitud %d valitud Vigane kasutajatunnus. Peaksid kasutama kas e-posti aadressi või õiget Matrix\'i kasutajatunnust vormingus \'@toreinimene:domeen.ee\' KUTSUTUD LIITUNUD - Sellest sisust teatamise põhjus Katkesta üleslaadimine Katkesta allalaadimine - Otsi Filtreeri jututoa liikmeid Tulemusi pole @@ -433,7 +377,6 @@ SÕNUMID INIMESED FAILID - LIITU LEMMIKUD JUTUTOAD @@ -444,7 +387,6 @@ Liitu jututoaga Liitu jututoaga Sisesta jututoa tunnus, nimi või alias - Sirvi kataloogi Otsevestlus VAATA @@ -457,23 +399,17 @@ Kirjelda oma ettepanekut siin Tänud, ettepaneku saatmine õnnestus Ettepaneku saatmine ei õnnestunud (%s) - Sõelu vestlustest… Kas sa ei leia seda, mida otsisid\? Loo uus jututuba Saada uus otsesõnum Vaata jututubade loendit - Sõelu kasutajanime või kasutajatunnuse alusel… - Näita muudatuste ajalugu - Algata uus otsevestlus Loo uus jututuba Kirjeldus on liiga lühike - Teen andmete esmast sünkroonimist… - Näita kõiki minu sessioone Lisaseadistused Tõuketeavituste kasutuselevõtuks vaata üle seadistused @@ -490,34 +426,26 @@ PIN-koodi kasutuselt eemaldamiseks korda PIN-koodi Kasuta krüptimist vaid verifitseeritud sessioonides Ära iialgi saada sellest sessioonist krüptitud sõnumeid verifitseerimata sessioonidesse selles jututoas. - Sellel jututoal puuduvad kohalikud aadressid Uus aadress (näiteks #seltskond:matrix.org) - Sellele jututoale ei ole jagatud ühtegi kogukonna rinnasilti Uus kogukonna tunnus (näiteks +meieseltsing:matrix.org) Vigane kogukonna tunnus \'%s\' ei ole korrektne kogukonna tunnus - - Vigane aliase vorming \'%s\' ei ole lubatud aliase vorming Sel jututoal ei saa olema põhiaadressi. Põhiaadressi hoiatus - Määra põhiaadressiks Eemalda põhiaadressiks määramine Kopeeri jututoa tunnus Kopeeri jututoa aadress - Krüptimine on selles jututoas kasutusel. Krüptimine ei ole selles jututoas kasutusel. Võta krüptimine kasutusele \n(hoiatus: seda ei saa hiljem enam välja lülitada!) - Kaust Teema - Fontide suurus Pisike Väike @@ -526,18 +454,15 @@ Suurem Veel suurem Hiidsuur - Sul on vaja õigusi, et selles jututoas hallata vidinaid Vidina loomine ei õnnestunud Algata rühmakõnesid jitsi vahendusel Kas oled kindel, et soovid siit jututoast selle vidina kustutada\? - Kasutusel on üks vidin + Kasutusel on %d vidin Kasutusel on %d vidinat Kasutusel vidinad - - Vidin Lae vidin Selle vidina lisaja: @@ -548,23 +473,18 @@ Lae vidin uuesti Ava veebibrauseris Eemalda minu ligipääsuõigused - Sinu kuvatav nimi Sinu avatari aadress Sinu kasutajatunnus Sinu teema Vidina tunnus Jututoa tunnus - - %2$s seadis sulle ligipääsukeelu %1$s jututuppa Põhjus: %1$s Liitu uuesti Unusta jututuba ära - (Lisaseadistused) Ekspordi võtmed käsitsi - Krüpti oma varukoopia paroolifraasiga. Me salvestame krüptitud varukoopia sinu krüptovõtmetest meie serveris. Tagamaks, et keegi ei saa seda kasutada, krüpti varukoopia paroolifraasiga. \n @@ -587,60 +507,47 @@ Palun sisesta taastevõti Verifitseeri see sessioon tehes kindlaks et järgnev emoji kuvatakse partneri ekraanil Verifitseeri see sessioon tehes kindlaks, et järgnevad numbrid kuvatakse partneri ekraanil - Sa võtsid vastu saabuva verifitseerimispalve. Ootan teise osapoole kinnitust… - Verifitseeritud! Sa oled edukalt verifitseerinud selle sessiooni. Turvalised sõnumid selle kasutajaga on läbivalt krüptitud ning kolmandad osapooled ei saa neid lugeda. Selge lugu - Mitte midagi huvitavat ei paista\? Kõik kliendid ei toeta veel interaktiivset verifitseerimist. Selle asemel kasuta vana kooli verifitseerimist. Kasuta vana kooli verifitseerimist. - Krüptovõtmete verifitseerimine Päring on tühistatud Teine osapool tühistas verifitseerimise. \n%s Verifitseerimine on tühistatud. \nPõhjus %s - Vastastikune sessioonide verifitseerimine Verifitseerimispäring %s soovib verifitseerida sinu sessiooni - Kasutaja tühistas verifitseerimise Verifitseerimisprotsess aegus Sinu jututoad kuvatakse siin - Reageerimised Nõus Meeldib Lisa reaktsioon Reageerimised - Sõnum on kustutatud Näita kustutatud sõnumeid Näita kustutatud sõnumite asemel kohatäidet Kasutaja kustutas sündmuse Jututoa haldur on sündmust modereerinud Viimati muudetud %1$s poolt, muutmise aeg %2$s - - Vigaselt vormindatud sündmus ja kuvamine pole seetõttu võimalik Võrguühendus puudub. Palun kontrolli oma internetiühendust. Muuda Vaheta võrku Palun oota… Kõik kogukonnad - Sellel jututoal puudub eelvaade Element ei toeta veel kõigile nähtavate jututubade eelvaadet - Jututoad Isiklikud sõnumid - Uus jututuba LOO Jututoa nimi @@ -648,14 +555,11 @@ Kõik võivad selle jututoaga liituda Jututubade loend Avalda see jututuba jututubade loendis - Saada manus - Selles jututoas pole meediafaile FAILID Lisaja %1$s, lisamise aeg %2$s Selles jututoas pole faile - Tegemist on spämmiga See on ebasobilik Kohandatud põhjus… @@ -665,15 +569,12 @@ Sinu salasõna on muudetud. Sa oled kõikidest sessioonidest välja logitud ning enam ei saa tõuketeavitusi. Nende taaskuvamiseks logi sisse kõikides oma seadmetes. Tagasi sisselogimise lehele - Hoiatus "Turvalisuse mõttes on oluline, et teed seda nii, et kas olete üheskoos või kasutate suhtluskanalit, mida mõlemad usaldate." - Võrdle unikaalset emoji\'de kombinatsiooni ja vaata, et nad oleksid samas järjekorras. Võrdle koodi sellega, mida kuvatakse teise kasutaja ekraanil. Sõnumid selle kasutajaga on läbivalt krüptitud ning kolmandad osapooled ei saa neid lugeda. Sinu uus sessioon on nüüd verifitseeritud. Selles sessioonis saad lugeda oma krüptitud sõnumeid ja teiste kasutajate jaoks on ta usaldusväärne. - Risttunnustamine Risttunnustamine on kasutusel \nPrivaatvõtmed asuvad seadmes. @@ -683,20 +584,17 @@ Risttunnustamine on kasutusel \nVõtmed ei ole usaldusväärsed Risttunnustamine ei ole kasutusel - Sinu serveri haldur on lülitanud läbiva krüptimise omavahelistes jututubades ja otsesõnumites välja. Aktiivsed sessioonid Näita kõiki sessioone Halda sessioone Logi sellest sessioonist välja - Teave krüptograafia kohta puudub - Kuna sina oled selle sessiooni verifitseerinud, siis see sessioon on krüptitud sõnumite saatmiseks usaldusväärne: See isikutuvastusserver kasutab vana API\'t. Element toetab aga vaid API versiooni 2. See tegevus ei ole võimalik. Koduserveri versioon on liiga vana. - 1 jututuba + %d jututuba %d jututuba @@ -704,7 +602,6 @@ Otsingusõnaga %2$s leidsin %1$s jututuba Otsin kataaloogist… - Kõik sõnumid (lärmakas) Kõik sõnumid Ainult mainimised @@ -713,8 +610,7 @@ Vähetähtis Lahku vestlusest Unusta - Lisa otsetee avalehele - + Lisa avalehele Sõnumid Seadistused Versioon @@ -723,7 +619,6 @@ Kolmandate osapoolte litsentsid Autoriõigused Privaatsuspoliitika - Profiilipilt Kuvatav nimi E-posti aadress @@ -735,11 +630,8 @@ Kinnitage oma salasõna Seda tegevust ei saa teha Element\'i nutirakendusest Autentimine on nõutav - - Teavituste lisaseadistused Teavituse olulisus sündmuse alusel - Teavituste privaatsus Süsteemiseadistused. Kasutajakonto seadistused. @@ -747,13 +639,11 @@ Sinu kontol ei ole teavitusi kasutusel. \nSeda saad muuta konto seadistuses. Võta kasutusele - Sessiooni seadistused. Selles sessioonis on teavitused kasutusel. Selles sessioonis ei ole teavitusi kasutusel. \nSeda saad muuta Element\'i seadistuses. Võta kasutusele - Kohandatud seadistused. Näita ajatempleid 12-tunnises vormingus Näita lugemisteatiseid @@ -766,32 +656,26 @@ Kood Sinu telefoninumbri kontrollimisel tekkis viga. Lisateave: %s - Meedia Vaikimisi pakkimine Vali Vaikimisi meediaallikas Vali Tee katiku klõpsu - Kogukonna rinnasilt Sa ei ole hetkel ainsamagi kogukonna liige. - 3 päeva 1 nädal 1 kuu Igavesti - Jututoa foto Jututoa nimi Jututoa teema Jututoa silt Sildistatud kui: - Lemmik Vähetähtis Ei midagi - Ligipääs ja nähtavus Näita seda jututuba jututubade kataloogis Teavitused @@ -799,23 +683,19 @@ Jututoa ajaloo loetavus Kes võivad lugeda ajalugu\? Kes pääsevad ligi siia jututuppa\? - Kõik kasutajad Ainult liikmetele (alates selle seadistuse kasutuselevõtmisest) Ainult liikmetele (alates nende kutsumise ajast) Ainult liikmetele (alates liitumisest) - Sellele jututoale viitamiseks peab tal olema aadress. Vaid kutsutud kasutajad Kõik, kes teavad jututoa viidet, välja arvatud külalised Kõik, kes teavad jututoa viidet, kaasa arvatud külalised - Suhtluskeelu saanud kasutajad %d suhtluskeelu saanud kasutaja %d suhtluskeelu saanud kasutajat - Lisaseadistused Selle jututoa sisemine tunnus Aadressid @@ -825,9 +705,7 @@ Läbiv krüptimine on kasutusel Läbiva krüptimise kasutuselevõtuks pead korraks välja logima. %s üritas laadida teatud hetke selle jututoa ajajoonelt, kuid ei suutnud seda leida. - Läbiva krüptimise teave - Sündmuse teave Kasutajatunnus Curve25519 identiteedi võti @@ -835,7 +713,6 @@ Algoritm Sessiooni tunnus Dekrüptimise viga - Saatja sessiooni teave Avalik nimi Avalik nimi (nähtav neile, kellega sa suhtled) @@ -845,18 +722,15 @@ Sessiooni võti Verifitseerimine Ed25519 sõrmejälg - Ekspordi jututubade läbiva krüptimise võtmed Ekspordi jututoa võtmed Ekspordi võtmed kohalikku faili Ekspordi Sina määrasid, et jututuppa pääseb vaid kutsega. Lugemata sõnumid - Krüptimise abil hoiad vestlused privaatsena Laienda ja kohanda oma kasutuskogemust Alusta - Vali server Nii nagu e-posti puhul, siis kasutajakonto asub ühes serveris, kuid saab suhelda kasutajatega üle ilma Liitu tasuta miljonite teistega suurimas tasuta Matrix\'i serveris @@ -864,7 +738,6 @@ Lisateave Muud serverid Kohandatud seadistused - Jätka Ühenda koduserveriga %1$s Ühenda teenusepakkujaga Element Matrix Services @@ -873,14 +746,12 @@ Registreeru Logi sisse Jätka kasutades SSO\'d ehk ühekordset autentimist - Element Matrix Services teenuse aadress Aadress Tasuline Matrix\'i majutusteenus organisatsioonidele Sisesta Modular Element teenuse või muu serveri aadress, mida sa soovid kasutada Sisesta serveri või Element\'i aadress, mida sa soovid kasutada Sisesta serveri aadress, mida sa soovid kasutada - Lehe laadimisel tekkis viga %1$s (%2$d) See rakendus ei saa sisse logida antud koduserverisse. See koduserver toetab järgmisi sisselogimise tüüp(e): %1$s. \n @@ -889,21 +760,16 @@ See rakendus ei saa antud koduserveris uut kasutajakontot. \n \nKas sa soovid registreerida kasutades veebibrauseri põhist klienti\? - See e-posti aadress ei ole seotud ühegi kasutajakontoga. - Loo uus salasõna koduserveris %1$s Kontrollimaks, et just sina sina ise sisestasid uue salasõna, siis saadame sulle kinnituskirja. Järgmine E-posti aadress Uus salasõna - Hoiatus! Salasõna muutmine tühistab kõik läbiva krüptimise võtmed sinu kõikides sessioonides ning seega muutub kogu sinu vestluste ajalugu loetamatuks. Palun kindlasti kas sea üles võtmete varundamine või ekspordi mõnest muust sessioonist jututubade võtmed enne senise salasõna tühistamist. Jätka - See e-posti aadress ei ole seotud ühegi kasutajakontoga - Vaata oma saabuvate e-kirjade postkasti Verifitseerimise jaoks saatsime e-kirja aadressile %1$s. Logi sisse @@ -913,7 +779,6 @@ \n \nEemalda nad, kui oled lõpetanud selle seadme kasutamise või soovid sisse logida muu kasutajakontoga. Eemalda kõik andmed - Eemalda andmed Kas sa oled kindel, et soovid kustutada kõik andmed, mis on hetkel siin seadmes salvestatud\? \nOma sõnumite ja kontoandmete nägemiseks logi uuesti sisse. @@ -928,47 +793,42 @@ Uus vestlus Lisa osaleja - 1 aktiivne osaleja + %d aktiivne osaleja %d aktiivset osalejat - 1 osaleja + %d osaleja %d osalejat 1 osaleja - - 1 sekund + %d sekund %d sekundit - 1 minut + %d minut %d minutit - 1 tund + %d tund %d tundi - 1 päev + %d päev %d päeva - Lahku jututoast Kas oled kindel, et soovid lahkuda jututoast\? Kas sa oled kindel, et soovid eemaldada %s sellest vestlusest\? Loo - Võrgus Võrgust väljas Jõude Nüüd %1$s Oli %1$s %2$s tagasi - HALDUSTARVIKUD KÕNED SESSIOONID - Kutsu Tühista kutse Lahku sellest jututoast @@ -986,14 +846,11 @@ \n \nHoiatus: rakenduse eemaldamisel ka see fail ilmselt kustutatakse. Taastevõti on salvestatud. - Varukoopia on juba olemas sinu koduserveris Asenda Peata - Palun tee koopia Ootel - Sisesta uue isikutuvastusserveri aadress Ei saanud ühendust isikutuvastusserveriga Palun sisesta isikutuvastusserveri aadress @@ -1001,67 +858,48 @@ Sinu valitud isikutuvastusserveril pole kasutustingimusi. Jätka vaid siis, kui sa usaldad serveri omanikku ja haldajat Saatsime tekstisõnumi numbrile %s. Palun sisesta seal kuvatud kontrollkood. Kontrollkood ei ole õige. - Muud jututoad - Saadab selle sõnumi vikerkaarevärvilisena Saadab antud emote vikerkaarevärvides - Ajajoon - Sõnumite kirjutamine - Võta läbiv krüptimine kasutusele "Kui krüptimine on juba kasutusele võetud, siis ei saa seda enam eemaldada." - Kas võtame krüptimise kasutusele\? Kui kord juba kasutusele võetud, siis krüptimist enam hiljem ära lõpetada ei saa. Krüptitud sõnumeid ei saa lugeda ei vaheapealses veebiliikluses ega serveris ja vaid jututoa liikmed saavad neid lugeda. Krüptimise kasutusele võtmine võib takistada nii robotite kui sõnumisildade tööd. Võta jututoas krüptimine kasutusele - Lisaturvalisus mõttes verifitseeri %s võrreldes selleks üheks korraks loodud koodi mõlemas seadmes. Verifitseeri see sessioon selleks, et teda võiks lugeda usaldusväärseks ja saaks ligipääsu krüptitud sõnumitele. Kui sina pole sellesse sessiooni sisse loginud, siis sinu kasutajakonto võib olla sattunud võõraste kontrolli alla: - %d aktiivne sessioon %d aktiivset sessiooni - Verifitseeri see sisselogimissessioon Teised kasutajad ei pruugi seda usaldada Vormista turvaseadistused lõpuni - Kasuta olemasolevat sessiooni selle sessiooni verifitseerimiseks, andes sellega ligipääsu krüptitud sõnumitele. - - Verifitseeri Verifitseeritud Hoiatus - Sessionide loendi laadimine ei õnnestunud Sessioonid Usaldusväärne Ei ole usaldusväärne - Kuna %1$s (%2$s) on selle sessiooni verifitseerinud, siis see sessioon on krüptitud sõnumite saatmiseks usaldusväärne: %1$s (%2$s) logis sisse uues sessioonis: Sa ei saa seda muudatust hiljem tagasi pöörata, sest annad teisele kasutajale samad õigused, mis sinul on. \nKas sa oled ikka kindel\? - Kas vähendad enda õigusi\? Kuna sa vähendad enda õigusi, siis sul ei pruugi hiljem olla võimalik seda muutust tagasi pöörata. Kui sa juhtumisi oled viimane haldusõigustega kasutaja jututoas, siis hiljem on võimatu samu õigusi tagasi saada. Vähenda enda õigusi - - Eira kasutajat Selle kasutaja eiramine eemaldab teie jagatud jututoast tema sõnumid. \n \nÜldistest seadistustest saad seda alati muuta. Eira selle kasutaja sõnumeid - Lõpeta selle kasutaja eiramine Selle kasutaja eiramise lõpetamine teeb tema sõnumid uuesti nähtavaks. Lõpeta eiramine - Tühista kutse Kas oled kindel et sa soovid tühistada kutse sellele kasutajale\? Müksa kasutaja välja @@ -1074,23 +912,18 @@ Taasta selle kasutaja ligipääs Sellele kasutajale suhtluskeelu seadmine tõstab ta jututoast välja ning ei võimalda uuesti liitumist. Suhtluskeelu eemaldamine võimaldab jututoaga uuesti liituda. - Põhjus - Kas sa oled kindel, et soovid kutsuda %s vestlusele\? "%1$s, " %1$s ja %2$s %1$s %2$s - Kutsu tunnuse alusel KONTAKTID SIIN SEADMES (%d) KASUTAJATE LOEND (%s) Vaid Matrix\'i kasutajad - Kutsu kasutajat tunnuse alusel Palun sisesta üks või enam e-posti aadressi või Matrix\'i kasutajatunnust E-posti aadress või Matrix\'i kasutajatunnus - Otsi %s kirjutab… %1$s & %2$s kirjutavad… @@ -1109,10 +942,9 @@ Faili ei leidunud Sul ei ole õigusi siia jututuppa kirjutamiseks - 1 uus sõnum + %d uus sõnum %d uut sõnumit - Usalda Ära usalda Logi välja @@ -1127,23 +959,19 @@ Esmasel vaatlusel vigu ei leidu. Kui sa jätkuvalt ei saa teavitusi, siis palun saada edaspidise uurimise jaoks meile veateade. Üks või enam testi andis tulemuseks vea, palun proovi pakutud lahendusi. Üks või enam testi andis tulemuseks vea, palun saada edaspidise uurimise jaoks meile veateade. - Süsteemi seadistustes on teavitused kasutusel. Süsteemi seadistustes ei ole teavitusi kasutusel. \nPalun vaata need üle. Ava seadistused - Pane tähele, et mõned teavitused on seadistatud vaiksetena (teavitus kuvatakse ilma helilise märguandeta). Mõned teavitused on sinu kohandatud seadistustes kinni keeratud. Kohandatud reeglite laadimine ei õnnestunud. Palun proovi uuesti. Palun kontrolli seadistusi - Goolge Play teenuste kontrollimine Google Play Services APK on kättesaadav ja uuendatud. Element kasutab Google Play teenuseid tõuketeavituste edastamiseks, kui see ei tundu olema korrektselt seadistatud: \n%1$s Paranda Google Play teenused - Firebase tunnusluba FCM tunnusloa laadimine õnnestus: \n%1$s @@ -1156,42 +984,35 @@ [%1$s] \nSee viga on väljaspool Element\'i kontrolli. Telefoni ei ole seadistatud Google\'i kontot. Palun ava seadistustes kontohaldur ning lisa üks Google\'i konto. Lisa Google\'i konto - Tunnusloa registreerimine FCM tunnusloa registreerimine koduserveris õnnestus. FCM tunnusloa registreerimine koduserveris ei õnnestunud: \n%1$s - Teavituste teenus Teavituste teenus töötab. Teavituste teenus ei tööta. \nProovi, kas Element\'i uuesti käivitamine aitab. Käivita teenus - Teavituste teenuse automaatne taaskäivitamine Teenus suleti ja käivitati automaatselt uuesti. Teenuse uuesti käivitamine ei õnnestunud - Käivita teenus seadme käivitamisel Teenus käivitatakse nutiseadme käivitamisel. Seda teenust ei käivitatata nutiseadme käivitamisel. Sa ei saa teavitusi enne, kui Element on vähemalt korra avatud. Käivita teenus nutiseadme käivitamisel - Kontrolli taustapiiranguid - 1 jututuba + %d jututuba %d jututuba - - %1$s: 1 sõnum + %1$s: %d sõnum %1$s: %2$d sõnumit %d teavitus %d teavitust - %1$s -> %2$s Uus sündmus Jututuba @@ -1201,9 +1022,7 @@ ** Saatmine ei õnenstunud - palun ava jututoa vaade %1$s: %2$s %1$s: %2$s %3$s - Otsi ajaloost - Vabandust, aga rühmakõned Jitsi vahendusel ei ole vanades seadmetes toetatud (Androidi versioon alla 5.0) See vidin soovib kasutada järgmisi andmeid: Luba @@ -1211,7 +1030,6 @@ Luba kaamera kasutamine Luba mikrofoni kasutamine Luba DRM\'iga kaitstud meedia avamine - Vidina loomine ei õnnestunud. Päringu saatmine ei õnnestunud. Õiguste tase peab olema positiivne täisarv. @@ -1232,7 +1050,6 @@ Element saab kontrollida teie aadressiraamatut, et leida teisi Matrixi kasutajaid nende e-posti aadresside ja telefoninumbrite põhjal. \n \nKas sa oled nõus oma aadressiraamatut sel eesmärgil jagama\? - Kas sa soovid peita selle kasutaja kõik sõnumid\? \n \nPane tähele, et antud toiming taaskäivitab rakenduse ja see võib võtta veidi aega. @@ -1242,12 +1059,10 @@ \nTöö, mida rakendus proovib teha, on agressiivselt piiratud, kui see on taustal ja see võib mõjutada teavituste kättesaamist. \n%1$s Keela piirangud - Aku optimeerimine Aku optimeerimine ei mõjuta Elementi. Kui kasutaja jätab seadme mõneks ajaks vooluvõrgust välja ja ühele kohale paigale ning ekraan on välja lülitatud, lülitub seade Doze režiimi. See takistab rakendustel juurdepääsu võrgule ja lükkab edasi nende töö, sünkroonimise ja tavalised teated. Eira optimeerimist - Tavaline Vähendatud privaatsus See rakendus vajab taustal toimimiseks õigusi @@ -1257,7 +1072,6 @@ • teavitusega märgitud sõnumi sisu laetakse turvaliselt ja otse sinu Matrix\'i koduserverist • teavitused sisaldavad nii metainfot kui sõnumi sisu • teavitused ei näita sõnumi sisu - Teavituste heli Võta teavitused sellel kontol kasutusele Võta teavitused selles sessioonis kasutusele @@ -1266,8 +1080,6 @@ Seadista kõneteavitused Seadista vaiksed teavitused Vali LED-tule värv, värin ning heli… - - Sõnumid, mis sisaldavad minu kuvatavat nime Sõnumid, mis sisaldavad minu kasutajatunnust Kahepoolsete vestluste sõnumid @@ -1275,7 +1087,6 @@ Kui mind kutsutakse jututuppa Saabuvad kõned Robotite saadetud sõnumid - Andmete sünkroniseerimine taustal Andmete taustal sünkroniseerimise režiim Optimeeritud akukestust silmas pidades @@ -1287,8 +1098,6 @@ Taustasünkroonimine puudub Kui rakendus töötab taustal, siis sa ei saa saabunud sõnumite kohta teavitusi. Seadistuste uuendamine ei õnnestunud. - - Käivita teenus seadme käivitamisel Kasuta taustasünkroonimist Sünkroniseerimispäring aegus @@ -1296,7 +1105,6 @@ %s \nSünkroniseerimine võib jääda vahele, kui aku täituvus on madal või seade energiasäästurežiimil. Viivitus sünkroonimiste vahel - Versioon olm-teegi versioon Kasutustingimused @@ -1306,7 +1114,6 @@ Säilita meedia Tühjenda puhver Tühjenda meedia puhver - Kasutaja seadistused Teavitused Eiratud kasutajad @@ -1341,7 +1148,6 @@ Enne meedia saatmist näita eelvaadet Saada sõnum reavahetusklahvi vajutamisel Tarkvaralise klaviatuuri reavahetusklahvi vajutus saadab sõnumi ning ei tee reavahetust - Turvaline varundus Halda Võta kasutusele turvaline varundus @@ -1350,7 +1156,6 @@ Tagamaks, et sa ei kaota ligipääsu krüptitud sõnumitele ja andmetele, varunda krüptimisvõtmed oma serveris. Loo uus turvavõti või seadista olemaoleva varukoopia jaoks uus turvafraas. Sellega asendad olemasoleva turvavõtme või turvafraasi. - Deaktiveeri kasutajakonto Eemalda minu konto kasutusest Leia kasutajaid @@ -1359,21 +1164,17 @@ Element võib taustal töötamise ajal hallata sinu teavitusi turvaliselt ja privaatselt. Aga see võib mõjutada akukasutust. Anna õigused Tee muu valik - Taustaühendus Selleks et teavitused toimiksid korralikult peab Element kasutama vähese kõrvalmõjuga taustaühendust. \nKui järgmisel lehel sinult küsitakse kas lubada, et Element töötab kogu aeg taustal, siis palun nõustu. Anna õigused - Analüütika Saada arendajatele analüütikat Võimaldamaks meil rakendust parandada kogub Element anonüümset teavet rakenduse kasutuse kohta. Selleks, et saaksime Element\'i paremaks teha, palun luba analüütika. Jah, ma soovin aidata! - Võrguliikluse mahu säästmise režiim Võrguliikluse andmemahu säästmiseks kasutatakse filtrit, mille puhul kasutajate olekuid ei uuendata ja sõnumite kirjutamise teavitusi ei laeta. - Sessiooniteave Seadme tunnus Avalik nimi @@ -1385,20 +1186,16 @@ Autentimine Salasõna: Saada - Sisselogitud kui Koduserver Isikutuvastusserver Luba lõimingud Lõiminguhaldur - Lõimingud ei ole kasutusel Selle tegevuse kasutuselevõetuks lülita seadetes sisse „Luba lõiminguid“ valik. - Kasutajaliides Keel Vali keel - Verifikatsioon on ootel Palun vaata oma e-kirju ning klõpsi meie saadetud kirjas leiduvat linki. Kui see on tehtud, siis vajuta Jätka-nuppu. E-posti aadressi õigsust pole veel õnnestunud kontrollida. Palun vaata oma e-kirju ning klõpsi meie saadetud kirjas leiduvat linki. Kui see on tehtud, siis vajuta Jätka-nuppu. @@ -1406,7 +1203,6 @@ Seda e-posti aadressi ei leidunud. See telefoninumber on juba kasutusel. Sinu e-posti aadressi kontrollimisel tekkis viga. - Salasõna Muuda salasõna Praegune salasõna @@ -1420,13 +1216,9 @@ \n \nPane tähele, et antud toiming taaskäivitab rakenduse ja see võib võtta veidi aega. Salasõnad ei klapi omavahel - Kas sa oled kindel et soovid eemaldada antud teavituse liigi\? - Kas sa oled kindel, et soovid eemaldada %1$s %2$s\? - Vali riik - Riik Palun vali riik Eksporditavate võtmete krüptimiseks palun sisesta paroolifraas. Võtmete importimisel pead kasutama sama paroolifraasi. @@ -1434,62 +1226,51 @@ \n \nHoiatus: rakenduse eemaldamisel ka see fail ilmselt kustutatakse. Võtmete eksportimine õnnestus - Krüptitud sõnumite taastamine Halda võtmete varundust - Impordi E2E läbiva krüptimise võtmed jututubade jaoks Impordi jututoa krüptovõtmed Impordi võtmed kohalikust failist Impordi Kasuta krüptimist vaid verifitseeritud sessioonides Ära iialgi saada sellest sessioonist krüptitud sõnumeid verifitseerimata sessioonidesse. - Ei ole verifitseeritud Verifitseeritud Mustas nimekirjas - tundmatu sessioon tundmatu ip-aadress ei midagi - Verifitseeri Eemalda verifitseerimine Lisa musta nimekirja Eemalda mustast nimekirjast - Verifitseeri sessioon Kinnita seda võrreldes järgnevaid andmeid oma teise sessiooni kasutajaseadetes: Kui nad omavahel ei klapi, siis teie suhtluse turvalisus võib olla ohus. Kinnitan, et võtmed klapivad omavahel - Jututoas leidub tundmatuid sessioone Siin jututoas leidub tundmatuid ja verifitseerimata sessioone. \nSee tähendab, et pole mitte mingit kindlust selles osas, et teised kasutajad on ka tegelikult need, keda nad väidavad end olevat. \nKui sa tõesti soovid, siis sa võid saata sõnumi ka verifitseerimata kasutajatele, kuid meie soovitame, et sa esmalt läbid verifitseerimise kõikide nende sessioonidega. \n \nTundmatud sessioonid: - Vali jututubade loend Server kas pole kättesaadav või on ülekoormatud Sisesta koduserveri nimi, mille avalike jututubade loendit soovid näha Koduserveri aadress Kõik jututoad %s serveris Kõik %s jututoad - Kirjuta siia… - - 1 lugemata teavitatud sõnum + %d lugemata teavitatud sõnum %d lugemata teavitatud sõnumit - 1 lugemata teavitatud sõnum + %d lugemata teavitatud sõnum %s lugemata teavitatud sõnumit Selle valiku kasutamine eeldab kolmanda osapoole rakendust sõnumite salvestamiseks. Jätkamaks pead nõustuma kasutustingimustega. - Sa oled lisanud uue sessiooni \'%s\', mis soovib saada krüptimisvõtmeid. Uus sessioon soovib krüptovõtmeid. \nSessiooni nimi: %1$s @@ -1500,7 +1281,6 @@ \nSessiooni nimi: %1$s \nViimati nähtud: %2$s \nKui sa pole mõnes muus seadmes sisse loginud, siis ära pane seda teadet tähele. - Alusta verifitseerimist Verifitseeri Jaga ilma verifitseerimata @@ -1508,10 +1288,8 @@ Krüptovõtmete jagamise päring Eira päringut Eira - Hoiatus! Rühmakõnede funktsionaalsus on veel arendamisel ja ei pruugi hetkel olla töökindel. - Käsu viga Tundmatu käsk: %s Käsk „%s“ eeldab rohkem parameetreid või mõni neist on vigane. @@ -1528,51 +1306,41 @@ Muudab sinu kuvatavat nime Lülita markdown-vormingu kasutamine sisse või välja Paranda Matrix\'i rakenduse haldust - Markdown-vormindus on kasutusel. Markdown-vormindus ei ole kasutusel. - Välja lülitatud Vaikne Jutukas - Loo Loo kogukond Kogukonna nimi Näiteks Kogukonna tunnus näiteks - Avaleht Inimesed Jututoad Kasutajaid ei ole - Jututoad Liikmed Kutsutud Filtreeri kogukonna liikmeid Filtreeri kogukonna jututubasid - - 1 liige + %d liige %d liiget - - 1 jututuba + %d jututuba %d jututuba Kogukonna haldur ei ole kogukonna pikka kirjeldust koostanud. - %2$s müksas sind välja %1$s jututoast Lugemisteatise tunnuspilt Teavituse tunnuspilt Tunnuspilt - Selleks et jätkata koduserveri %1$s kasutamist sa pead üle vaatama ja nõustuma meie kasutustingimustega. Vaata üle - Deaktiveeri konto See muudab sinu konto jäädavalt mittekasutatavaks. Sina ei saa enam sisse logida ja keegi teine ei saa seda kasutajatunnust uuesti pruukida. Samuti logitakse sind välja kõikidest jututubadest, kus sa osaled ning eemaldatakse kõik sinu andmed sinu isikutuvastusserverist. Seda tegevust ei saa tagasi pöörata. \n @@ -1581,58 +1349,44 @@ \nMatrix\'i sõnumite nähtavus on sarnane e-posti kirjadega. Sõnumite unustamine tegelikult tähendab seda, et sinu varemsaadetud sõnumeid ei jagata uute või veel registreerumata kasutajatega, kuid registeerunud kasutajad, kes juba on need sõnumid saanud, võivad neid ka jätkuvalt lugeda. Jätkamiseks palun sisesta oma salasõna: Deaktiveeri konto - Palun sisesta kasutajanimi. Palun sisesta oma salasõna. See jututuba on asendatud teise jututoaga ning ei ole enam kasutusel Vestlus jätkub siin See jututuba on järg varasemale vestlusele Vanemate sõnumite nägemiseks klõpsi siin - Ressursipiir on ületatud Võta ühendust teenuse haldajaga - palun võta ühendust oma teenuse haldajaga - See koduserver ületanud ühe oma ressursipiirangutest, seega mõned kasutajad ei saa sisse logida. See koduserver ületanud ühe oma ressursipiirangutest. - See koduserver on saavutanud igakuise aktiivsete kasutajate piiri, seega mõned kasutajad ei saa sisse logida. See koduserver on saavutanud igakuise aktiivsete kasutajate piiri. - Selle piiri suurendamiseks palun %s. Jätkamaks selle teenuse kasutamist palun %s. - Lae jututoa kasutajad aeglaselt Paranda jõudlust ja lae jututoa liikmed alles selle esimese vaatamise ajal. Sinu koduserver ei võimalda jututoa kasutajate aeglast laadimist. Proovi hiljem uuesti. - Vabandust, aga tekkis viga - laienda ahenda - Näita teabevälja Alati Sõnumite ja vigade puhul Ainult vigade puhul - %1$s: %1$s: %2$s +%d %d+ Ühtegi Google Play teenuste rakendusefaili ei leidunud. Teavitused ei pruugi korralikult toimida. - Loo paroolifraas Sisesta paroolifraas veel üks kord Sisesta paroolifraas Paroolifraasid ei klappi mitte Palun sisesta paroolifraas Paroolifraas on liiga nõrk - "Kui sa soovid, et Element looks taastevõtme, siis palun kustuta paroolifraas." Matrix\'i sessioone ei leidu - Ära kunagi kaota krüptitud sõnumeid Sõnumid krüptitud jututubades kasutavad läbivat krüptimist. Ainult sinul ja saaja(te)l on võtmed selliste sõnumite lugemiseks. \n @@ -1645,31 +1399,23 @@ Ootamatu viga Alustasin varukoopia tegemist Teen taustal sinu krüptovõtmetest varukoopia sinu koduserverisse. Esimese varukoopia tegemine võib võtta paar minutit. - - Kas sa oled kindel\? Kui sa logid välja või kaotad seadme, siis sa ei saa enam lugeda oma krüptitud sõnumeid. - Laen varukoopia versiooni… Selleks, et krüptitud sõnumite ajalugu lukust lahti võtta, kasuta oma taastamiseks mõeldud paroolifraasi kasuta oma taastevõtit Kas sa ei tea oma taastamiseks mõeldud paroolifraasi\? Siis sa võid %s. - Selleks, et krüptitud sõnumite ajalugu lukust lahti võtta, kasuta oma taastevõtit Sisesta taastevõti - Sõnumite taastamine - Kas sa kaotasid oma taastevõtme\? Sa võid seadistustes määrata uue. Selle paroolifraasiga ei õnnestunud varundust dekrüptida: palun kontrolli, kas sa kasutad õiget taastamiseks mõeldud paroolifraasi. Võrguühenduse viga: palun kontrolli, kas võrguühendus on olemas ja proovi uuesti. - Taastan võtmed varundusest: Arvutan taastevõtit… Laen alla krüptovõtmeid… Impordin krüptovõtmeid… Selle taastevõtmega ei õnnestunud varundust dekrüptida: palun kontrolli, kas sa kasutad õiget taastevõtit. - Varukoopiast taastatud %s! Taastasin varukoopiast ühe võtmega. @@ -1679,18 +1425,13 @@ Üks uus võti on lisatud siia sessiooni. %d uut võtit on lisatud siia sessiooni. - Ei õnnestunud laadida taastevõtmete versiooni (%s). Sessioonis pole krüptograafia kasutusel - - Taasta varundusest Kustuta varukoopia - Võtmete varundus on selles sesioonis korrektselt üles seatud. Krüptovõtmete varundamine ei ole selles sessioonis kasutusel. Sinu selle sessiooni krüptovõtmeid ei varundata. - Varukoopial on allkiri tundmatult sessioonilt tunnusega %s. Varukoopial on selle sessiooni kehtiv allkiri. Varukoopial on kehtiv allkiri verifitseeritud sessioonilt tunnusega %s. @@ -1698,63 +1439,47 @@ Varukoopial on vigane allkiri verifitseeritud sessioonilt tunnusega %s Varukoopial on vigane allkiri verifitseerimata sessioonilt tunnusega %s Ei õnnestunud laadida teavet varukoopia usaldamiseks (%s). - Selleks, et saaksid selles sessioonis kasutada krüptovõtmete varundamist, pead esmalt võtmed taastama oma paroolifraasi või taastevõtme abil. Kustutan varukoopia… Varukoopia kustutamine ei õnnestunud (%s) - Kontrollin varukoopia olekut Kustuta varukoopia Kas kustutame krüptovõtmete varukoopia serverist\? Sellisel juhul sa ei saa kasutada ka taastevõtit krüptitud sõnumite ajaloo loetavaks muutmseks. - Uus võtmete varukoopia Tuvastasin uue krüptovõtmete varukoopia. \n \nKui sa ei ole ise uusi taastamise meetodeid lisanud, siis võib olla tegemist ründega sinu konto vastu. Palun vaheta koheselt oma kasutajakonto salasõna ning määra seadistustes uus taastemeetod. See olin mina - Ära kunagi kaota krüptitud sõnumeid Võta kasutusele krüptovõtmete varundamine - Turvaline varundus Hoia ära, et kaotad ligipääsu krüptitud sõnumitele ja andmetele - Ära kunagi kaota krüptitud sõnumeid Kasuta varundatud võtmeid - Uued sõnumite krüptovõtmed Halda võtmete varunduses - Varundan sinu krüptovõtmeid. Selleks võib kuluda mitu minutit… - - Võta kasutusele turvaline varundus - Kõik krüptovõtmed on varundatud Varundan üht krüptovõtit… Varundan %d krüptovõtit… - Versioon Algoritm Allkiri - Vigane vastus koduserveri tuvastamise päringule Serveriseadistuste automaatne sõnalõpetus Element tuvastas kohandatud serveriseadistused sinu kasutajanime domeenis „%1$s“: \n%2$s Kasuta seadistusi - Oled kehtetu või aegunud kasutajanime või salasõna tõttu välja logitud. - Verifitseeri lühikese sõne võrdlemise teel. "Parima turvalisuse mõttes on oluline, et teed seda nii, et kas olete üheskoos või kasutate muud suhtluskanalit, mida mõlemad usaldate." Alusta verifitseerimist Saabuv verifitseerimispalve Selle sessiooni usaldamiseks peaksid ta verifitseerima. Kui sa pruugid läbivalt krüptitud sõnumeid, siis partnerite sessioonide verifitseerimine tagab sulle täiendava meelerahu. Selle sessiooni verifitseerimisel loetakse ta usaldusväärseks ning samuti märgitakse sinu sessioon tema jaoks usaldusväärseks. - See sessioon ei tea midagi antud toimingust Sessioon ei suuda kokku leppida võtmete, räsi, MAC\'i või SAS\'i meetodi osas Räsid ei klapi @@ -1764,20 +1489,15 @@ Krüptovõtmed ei klapi Kasutajad ei klapi Teadmata viga - Sa ei kasuta ühtegi isikutuvastusserverit Ühtegi isikutuvastusserverit pole seadistatud, aga salasõna muutmiseks on see oluline. - Tundub, et sa üritad luua ühendust teise koduserveriga. Kas sa soovid välja logida\? - Muuda Vasta - Proovi uuesti Selleks, et asuda rakendust kasutama, liitu mõne jututoaga. Saatis sulle kutse %s kutsus - Ei tea… kõik vist on nüüd tehtud! Sul ei ole rohkem lugemata sõnumeid Tere tulemast koju! @@ -1787,15 +1507,11 @@ Jututoad Usaldamiseks vajaliku teabe laadimisel tekkis viga Krüptovõtmete varukoopia andmete laadimisel tekkis viga - Impordi läbiva krüptimise võtmed failist „%1$s“. - Matrix\'i SDK versioon Muud kolmandate osapoolte litsentsid Sa juba vaatad seda jututuba! - Reageeri lennult - Üldist Eelistused Turvalisus ja privaatsus @@ -1803,57 +1519,40 @@ Tõuketeavituste reeglid Tõuketeavituste reegleid pole kirjeldatud Tõuketeavituste võrguväravaid pole registreeritud - app_id: push_key: app_display_name: session_name: URL: Vorming: - Heli ja video Abiteave ning info meie kohta - - Registreeri tunnusluba - Näita peidetud sündmusi ajajoonel - Isiklikud sõnumid - Ootan… Krüptin pisipilti… Saadan pisipilti (%1$s / %2$s) Krüptin faili… Saadan faili (%1$s / %2$s) - Laen alla faili %1$s… %1$s on alla laetud! - (muudetud) - Sõnumite muutmised Muutmisi ei leidunud - Nimi või kasutajatunnus (#example:matrix.org) - Luba vastamine ajajoonel viipamisega Link on kopeeritud lõikelauale - Lisa Matrix\'i tunnuse alusel Loon jututuba… Tulemusi ei leidunud. Otsinguks serverist kasuta „Lisa Matrix\'i tunnuse alusel“. Otsingutulemuste jaoks kirjuta midagi Liitun jututoaga … - Kasutustingimused Vaata kasutustingimused üle Ole teiste poolt leitav Kasuta roboteid, võrgusildu, vidinaid või kleepsupakke - Loe aadressil - - Isikutuvastusserver Katkesta ühendus isikutuvastusserveriga Seadista isikutuvastusserverit @@ -1866,50 +1565,39 @@ Isikutuvastusserveri kasutamise lõpetamine tähendab, et sa ei ole leitav teiste kasutajate poolt ega sulle ei saa telefoninumbri või e-posti aadressi alusel kutset saata. Küll aga saab kutset saata Matrix\'i kasutajatunnuse alusel. Leitavad telefoninumbrid Lisa avaekraanile eraldi sakk lugemata teavituste jaoks. - Saatsime kinnituskirja e-posti aadressile %s. Palun vaata oma kirju ja klõpsi selles kirjas leiduvat linki Saatsime kinnituskirja e-posti aadressile %s. Esmalt palun vaata oma kirju ja klõpsi selles kirjas leiduvat linki Selleks, et sind võiks leida e-posti aadressi või telefoninumbri alusel, nõustu isikutuvastusserveri (%s) kasutustingimustega. - Lülita sisse detailsete logide salvestamine. Sa parasjagu ei eira ühtegi kasutajat - Lisavalikute nägemiseks vajuta jututoa nimel kaua - - %1$s muutis selle jututoa avalikuks kõigile, kes teavad tema aadressi. Sina muutsid selle jututoa avalikuks kõigile, kes teavad tema aadressi. %1$s määras, et jututuppa pääseb vaid kutsega. Vestle kahekaupa või rühmiti Ma olen verifitseerinud oma e-posti aadressi - Õnnestus! Sinu salasõna ei ole veel muutunud. \n \nKas katkestame muutmise\? - Määra e-posti aadress Seadista e-posti aadress, mis võimaldaks sul vajadusel taastada ligipääsu oma kontole. Lisaks võid kasutada e-posti aadressi või telefoninumbrit, et need inimesed, kes sind tunnevad, saaks sind leida. E-posti aadress E-posti aadress (kui soovid) Järgmine - Lisa telefoninumber Salvesta oma telefoninumber selleks, et teised kasutajad saaks sind leida. Palun kasuta rahvusvahelist numbrivormingut. Telefoninumber Telefoninumber (kui soovid) Järgmine - Kinnita telefoninumbri lisamine Me just saatsime tuvastuskoodi telefoninumbrile %1$s. Kinnitamaks, et see on sinu number, palun sisesta ta alljärgnevalt. Sisesta tuvastuskood Saada uuesti Järgmine - Rahvusvahelise telefoninumbri alguses peaks olema „+“ Telefoninumber ei tundu olema õige. Palun kontrolli seda veel üks kord - Logi sisse koduserverisse %1$s Kasutajanimi või e-posti aadress Kasutajanimi @@ -1920,28 +1608,23 @@ Sinu kasutajakonto pole veel loodud. \n \nKas katkestame konto registreerimise\? - Vali matrix.org Vali teenusepakkujaks Element Matrix Services Vali mõni muu koduserver Palun tee läbi robotilõksu katse Jätkamiseks nõustu kasutustingimustega - Palun kontrolli oma e-kirju Saatsime e-kirja %1$s aadressile. \nKonto loomisega jätkamiseks palun klõpsi selles kirjas leiduvat linki. Sisestatud kood ei ole õige. Palun kontrolli seda. Iganenud koduserver Selle koduserveri versioon on ühenduse loomiseks liiga vana. Palu oma koduserveri haldajal seda uuendada. - Liiga palju samaaegseid päringuid. Proovi uuesti ühe sekundi pärast… Liiga palju samaaegseid päringuid. Proovi uuesti %1$d sekundi pärast… - See ei ole õige kasutajatunnus. Õige vorming on: \'@torekasutaja:koduserver.ee\' Ei leia toimivat koduserverit. Palun vaata, et kasutajatunnus oleks õige - Logi sisse "Sinu koduserveri (%1$s) haldur on sind sinu kontolt %2$s (%3$s) välja loginud." Selleks, et laadida vaid siin seadmes kasutatud krüptovõtmeid, palun logi sisse. Sa vajad neid võtmeid selleks, et lugeda kõiki oma krüptitud sõnumeid kõikides oma seadmetes. @@ -1957,29 +1640,21 @@ Seadistused Praegune sessioon Muud sessioonid - Kuvan vaid esimesi tulemusi, kirjuta natuke pikemalt… - Kiire lõppmäng Teadmata vigade puhul võib Element sagedamini kokku joosta - Lisa ¯\\_(ツ)_/¯ smaili vormindamata teksti algusesse - Võta jututoas krüptimine kasutusele "Kui krüptimine on juba kasutusele võetud, siis ei saa seda enam eemaldada." - Sinu e-posti domeeni ei saa kasutada selles serveris registreerimisel - Sisselogimine ei ole usaldusväärne Nad klapivad Nad ei klapi Verifitseeri see sessioon tehes kindlaks et järgnev emoji kuvatakse samas järjekorras partneri ekraanil. Parima turvalisuse nimel palun kasuta mõnda muud usaldusväärset suhtlusvahendit või kohtu silmast silma. Roheline kilp näitab, et kasutaja on usaldusväärne. Tagamaks, et kogu jututuba on usaldusväärne, siis peaks kõikidel selle liikmetel olema roheline kilp. - Ei ole turvaline Kleeps - Ootan… %s tühistas Sa tühistasid @@ -1989,54 +1664,42 @@ Verifitseerimispäring Verifitseeri see sessioon Verifitseeri käsitsi - Sina - Selleks, et üksteist turvaliselt verifitseerida, skaneeri see QR-koodi teise kasutaja seadmest Skaneeri teise osapoole QR-koodi Kas sa ei saa skaneerida Kui te pole üheskoos, siis võrrelge selle asemel pilte - Verifitseeri võrreldes emoji\'sid - Verifitseeri emoji\'de abil Kui sa ei saa skaneerida eespool kuvatud koodi, siis verifitseeri unikaalsete emoji\'de kombinatsiooni võrdlemise teel. - QR-kood - Verifitseeri %s %s on verifitseeritud Ootan vastust kasutajalt %s… Kasuta seda sessiooni uute sessioonide verifitseerimiseks, andes sellega ligipääsu krüptitud sõnumitele. See ei olnud mina Sinu kasutajakonto võib olla sattunud valedesse kätesse - Kasuta taastevõtit Vali oma taastevõti, sisesta ta käsitsi või aseta lõikelaualt Selle taastevõtmega ei õnnestunud varundust dekrüptida: palun kontrolli, kas sa kasutad õiget taastevõtit. Turvahoidla kasutamine ei õnnestu - Krüptimata Krüptitud verifitseerimata seadme poolt Vaata üle, kust sa oled Matrix\'i võrku loginud Selleks et sinu konto ja sõnumid oleks turvatud, verifitseeri kõik oma sessioonid Verifitseeri uus kasutajasessioon, mis pruugib sinu kontot: %1$s - "Verifitseeri käsitsi etteantud teksti abil" Verifitseeri sisselogimissessioon Verifitseeri interaktiivselt Emoji abil Kinnita oma isikusamasust verifitseerides seda sisselogimissessiooni mõnest oma muust sessioonist. Sellega tagad ka ligipääsu krüptitud sõnumitele. Märgi usaldusväärseks - Palun vali kasutajanimi. Palun vali salasõna. Kontrolli seda linki veel üks kord See %1$s link viib sind teise veebisaiti: %2$s. \n \nKas sa kindlasti soovid jätkata\? - Otsevestluse loomine ei õnnestunud. Palun kontrolli, et kasutajanimed oleks õiged ja proovi uuesti. - Lisa liikmeid KUTSU Kutsun kasutajaid… @@ -2048,11 +1711,9 @@ Kutsed saadetud kasutajatele %1$s ja veel %2$d\'le kasutajale Meil ei õnnestunud neile kasutajatele kutset saata. Palun kontrolli, keda soovid kutsuda ning proovi uuesti. - Hetkel kasutatav keel Muud saadaval olevad keeled Laen saadaval olevaid keeli… - Ava %s kasutustingimused Kas katkestame ühenduse isikutuvastusserveriga %s\? Palun esmalt seadista isikutuvastusserver. @@ -2060,7 +1721,6 @@ Sinu privaatsuse nimel saadab Element e-posti aadressid ja telefoninumbrid serverisse vaid räsituna. Seoste loomine ei õnnestunud. Selle tunnusega ei ole hetkel ühtegi seost. - Sinu koduserver (%1$s) soovitab kasutada sinu isikutuvastusserverina %2$s teenust Kasuta %1$s teenust Alternatiivina või sa sisestada ükspuha missuguse isikutuvastusserveri aadressi @@ -2073,9 +1733,7 @@ Eemalda mikrofoni summutamine Lõpeta kaamera kasutamine Võta kaamera kasutusele - Võta kasutusele turvaline varundus - Turvaline varundus Tagamaks, et sa ei kaota ligipääsu krüptitud sõnumitele ja andmetele, varunda krüptimisvõtmed oma serveris. Võta kasutusele @@ -2083,22 +1741,17 @@ Loome turvavõtme, mida sa peaksid hoidma turvalises kohas, nagu näiteks arvutis salasõnade halduris või vana kooli seifis. Kasuta turvafraasi Sisesta turvafraas, mida vaid sina tead ning loo võti varukoopia jaoks. - Salvesta oma turvavõti Salvesta turvavõti turvalisesse kohta, nagu näiteks arvutis salasõnade haldur või vana kooli seif. - Määra turvafraas Andmete kaitsmiseks sinu koduserveris sisesta turvafraas, mida vaid sina tead. Turvafraas Kinnituseks sisesta turvafraas uuesti. - Salvesta oma turvavõti Salvesta oma turvavõti turvalisesse kohta, nagu näiteks arvutis salasõnade haldur või vana kooli seif. - Jututoa nimi Jututoa teema Jututoa seadistuste muutmine õnnestus - Sa ei saa seda sõnumit lugeda Ootan kuni sõnumite dekrüptimine lõppeb ja selleks võib kuluda aega Dekrüptimine ei ole võimalik @@ -2107,17 +1760,12 @@ Sa ei saa seda sõnumit lugeda, kuna saatja ei usalda sinu sessiooni Sa ei saa seda sõnumit lugeda, kuna saatja teadlikult ei saatnud vajalikke võtmeid Ootan krüptimise ajalugu - Riot\'i uus nimi on Element! Meil on hea meel teatada, et oleme muutnud nime! Sinu rakendus on uuendatud ning sa oled oma kontole sisse logitud. Selge lugu Vaata lisateavet - element - - Salvesta taastevõti järgnevalt - Lisa telefoniraamatust Sinu telefoniraamat on tühi Telefoniraamat @@ -2125,31 +1773,23 @@ Laen sinu kontaktide loendit… Sinu kontaktide loend on tühi Sinu kontaktide loend - Tühista kutse Kas tühistame kasutaja %1$s kutse\? - Suhtluskeeld on seatud kasutaja %1$s poolt Kasutaja ligipääsu taastamine ei õnnestunud - Tõuketeavitused on välja lülitatud Jätkamaks sisesta oma %s. - %s abil krüpti ja loe oma sõnumeid ning taga usaldusseosed. Kinnituseks sisesta %s uuesti. Palun ära kasuta selleks oma tavalist konto salasõna. - Andmete kaitsmiseks sinu koduserveris sisesta turvafraas, mida vaid sina tead. - Selleks võib kuluda mitu sekundit. Alustame taastamist. Sinu taastevõti Valmis! Hoia seda turvaliselt Lõpeta - Kasuta seda %1$s turvavõrguna olukorras, kus unustad oma %2$s. - Avaldan justloodud Identiteedivõtmed Loon paroolifraasi põhjal turvavõtme Määratlen vaikimisi kasutatava SSSS-võtme @@ -2157,14 +1797,10 @@ Sünkroniseerin kasutaja võtit Sünkroniseerin allkirjavõtit Teeme võtmetest varukoopia - - Trüki ta välja ja hoia turvalises kohas Salvesta ta mälupulgale või varunduskettale Kopeeri ta isiklikku andmehoidlasse mis asub pilves - Seda tegevust ei saa teha nutiseadmest - Paroolifraasi kasutusele võtmine võimaldab sul krüptida ja lugeda oma sõnumeid ning tagada usaldusseoseid. \n \nKui sa ei soovi selleks kasutada sõnumite salasõna, siis võid genereerida ka sõnumivõtme. @@ -2172,21 +1808,16 @@ Kui sa tühistad nüüd, siis sa võid peale viimasest seadmest välja logimist kaotada ligipääsu oma krüptitud sõnumitele ja andmetele. \n \nAga sa võid seadistustes võtta kasutusele turvalise varunduse ning hallata oma krüptovõtmeid. - Krüptimine on kasutusel Sõnumid selles jututoas on läbivalt krüptitud. Uuri lisaks ja verifitseeri kasutajad nende kasutajaprofiilides. Krüptimine ei ole kasutusel Selles jututoas kasutatud krüptimine ei ole toetatud - %s lõi ja seadistas jututoa. Sina lõid ja seadistasid jututoa. - Peaaegu valmis! Kas teises seadmes kuvatakse sama kilpi\? Peaaegu valmis! Ootan kinnitust… Ootan vastust kasutajalt %s… - Võtmete importimine ei õnnestunud - Teavituste seadistused Sõnumid, mis sisaldavad sõna @room Kahepoolsete vestluste krüptitud sõnumid @@ -2194,9 +1825,7 @@ Kui jututubasid uuendatakse Leia lahendus vigadele Seadista teavituse olulisus sündmuse alusel - Saadab sõnumi vormindamata tekstina ega tõlgenda seda markdown-vormindusena - Sul ei ole õigusi siin jututoas rühmakõne algatamiseks Rühmakõne on hetkel käimas! Alusta videokoosolekut @@ -2210,22 +1839,15 @@ %1$d/%2$d võtme importimine õnnestus. %1$d/%2$d võtme importimine õnnestus. - Halda lõiminguid Aktiivseid vidinaid ei leidu Jututuba on loodud, kuid mõned kutsed on saatmata järgmistel põhjustel: \n \n%s - - Ei õnnestunud dekrüptida ajajoonel leiduvat sõnumit Sa parasjagu jagad oma e-posti aadressi ja telefoninumbrit isikutuvastusserveriga %1$s. Selleks, et lõpetada see jagamine, pead uuesti looma ühenduse isikutuvastusserveriga %2$s. Detailne logimine võimaldab arendajatel saada rohkem teadet kui sa peale nutiseadme tõsist raputamist saadad veateate. Kui ka detailne logimine on kasutusel, siis rakendus ei logi sõnumite sisu ega isiklikke andmeid. - - Kui sa oled nõustunud koduserveri kasutustingimustega, siis palun proovi uuesti. - Tundub, et server ei suuda päringule mõistliku ajaga vastata ning see võib olla seotud kas vigase ühendusega või serveri veaga. Palun proovi mõne aja pärast uuesti. - Ava navigatsioonivalik Ava menüüvalik jututoa loomiseks Sulge menüüvalik jututoa loomiseks… @@ -2233,7 +1855,6 @@ Näita salasõna Peida salasõna Mine lõppu - %1$s, %2$s ja %3$d muu loevad %1$s, %2$s ja %3$d muud loevad @@ -2242,12 +1863,10 @@ %1$s ja %2$s lugesid %s luges - 1 kasutaja luges + %d kasutaja luges %d kasutajat lugesid - See fail „%1$s“ (%2$s) on üleslaadimiseks liiga suur. Failisuuruse ülempiir on %3$s. - Manuse laadimisel tekkis viga. Fail Kontakt @@ -2256,31 +1875,25 @@ Meediagalerii Kleeps Jagatud andmete töötlemine ei õnnestunud - MEEDIA EIRA KASUTAJAT - Teatasin sisust - Selle sisu kohta on nüüd teade saadetud. -\n -\nKui sa ei soovi enam näha selle kasutaja sisu, siis sa võid tema sõnumite peitmiseks kasutaja blokeerida + Selle sisu kohta on nüüd teade saadetud. +\n +\nKui sa ei soovi enam näha selle kasutaja sisu, siis sa võid tema sõnumite peitmiseks kasutajat eirata. See on rämpssõnum - Haldaja sai nüüd teate, et see sisu on rämpssõnum. -\n -\nKui sa ei soovi enam näha selle kasutaja sisu, siis sa võid tema sõnumite peitmiseks kasutaja blokeerida + Haldaja sai nüüd teate, et see sisu on rämpssõnum. +\n +\nKui sa ei soovi enam näha selle kasutaja sisu, siis sa võid tema sõnumite peitmiseks kasutajat eirata. See sõnum ei ole sobilik - Haldaja sai nüüd teate, et see sisu ei ole sobilik. -\n -\nKui sa ei soovi enam näha selle kasutaja sisu, siis sa võid tema sõnumite peitmiseks kasutaja blokeerida - + Haldaja sai nüüd teate, et see sisu ei ole sobilik. +\n +\nKui sa ei soovi enam näha selle kasutaja sisu, siis sa võid tema sõnumite peitmiseks kasutajat eirata. Läbiva krüptograafia jaoks vajalike võtmete salvestamiseks kohalikule andmekandjale vaajab Element sinu luba. \n \nSelleks et saaksid võtmed käsitsi eksportida, siis palun anna selleks järgmises vaates luba. - Hetkel puudub võrguühendus - Eira kasutajat - Kõik sõnumid (lärmakas) Kõik sõnumid Ainult mainimised @@ -2294,7 +1907,6 @@ Saadab selle sõnumi rõõmurikkujana Rõõmurikkuja Reageerimiste leidmiseks sisesta otsingusõnad. - See on sinu vestlus, mida sa ka tegelikult kontrollid. Salasõna kinnitamiseks klõpsi kirjas olnud linki. Kui oled avanud selles kirjas leidunud lingi, siis palun vajuta järgnevat nuppu. Kui sul juba on kasutajakonto ning sa tead oma Matrix\'i kasutajatunnust ja salasõna, siis alternatiivina sa võid kasutada seda meetodit: @@ -2304,7 +1916,6 @@ Matrix\'i kasutajatunnus Kui sa ei tea oma salasṍna, siis mine tagasi ja loo uus salasõna. Nähtud kasutaja poolt - Sa oled loginud välja See võib juhtuda eri põhjustel: \n @@ -2314,18 +1925,15 @@ \n \n• Sinu koduserveri haldaja on turvakaalutlustel keelanud sinu ligipääsu. Logi uuesti sisse - Sa oled loginud välja Praegune sessioon kuulub kasutajale %1$s, kuid sina üritad siseneda kasutaajaga %2$s. Sellist võimalust Element hetkel ei toeta. \nPalun eemalda kõik andmed ning siis logi sisse muu kontoga. - Mõni järgnevatest võib olla sattunud valedesse kätesse: \n \n - Sinu koduserver \n - Selle osapoole koduserver, keda sa üritad verifitseerida \n - Sinu või teise osapoole internetiühendus \n - Sinu või teise osapoole seade - Video. Pilt. Helifail @@ -2347,49 +1955,35 @@ Üleslaetud failid Lahku jututoast Lahkun jututoast… - Haldajad Moderaatorid Kohandatud õigused Kutsed Kasutajad - Peakasutaja jututoas %1$s Moderaator jututoas %1$s Vaikimisi õigused jututoas %1$s Kohandatud õigused (%1$d) jututoas %2$s - Hüppa lugemisteatise juurde - Element ei oska käsitleda %1$s-tüüpi sündmusi Element ei oska käsitleda %1$s-tüüpi sünumeid Tekkis viga, kui Element üritas töödelda sõnumi %1$s sisu - Võta vastu Keeldu Lõpeta kõne - Lõpeta eiramine - See sessioon ei oska jagada verifitseerimisteavet sinu teiste sessioonidega. \nSeni salvestatakse verifitseerimisteave kohalikus seadmes ja jagatakse rakenduse tulevases versioonis. - Hiljutised jututoad Kuni antud kasutaja seda sessiooni ei usalda, on ta märgitud hoiatussildiga. Alternatiivina on sul võimalus verifiseerimist ka käsitsi teha. - - Käivita risttunnustamine Lähtesta krüptovõtmed - QR-kood - Peaaegu valmis! Kas %s kuvab sama kilpi\? Jah Ei - Ühendus sinu serveriga on katkenud Lennurežiim on kasutusel - Arendaja töövahendid Kasutajakonto andmed @@ -2404,45 +1998,33 @@ Loob lihtsa hääletuse Kasuta taastamiseks mõeldud paroolifraasi või võtit Kui sa ei pääse ligi olemasolevale sessioonile - Uus sisselogimine - Ei leia turvahoidlast vajalikke andmeid Sisesta turvahoidla paroolifraas Hoiatus: Sa peaksid turvahoidlat kasutama vaid usaldusväärsest seadmest - Eemalda… Kas sa soovid seda manust saata kasutajale %1$s\? Saada pilt algupärases suuruses Saada pildid algupärases suuruses - Kinnita eemaldamine Kas sa oled kindel et soovid eemaldada (kustutada) seda sündmust\? Pane tähele, et see muutus võib tagasi pöörduda, kui muudad hiljem jututoa nime või teema nime. Lisa põhjus Kustutamise põhjus - Kasutaja kustutas sündmuse, põhjusena %1$s Jututoa haldur on sündmust modereerinud, põhjusena %1$s - Krüptovõtmed on juba uuendatud! - Element Android - Päringud võtmete laadimiseks - Võta krüptitud sõnumite ajalugu lukust lahti - Värskenda - Uus sisselogimine. Kas see olid sina\? Kontrollimiseks ja verifitseerimiseks klõpsi Kui sa katkestad, siis sa ei saa selles seadmes lugeda krüptitud sõnumeid ning muud kasutajad ei usalda seda Kui sa katkestad, siis sa ei saa oma uues seadmes lugeda krüptitud sõnumeid ning muud kasutajad ei usalda seda Kui sa nüüd katkestad, siis sa ei verifitseeri seadet %1$s (%2$s). Alusta uuesti sealsest profiilist. - Mõni järgnevatest võib olla sattunud valedesse kätesse: \n \n - Sinu salasõna @@ -2451,40 +2033,29 @@ \n - Sinu või teise osapoole internetiühendus \n \nPalun vaheta seadistuste lehelt koheselt oma kasutajakonto salasõna ja taastevõti. - Seadistuste lehelt verifitseeri oma seade. Verifitseerimine on tühistatud - Taastamiseks mõeldud paroolifraas Sõnumite krüptovõti Kasutajakonto salasõna - Seadista %s Loo sõnumite krüptovõti - Kinnita %s - Sinu %2$s ja %1$s on seadistatud. \n \nHoia neid turvaliselt! Kui sa oled oma rakendustes kõik sessioonid sulgenud, siis sa vajad neid ligipääsu taastamiseks oma krüptitud sõnumitele ja turvalistele andmetele. - Vigane salasõna ja/või kasutajanimi. Palun vaata, kas mitte sisestatud salasõna ei alga või lõppe tühikutega. See kasutajakonto on deaktiveeritud. - Sõnum… - Krüptimise uuendus on saadaval Võta risttunnustamine kasutusele Selleks et sinu vestlused oleks turvatud, verifitseeri end ja teisi - Jätkamaks sisesta oma %s Kasuta faili - Sisesta %s Taastamiseks mõeldud paroolifraas See ei ole sobilik taastevõti Palun sisesta taastevõti - Kontrollin varukoopia võtmeid Kontrollin varukoopia võtit (%s) Arvutan elliptilise joone võtit @@ -2493,20 +2064,16 @@ Loon taastevõtme alusel SSSS\'i Salvestan võtmete varunduse andmed SSSS\'is %1$s (%2$s) - Jätkamiseks sisesta oma võtmete varunduse paroolifraas. või kasuta oma võtmete varukoopia taastevõtit Kui sa ei tea oma võtmete varunduse paroolifraasi, siis sa võid %s. Võtmete varukoopia taastevõti - Ära luba sel rakendusel teha ekraanitõmmiseid Selle seadistusega määratakse FLAG_SECURE märge kõikidele tegevustele. Muutuse jõustamiseks vajab rakendus uuesti käivitamist. - Lisasime meediafaili galeriisse Meediafaili galeriisse lisamine ei õnnestunud Meediafaili salvestamine ei õnnestunud Määra kontole uus salasõna… - Kasuta kõige uuemat Element\'i versioon mõnes muus seadmes, nagu näiteks Element Web, Element Desktop, Element iOS, Element Android. Samuti sobib mõni muu Matrix\'i klient, mis oskab risttunnustamist Element Web \nElement Desktop @@ -2530,10 +2097,8 @@ Telefoninumbrid Kas eemaldan %s\? Palun vaata üle, et kindlasti oleksid klõpsinud linki, mille me sulle e-kirjaga saatsime. - E-posti aadressid ja telefoninumbrid Halda on Matrix\'i kontoga seotud e-posti aadresse ja telefoninumbreid - Kood Palun kasuta rahvusvahelist vormingut (telefoninumbri alguses peaks olema „+“) Lisaturvalisus mõttes verifitseeri %s võrreldes selleks üheks korraks loodud koodi teie mõlemas seadmes. @@ -2541,20 +2106,74 @@ \nParima turvalisuse nimel kohtuge silmast silma. Kinnita oma isikusamasust verifitseerides seda sisselogimissessiooni. Sellega tagad ligipääsu krüptitud sõnumitele. Vabandust, selline tegevus pole veel võimalik kasutajakontode puhul, kus kasutatakse ühekordset sisselogimist. - Ei ole võimalik avada sellise jututoa vaadet, kus sulle on seatud suhtluskeeld. Ei leia sellist jututuba. Palun kontrolli, et ta ikka olemas on. %d sekund %d sekundit - Näita jututoa liikmete olekusündmusi Sealhulgas kutsumisi, liitumisi, lahkumisi, müksamisi, keelamisi ning tunnuspildi ja kuvatava nime muutusi. Küsitlus Robotinupud Reageeris: %s Verifitseerimise tulemus - Link oli vigane - + Sul ei ole õigusi siin jututoas helistamiseks + Kas kustutame %1$s tüüpi kasutajakonto andmed\? +\n +\nKuna nii mõndagi ootamatut võib juhtuda, siis kasuta seda võimalust mõningase ettevaatusega. + PIN-kood on nõutav alati, kui sa Element\'i avad. + PIN-kood on nõutav, kui sa kahe minuti jooksul pole Element\'i kasutanud. + Kahe minuti möödumisel küsi uuesti PIN-koodi + Näita vaid napi teavitusena lugemata sõnumite arvu. + Näita täpsemat teavet nagu jututoa nimi ja sõnumi sisu. + Näita teavitustes sisu + PIN-kood on ainus võimalus, kuidas sa saad Element\'i lukust lahti. + Kasuta selles seadmes leiduvaid biomeetrilise tuvastuse võimalusi nagu sõrmejälgede lugejat või näotuvastust. + Kasuta biomeetrilist tuvastust + Seadista + Turva ligipääsu kasutades PIN-koodi või biomeetrilist tuvastust. + Turva oma ligipääsu + + Näita seda seadet, millega saaksid praegu verifitseerimist alustada + Näita %d seadet, millega saaksid praegu verifitseerimist alustada + + Siis alustad nii, et kadunud on ajalugu, sõnumid ning usaldatud seadmed ja kasutajad + Kui sa alustad kõigega algusest + Jätka vaid siis, kui sul pole ühtegi muud seadet, millega seda seadet saaks verifitseerida. + Alusta kõigega algusest + Kas sa unustasid või kaotasid kõik võimalused taastada ligipääsu oma kontole\? Lähtesta kõik seadistused + Sina liitusid. + %s liitus. + See jututuba on läbivalt krüptitud. + Lahku + Seadistused + Sõnumid siin jututoas kasutavad läbivat krüptimist. +\n +\nSinu sõnumid on turvatud ning ainult sinul ja saaja(te)l on unikaalsed võtmed selliste sõnumite lugemiseks. + See jututuba ei ole läbivalt krüptitud. + "Selle koduserveri versioon on üsna vana. Palu oma koduserveri haldajat, et ta teeks vajalikud uuendused. Kui sa jätkad, siis mõni funktsionaalsus ei pruugi toimida nii, nagu vaja." + Sina määrasid, et siia jututuppa pääseb vaid kutsega. + %1$s määras, et siia jututuppa pääseb vaid kutsega. + Näita krüptitud jututubades tervet ajalugu + %1$s ja %2$s + %1$s %2$s jututoas ja %3$s + + %d kutse + %d kutset + + Sa oled klõpsinud teavitust! + "Palun klõpsi teavitust. Kui sa teavitust ei näe, palun kontrolli oma seadme seadistusi." + Teavituse kuvamine + Sa praegu vaatad teavitust! Klõpsi mind! + Tõuketeavituse vastuvõtmine ei õnnestunud. Üks lahendus oleks kui paigaldad rakenduse uuesti. + Rakendus võtab vastu tõuketeavitust + Rakendus ootab tõuketeavitust + Tõuketeavituse test + Otsing krüptitud jututubades ei ole veel toetatud. + Otsi suhtluskeelu saanud kasutajaid + Sul ei ole õigusi siin jututoas helistamiseks + Sul ei ole piisavalt õigusi, et selles jututoas alustada konverentsikõnet + Taasta algolek + \ No newline at end of file diff --git a/vector/src/main/res/values-fa/strings.xml b/vector/src/main/res/values-fa/strings.xml index 34aaeb3599..9dec816e0c 100644 --- a/vector/src/main/res/values-fa/strings.xml +++ b/vector/src/main/res/values-fa/strings.xml @@ -20,9 +20,9 @@ ذخیره ترک کردن ارسال - ارسال مجدد + ارسال دوباره نقل قول - اشتراک گذاری + هم‌رسانی بعداً مشاهده منبع حذف @@ -74,7 +74,7 @@ ساخت حساب ورود خروج از حساب - جستجو + جست‌وجو آغاز گپ جدید آغاز تماس صوتی @@ -152,7 +152,7 @@ مخاطبین من فهرست کاربران - فقط مخاطبین ماتریس + فقط مخاطبین ماتریکس شما اجازه‌ی دسترسی به مخاطبین را به برنامه نداده‌اید نتیجه‌ای نیست @@ -202,7 +202,7 @@ سرور خانه: سرور هویتی: امکان ورود وجود ندارد - %1$dm %2$ds + %1$dد %2$dث موضوع اتاق @@ -289,7 +289,7 @@ رد شدن انجام شد انصراف - نادیده‌گیری + چشم‌پوشی آیا می‌خواهید از حساب کاربری خود خارج شوید؟ علامت‌گذاری به عنوان خوانده شده @@ -304,7 +304,7 @@ جزییات انجمن ارسال برچسب پشتیبان‌گیری کلید - بازیابی پشتیبان کلید + استفاده از پشتیبان کلید پشتیبان‌گیری از کلید هنوز به پایان نرسیده است، لطفاً شکیبا باشید… در صورتی که اکنون از حساب خود خارج شوید، پیام‌های رمزنگاری شده خود را از دست خواهید داد پشتیبان‌گیری کلید در جریان است. در صورتی که اکنون از حساب خود خارج شوید، پیام‌های رمزنگاری شده خود را از دست خواهید داد. @@ -335,7 +335,7 @@ درخواست کلید ارسال شد. درخواست ارسال شد - لطفاً ریوت را روی دستگاه دیگر که می‌تواند پیام را رمزگشایی کند، اجرا کنید تا بتواند کلیدها را به این نشست بفرستد. + لطفاً المنت را روی افزاره‌ای دیگر که می‌تواند پیام را رمزگشایی کند، اجرا کنید تا بتواند کلیدها را به این نشست بفرستد. فهرست رسیدهای خواندن @@ -346,7 +346,7 @@ دوباره از من نپرس - برای بازیابی یک ایمیل وارد کنید، و بعدا در صورت دلخواه می توانید از آن برای شناسایی دوستان خود استفاده کنید. + رایانامه‌ای برای بازیابی تنظیم کرده تا بتوانید در صورت نیاز، از طریقش به دست افرادی که می‌شناسید، قابل کشف باشید. ثبت شماره تلفن (بعدا در صورت دلخواه می توانید از آن برای شناسایی دوستان خود استفاده کنید). نشانی رایانامهٔ پیوسته به حسابتان را برای بازنشانی گذواژه‌تان وارد کنید: Latn @@ -370,7 +370,7 @@ نگارش %s شرایط و ضوابط تذکّرهای سوم‌شخص - حق نشر + حق رونوشت سیاست محرمانگی عکس نمایه @@ -382,7 +382,7 @@ اطّلاعات برنامه نمایش اطّلاعات برنامه در تنظیمات سامانه. تأیید گذرواژه‌تان - نمی‌توانید این کار را از ریوت همراه انجام دهید + نمی‌توانید با المنت همراه، این کار را بکنید نیاز به تأیید هویت است @@ -395,8 +395,8 @@ آگاهی‌ها برای حسابتان از کار افتاده‌اند. \nلطفاً تتظیمات حساب را بررسی کنید. آگاهی‌ها برای این نشست به کار افتاده‌اند. - آگاهی‌ها برای این نشست از کار افتاده‌اند. -\nلطفاً تنظینات ریوت را بررسی کنید. + آگاهی‌ها برای این نشست به کار نیفتاده‌اند. +\nلطفاً تنظیمات المنت را بررسی کنید. برخی آگاهی‌ها در تنظیمات سفارشیتان از کار افتاده‌اند. خدمت آگاهی خدمت آگاهی در حال اجراست. @@ -414,7 +414,7 @@ نگارش olm شرایط و ضوابط تذکّرهای سوم‌شخص - حق نشر + حق رونوشت سیاست محرمانگی نگه‌داری رسانه پاک‌سازی انباره @@ -476,7 +476,7 @@ زمینه رمزنگاری فقط به نشست‌های تأیید شده - اتاق شامل نشست‌های ناشناخته + اتاق شامل نشست‌های ناشناخته است برای فهرست شدن اتاق‌های عمومی از یک کاساز، نامش را بنویسید تمامی اتاق‌های روی کارساز %s تمامی اتاق‌های بومی %s @@ -492,7 +492,7 @@ زمینه‌تان - افزودن کاره‌های ماتریس + افزودن کاره‌های ماتریکس پیام رمزنگاری شده ایجاد اجتماع @@ -566,7 +566,7 @@ نمایهٔ اتاق‌ها انتشار این اتاق در نمایهٔ اتاق‌ها - نگارش SDK ماتریس + نگارش SDK ماتریکس عمومی ترجیحات امنیت و محرمانگی @@ -602,8 +602,8 @@ به کار انداختن کشیدن برای پاسخ در خط زمانی - افزودن با شناسهٔ ماتریس - نتیجه‌ای پیدا نشد. برای جست‌وجو روی کارساز، از افزون با شناسهٔ ماتریس استفاده کنید. + افزودن با شناسهٔ ماتریکس + نتیجه‌ای پیدا نشد. برای جست‌وجو روی کارساز، از افزون با شناسهٔ ماتریکس استفاده کنید. پالایش با نام کاربری یا شناسه… ایجاد یک گفت‌وگوی مستقیم جدید @@ -614,12 +614,12 @@ گزارش این محتوا دلیل گزارش این محتوا گزارش - انسداد کاربر + چشم‌پوشی از کاربر محتوا گزارش شد به عنوان هرزنامه گزارش شد به عنوان نامناسب گزارش شد - انسداد کاربر + چشم‌پوشی از کاربر تنظیمات پیش‌رفته و سفارشی @@ -649,7 +649,7 @@ شکست در گرفتن نشست‌ها نشست‌ها ابزارهای توسعه - نشست را تایید کن + تأیید نشست هیچ ابطال @@ -657,8 +657,8 @@ تماس بدلیل سرور بد پیکربندی شده شکست خورد از %s استفاده کنید - یک ایمیل برای بازیابی ثبت کنید. بعدا در صورت دلخواه می توانید از آن برای شناسایی دوستان خود استفاده کنید. - یک ایمیل برای بازیابی ثبت کنید. بعدا در صورت دلخواه می توانید از آن برای شناسایی دوستان خود استفاده کنید. + رایانامه‌ای برای بازیابی تنظیم کنید. برای کشف شدن به دست افرادی که می‌شناسید، از رایانامه یا تلفن بعدی استفاده کنید. + رایانامه‌ای برای بازیابی تنظیم کنید. برای کشف شدن به دست افرادی که می‌شناسید، از رایانامه یا تلفن بعدی استفاده کنید. نشانی ایمیل پیوست شده به حساب شما باید وارد شده باشد. گذرواژه جدیدی باید وارد شود. یک ایمیل به %s فرستاده شد. هنگامی که پیوند همراه را دنبال کردید، در زیر کلیک کنید. @@ -674,24 +674,24 @@ نمی‌توان ثبت‌نام کرد : مالکیت ایمیل تایید نشد لطفا یک نشانی معتبر وارد کنید نشانی قابل دسترس نیست، لطفا آن را بررسی کنید - این یک نشانی سرور ماتریس معتبر نیست + این یک نشانی کارساز ماتریکس معتبر نیست نمی‌توان به این نشانی سرور خانه دست یافت، لطفا آن را بررسی کنید - دستگاه شما یک پروتوکل امنیتی TLS خارج از رده را استفاده می‌کند، به حمله آسیپ پذیر بوده، برای امنیت شما شما نمی‌توانید متصل شوید + افزاره‌تان از یک قرارداد امنیتی TLS تاریخ‌گذشته که به حمله آسیپ‌پذیر است استفاده می‌کند. برای امنیتتان، نخواهید توانست وصل شوید توکن دسترسی مشخص شده، شناخته نشد JSON ناهنجار شامل یک JSON معتبر نبود درخواست‌های بیش از حد ارسال شده هنوز روی پیوند ایمیل کلیک نشده - برای تماس‌های ورودی از آهنگ زنگ پیش‌فرض ریوت استفاده کن - آهنگ زنگ تماس ورودی - انتخاب آهنگ زنگ برای تماس‌ها: + استفاده از صدای زنگ پیش‌گزیدهٔ المنت برای تماس‌های ورودی + صدای زنگ تماس ورودی + گزینش صدای زنگ برای تماس‌ها: یک عکس یا ویدیو بگیر نمی‌توان ویدیو ظبط کرد اطلاعات - ریوت برای گرفتن عکس و برای تماس‌های ویدیویی باید به دوربین شما به دسترسی یابد. + المنت برای گرفتن عکس و تماس‌های ویدیویی نیاز به اجازه دارد. بازکردن سرتیتر در حال هم‌گام‌سازی… پریدن به نخستین پیام خوانده نشده. @@ -704,22 +704,22 @@ ۱ عضو فعال %d عضو فعال - انسداد + تحریم رفع انسداد اخراج بازنشانی به کاربر عادی ناظر کردن مدیر کردن - پنهان کردن همه پیام‌ها از این کاربر + چشم‌پوشی نمایش همه پیام‌ها از این کاربر اشاره نمایش فهرست نشست - آیا مطئنید می‌خواهید این کاربر را از این گفتگو مسدود کنید؟ + انسداد کاربر، او را از این اتاق اخراج کرده و از پیوستن دوباره‌اش جلوگیری می‌کند. دلیل تنظیمات پیام‌ها - همه پیام‌ها + تمام پیام‌ها پیام‌ها تنظیمات گذرواژه @@ -738,11 +738,11 @@ تنظیمات گذرواژه جدید - گذرواژه شما بازنشانی شد. + گذرواژه‌تان بازنشانی شد. گذرواژه گذرواژه تنظیمات - گذرواژه حساب + گذرواژهٔ حساب پخش توقف @@ -750,4 +750,840 @@ موفقیت اعلان‌ها + خاتمه + + + اجازهٔ شروع تماس کنفرانسی در این اتاق را ندارید + اجازهٔ شروع تماس در این اتاق را ندارید + کنفرانسی در حال اجراست! + شروع جلسهٔ ویدیویی + شروع جلسهٔ صوتی + نمی‌توانید با خودتان تماس بگیرید + نمی‌توانید با خودتان تماس بگیرید. منتظر پذیرش دعوت شرکت‌کنندگان شوید + شکست در افزودن ابزارک + شکست در برداشتن ابزارک + پذیرش + رد + قطع + + تماس المنت شکست خورد + گزینش افزارهٔ صوتی + تلفن + بلندگو + هدست + هدست بی‌سیم + تعویض دوربین + جلو + پشت + خاموش کردن HD + روشن کردن HD + + این شماره تلفن از پیش تعریف شده. + خطای SSL. + + ۱ عضویت تغییر کرد + %d عضویت تغییر کردند + + + پیش‌گیری از تماس تصادفی + خواستن تأیید پیش از شروع تماس + تماس فعّال (%s) + بازگشت به تماس + + این پیش‌نمایی از اتاق است. برهم‌کنش‌های اتاق از کار افتاده‌اند. + برای انجام این عمل، کارساز هویتی را به تنظیماتتان بیفزایید. + لغو دعوت + چشم‌پوشی از کاربر + نادیده نگرفتن کاربر + لغو دعوت + مطمئنید که می‌خواهید دعوت این کاربر را لغو کنید؟ + اخراج کاربر + دلیل اخراج + اخراج کاربر، از این اتاق برش می‌دارد. +\n +\nبرای پیش‌گیری از پیوستن دوباره، باید تحریمش کنید. + تحریم کاربر + دلیل تحریم + تحریم نکردن کاربر + مطمئنید که می‌خواهید %s را به این گپ دعوت کنید؟ + "%1$s، " + %1$s و %2$s + %1$s %2$s + + با شناسه دعوت شده + مخاطبان محلّی (%d) + شاخهٔ کاربر (%s) + فقط کاربران ماتریکس + + دعوت کاربر با شناسه + لطفاً یک یا چند نشانی رایانامه یا شناسهٔ ماتریکس را وارد کنید + رایانامه یا شناسهٔ ماتریکس + + جست‌وجو + %s دارد می‌نویسد… + %1$s و %2$s دارند می‌نویسند… + %1$s و %2$s و دیگران دارند می‌نویسند… + فرستادن پیام رمزشده… + فرستادن پیام (رمز نشده)… + اتّصال به کارساز از دست رفت. + بازفرستادن همه + لغو همه + بازفرستادن پیام‌های فرستاده‌نشده + حذف پیام‌های فرستاده‌نشده + پرونده پیدا نشد + اجازهٔ فرستادن در این اتاق را ندارید + + ۱ پیام جدید + %d پیام جدید + + + اعتماد کردن + اعتماد نکردن + خروج + چشم‌پوشی + اثرانگشت (%s): + نمی‌توان هویت کارساز دوردست را تأیید کرد. + جست‌وجو + افراد + پرونده‌ها + + پیوستن + شاخه + محبوب‌ها + کم‌اهمیت + دعوت‌ها + شروع گپ + ایجاد اتاق + پیوستن به اتاق + پیوستن به یک اتاق + شناسهٔ اتاق یا نام مستعارش را بنویسید + + مرور شاخه + جست‌وجو کردن شاخه… + + تمام پیام‌ها (پرصدا) + فقط اشاره‌ها + خموش + محبوب + بی‌اولویت + ترک گفت‌وگو + فراموشی + افزودن میان‌بر صفحهٔ خانگی + + اهمّیت آگاهی بر حسب رویداد + + محرمانگی آگاهی + تنظیمات سامانه. + گشودن تنظیمات + + تنظیمات حساب. + به کار انداختن + + تنظیمات نشست. + به کار انداختن + + تنظیمات سفارشی. + به یاد داشته باشید که برخی گونه‌های پیام‌ها روی صامت بودن تنظیم شده‌اند (آگاهی‌ای بدون هیچ صدایی تولید خواهند کرد). + بررسی تنظیمات + + بررسی خدمات پلی + شروع خدمت + + خدمت کشته و به صورت خودکار دوباره شروع شد. + شروع دوبارهٔ خدمت شکست خورد + + شروع هنگام راه‌اندازی + خدمت هنگام شروع دوبارهٔ افزاره، شروع خواهد شد. + به کار اندازی شروع هنگام راه‌اندازی + + بررسی محدودیت‌های پس‌زمینه + محدودیت‌های پس‌زمینه برای المنت از کار افتاده‌اند. این آزمون باید با استفاده از دادهٔ همراه (بدون وای‌فای) اجرا شود. +\n%1$s + محدودیت‌های پس‌زمینه برای النت به کار افتاده‌اند. +\nکارهایی که کاره می‌خواهد انجام دهد، هنگامی که در پس‌زمینه است به صورت تهاجمی محدود می‌شوند که می‌تواند روی آگاهی‌ها تأثیر بگذارد. +\n%1$s + از کار انداختن محدودیت‌ها + + بهینه‌سازی باتری + المنت تحت تأثیر بهینه‌سازی باتری نیست. + چشم‌پوشی از بهینه‌سازی + + عادی + محرمانگی کاهش‌یافته + کاره برای اجرا در پس‌زمینه نیاز به اجازه دارد + کاره در پس‌زمینه، به وصل شدن به کارساز خانگی نیاز ندارد. این کار نصرف باتری را کاهش می‌دهد + صدای آگاهی + گزینش رنگ چراغ، لرزش، صدا و… + + + پیام‌های شامل نام نمایشی + پیام‌های شامل نام کاربری + پیام‌ها در گپ‌های یک‌به‌یک + پیام‌ها در گپ‌های گروهی + هنگام دعوت شدن به یک اتاق + دعوت‌های تماس + پیام‌های ارسالی از بات‌ها + + همگام‌سازی پس‌زمینه + حالت همگام‌سازی پس‌زمینه + بهینه برای باتری + بهینه برای بلادرنگ + بدون همگام‌سازی پس‌زمینه + هنگامی که کاره در پس‌زمینه‌است، از پیام‌های ورودی آگاه نخواهید شد. + شکست در به‌روز رسانی تنظیمات. + + + شروع هنگام راه‌اندازی + به کار انداختن همگام‌سازی پس‌زمینه + پایان زمان درخواست همگام‌سازی + دورهٔ همگام‌سازی ترجیحی + + %d ثانیه + %d ثانیه + + + برای مدیریت بات‌ها، پل‌ها، ابزارک‌ها و بسته‌های برچسب از یک مدیر یکپارچگی استفاده کنید. +\nمدیرهای یکپارچگی، داده‌های پیکربندی را دریافت کرده و می‌توانند از طرف شما ابزارک‌ها را تغییر داده، دعوت‌های اتاق فرستاده و سطوح قدرت را تنظیم کنند. + نمایش برچسب زمانی برای تمامی پیام‌ها + نمایش برچسب‌های زمانی در قالب ۱۲ساعته + شامل رویدادهای دعوت/پیوستن/خروج/اخراج/تحریم و تغییرهای آواتار/نام نمایشی. + پشتیبان امن + مدیریت + برپایی پشتیبان امن + بازنشانی پشتیبان امن + برپایی روی این افزاره + سپرامنیتی در برابر از دست دادن دسترسی به داده‌ها و پیام‌های رمزشده با پشتیبان گیری از کلیدها روی کارسازتان. + غیرفعّال‌سازی حساب + غیرفعّال‌سازی حسابم + کشف + مدیریت تنظیمات کشفتان. + محرمانگی آگاهی + اتّصال پس‌زمینه + نمایش همهٔ پیام‌ها از %s؟ +\n +\nبه خاطر داشته باشید این عمل، کاره را دوباره شروع خواهد کرد و ممکن است کمی زمان ببرد. + گذرواژه‌ها مطابق نیستند + + رایانامه‌ها و شماره تلفن‌ها + مدیریت رایانامه‌ها و شماره تلفن‌های پیوسته به حساب ماتریکستان + + مطمئنید که می‌خواهید این هدف آگاهی را بردارید؟ + + ۳ روز + ۱ هفته + ۱ ماه + برای همیشه + + عکس اتاق + نام اتاق + موضوع + برچسب اتاق + برچسب خورده به عنوان: + + محبوب + اولویت پایین + هیچ + + دسترسی و مشاهده‌پذیری + فهرست کردن این اتاق در شاخهٔ اتاق‌ها + دسترسی اتاق + خوانش تاریخچهٔ اتاق + چه‌کسی می‌تواند تاریخچه را بخواند؟ + چه‌کسی می‌تواندبه این اتاق دسترسی داشته باشد؟ + + هرکسی + فقط اعضا (از زمان گزینش این گزینه) + فقط اعضا (از زمان دعوتشان) + فقط اعضا (از زمان پیوستنشان) + + + %d کاربر مسدود + %d کاربر مسدود + + + این‌ها ویژگی‌های آزمایشی‌ای هستند که ممکن است به روش‌های نامنتظره‌ای حراب شوندا. با احتیاط استفاده کنید. + هرگز پیام‌های رمزشده از این نشست به نشست‌های تأییدنشده در این اتاق فرستاده نشود. + + این اتاق هیچ نشانی محلّی‌ای ندارد + نشانی جدید (مثلاً ‪#foo:matrix.org‬) + + قالب نام مستعار نامعتبر + تظنیم به عنوان نشانی اصلی + شناسهٔ نشست + نام عمومی + نام عمومی + شناسهٔ نشست + کلید نشست + تأییدیه + اثرانگشت Ed25519 + + برون‌ریزی کلید‌های اتاق‌های سرتاسری + برون‌ریزی کلید‌های اتاق‌ها + برون‌ریزی کلیدها به یک پروندهٔ محلّی + برون‌ریزی + بازیابی پیام‌های رمزشده + مدیریت پشتیبان کلید + + درون‌ریزی کلید‌های اتاق‌های سرتاسری + درون‌ریزی کلیدهای اتاق‌ها + درون‌ریزی کلیدها از یک پروندهٔ محلّی + درون‌ریزی + هرگز پیام‌های رمزشده از این نشست به نشست‌های تأییدنشده فرستاده نشود. + تأیید نشده + تأیید‌شده + در فهرست سیاه + + نشست ناشناخته + آی‌پی ناشناخته + هیچ‌کدام + + تأیید + عدم تأیید + بردن در فهرست سیاه + بیرون آوردن از فهرست سیاه + + تأیید نشست + با مقایسهٔ موارد زیر با تنظیمات کاربر در دیگر نشستتان، تأیید کنید: + اگر مطابق نبودند، ممکن است امنیت ارتباطاتتان در معرض خطر باشد. + تأیید می‌کنم که کلیدها مطابقند + + گزینش یک شاخهٔ اتاق + ممکن است کارساز در دسترس نبوده یا شلوغ باشد + این‌جا بنویسید… + + + ۱ پیام آگاهی نخوانده + %d پیام آگاهی نخوانده + + + ۱ پیام آگاهی نخوانده + %d پیام آگاهی نخوانده + + + %1$s: ۱ پیام + %1$s: %2$d پیام + + %1$s در %2$s + رویداد جدید + اتاق + پیام‌های جدید + دعوت جدید + من + **‌شکست در فرستادن - لطفاً اتاق را بگشایید + %1$s: %2$s + %1$s: %2$s %3$s + + اندازهٔ قلم + ریز + کوچک + عادی + بزرگ + بزرگ‌تر + بزرگ‌ترین + سترگ + + برای مدیریت ابزارک‌های این اتاق، نیاز به اجازه دارید + ایجاد ابزارک شکست خورد + ایجاد تماس کنفرانسی با جیتسی + مطمئنید که می‌خواهید ابزارک را از این اتاق حذف کنید؟ + + ۱ ابزارک فعّال + %d ابزارک فعّال + + نما + ابزارک‌های فعّال + + + ابزارک + بار کردن ابزارک + ابزارک افزوده شده به دست: + استفاده از آن ممکن است کوکی تنظیم کرده و داده‌ها را با %s هم‌رسانی کند: + استفاده از آن ممکن است داده‌ها را با %s هم‌رسانی کند: + شکست در بار کردن ابزارک. +\n%s + بار کردن دوبارهٔ ابزارک + گشودن در مرورگر + ابطال دسترسی من + + نام نمایشیتان + نشانی اینترنتی آواتارتان + شناسهٔ کاربریتان + شناسهٔ ابزارک + شناسهٔ اتاق + + + متأسّفانه تماس‌های کنفرانسی با جیتسی روی افزاره‌های قدیمی (افزاره‌هایی با سیستم‌عامل اندروید زیر ۵) پشتیبانی نمی‌شود + این ابزارک می‌خواهد از منابع زیر استفاده کند: + اجازه + انسداد همه + استفاده از دوربین + استفاده از میکروفون + خواندن رسانهٔ محافظت‌شده با DRM + + ناتوان در ایجاد ابزارک. + شکست در فرستادن درخواست. + سطح قدرت باید عدد صحیح مثبت باشد. + در این اتاق نیستید. + اجازهٔ انجامش را در این اتاق ندارید. + بدون room_id در درخواست. + بدون user_id در درخواست. + اتاق %s قابل مشاهده نیست. + هیچ مدیر یکپارچگی‌ای پیکربندی نشده. + مدیریت یکپارچگی‌ها + بدون ابزارک فعّال + استفاده از دوربین بومی + استفاده از دوربین سامانه به جای صفحهٔ دوربین سفارشی. + استفاده از کلید ورود صفحه‌کلید برای فرستادن پیام + فرستادن پیام‌های صوتی + این انتخاب برای ضبط پیام نیاز به یک برنامهٔ سوم‌شخص دارد. + برای ادامه لازم است شرایط خدمت را بپذیرید. + + آغاز تأیید + تأیید + هم‌رسانی بدون تأیید + هم‌رسانی + درخواست هم‌رسانی کلید + چشم‌پوشی از درخواست + چشم‌پوشی + + هشدار! + خاموش + خموش + پرصدا + + ایجاد + مثال + مثال + + خانه + مردم + بدون کاربر + + پیوست + دعوت شد + + ۱ عضو + %d عضو + + + غیرفعّال‌سازی حساب + غیرفعّال‌سازی حساب + + افزایش کارایی با فقط بار کردن اعضای اتاق در نمای نخست. + کارساز خانگیتان هنوز از بار کردن تنبلانهٔ اعضای اتاق پشتیبانی نمی‌کند. بعداً تلاش کنید. + + هرگز پیام‌های رمزشده را از دست ندهید + شروع با استفاده از پشتیبان کلید + برون‌ریزی دستی کلیدها + + امن کردن پشتیبانتان با یک عبارت عبور. + تنظیم عبارت عبور + ایجاد کردن پشتیبان + موفّق! + انجام شد + رونوشت گرفتم + ذخیرهٔ کلید بازیابی + هم‌رسانی + ذخیره به شکل پرونده + جایگزینی + توقّف + + لطفاً رونوشتی بردارید + هم‌رسانی کلید بازیابی با… + کلید بازیابی + خطای نامنتظره + پشتیبان‌گیری آغاز شد + استفاده از کلید بازیابیتان + ورود کلید بازیابی + + بازیابی پیام + + محاسبهٔ کلید بازیابی… + لطفاً‌یک کلید بازیابی وارد کنید + من بودم + + هرگز پیام‌های رمزشده را از دست ندهید + شروع با استفاده از پشتیبان کلید + + پشتیبان امن + سپرامنیتی در برابر از دست دادن دسترسی به داده‌ها و پیام‌های رمزشده + + هرگز پیام‌های رمزشده را از دست ندهید + استفاده از پشتیبان کلید + + برپایی پشتیبان امن + + پاسخ کشف کارساز خانگی نامعتبر + نمایش پیام‌های برداشته + نمایش یک جانگه‌دار برای پیام‌های برداشته‌شده + ویرایش‌های پیام‌ها + هیچ ویرایشی پیدا نشد + + نمی‌توانید کسی که به دنبالش هستید را بیابید؟ + مشاهدهٔ شاخهٔ اتاق + + افزودن زبانه‌ای اختصاصی روی صفحهٔ اصلی برای آگاهی‌های خوانده‌نشده. + + پیوند در تخته‌گیره رونشت شد + + ایجاد کردن اتاق… + برغای گرفتن نتایج، شروع به نوشتن کنید + پیوستن به اتاق… + + مشاهدهٔ تاریخچهٔ ویرایش + + شرایط خدمت + بازبینی شرایط + قابل‌مشاهده بودن برای دیگران + استفاده از بات‌ها،پل‌ها، ابزارک‌ها و بسته‌های برچسب + + کارساز هویت + قطع از کارساز هویت + پیکربندی کارساز هویت + تغییر کارساز هویت + نشانی‌های رایانامهٔ قابل‌کشف + گزینه‌های کشف به محض افزودن یک رایانامه، ظاهر خواهند شد. + گزینه‌های کشف به محض افزودن یک شماره تلفن، ظاهر خواهند شد. + شماره تلفن‌های قابل‌کشف + منتظر + + به کار انداختن گزارش‌های پرگو. + گزارش‌های پرگو با فراهم کردن گزارش‌های بیش‌تر هنگام تکان دادن محکم گوشی، به توسعه‌دهندگان کمک می‌کند. حتا هنگام به کار افتاده بودن هم برنامه، محتوای پیام یا هیچ دادهٔ خصوصی دیگری را گزارش نمی‌کند. + + + پرونده + مخاطب + دوربین + صدا + جُنگ + برچسب + رسانه + هیچ رسانه‌ای در این اتاق نیست + پرونده‌ها + %1$s در %2$s + هیچ پرونده‌ای در این اتاق نیست + + تمام پیام‌ها (پرصدا) + تمام پیام‌ها + فقط اشاره‌ها + خموش + افزودن به محبوب‌ها + برداشتن از محبوب‌ها + ترک اتاق + %1$s تغییری ایجاد نکرد + تغییری ایجاد نکردید + برای یافتن واکنش، کلیدواژگان را بنویسید. + + از هیچ کاربری چشم نمی‌پوشید + + میزبانی حرفه‌ای برای سازمان‌ها + بیش‌تر بدانید + دیگر + ادامه + وصل شدن به %1$s + وصل شدن به حدمات ماتریکس المنت + وصل شدن به یک کارساز سفارشی + ورود به %1$s + نام‌نویسی + ورود + ادامه با ورود یکپارچه + + نشانی خدمات ماتریکس المنت + نشانی + میزبانی حرفه‌ای برای سازمان‌ها + رایانامه + هشدار! + ادامه + + موفق! + بازگشت به ورود + + هشدار + تنظیم نشانی رایانامه + رایانامه + رایانامه (اختیاری) + بعدی + + تنظیم شماره تلفن + لطفاً از قالب بین‌المللی استفاده کنید. + شماره تلفن + شماره تلفن (اختیاری) + بعدی + + تأیید شماره تلفن + ورود رمز + فرستادن دوباره + بعدی + + لطفاً از قالب بین‌المللی استفاده کنید (شماره تلفن باید با + شروع شود) + شماره تلفن‌های بین‌المللی باید با + شروع شوند + نام کاربری + بعدی + نام کاربری گرفته شده + هشدار + ورود با شناسهٔ ماتریکس + ورود با شناسهٔ ماتریکس + شناسهٔ ماتریکس + دیده شده به دست + + خارج شده‌اید + ورود دوباره + + خارج شده‌اید + ورود + ورود + پاک‌سازی داده‌های شخصی + پاک‌سازی تمامی داده‌ها + + پاک‌سازی داده + پاک‌سازی داده + شرح بیش از حد کوتاه است + + همگام‌سازی نخستین… + + حالت توسعه‌دهنده ویژگی‌های پنهان را فعّال کرده و همچنین ممکن است پایداری برنامه را کاهش دهد. فقط برای توسعه‌دهنگان! + آستانهٔ تشخیص + برای آزمودن آستانهٔ تشخیص، تلفنتان را تکان دهید + نشست جاری + فقط نمایش نخستین نتایج. حرف‌های بیش‌تری بنویسید… + + شکست سریع + المنت ممکن است هنگام رخ دادن خطای نامنتظره،‌بیش‌تر فروبپاشد + + به کار انداختن رمزنگاری + پس از به کار افتادن، رمزنگاری قابل از کار انداختن نیست. + + ورود نامطمئن + مطابقند + مطابق نیستند + ناامن + ویدیو. + تصویر. + صدا + پرونده + برچسب + نظرسنجی + دکمه‌های بات + واکنش داده با: %s + نتیجه‌گیری تأیید + + در حال انتظار… + %s لغو شد + لغو کردید + %s پذیرفت + پذیرفتید + تأییدیه فرستاده شد + درخواست تأییدیه + تأیید این نشست + تأیید دستی + + شما + + برای تأیید امن یکدیگر* رمز را با افزارهٔ کاربر دیگر بپویید + رمزش را بپویید + ناتوانی در پویش + اگر کنار هم نیستید، به‌جایش اموجی‌ها را مقایسه کنید + + تأیید با مقایسهٔ اموجی‌ها + + تأیید با اموجی + اگر نمی‌توانید رمز بالا را بپویید، با مقایسهٔ گزیده‌ای خاص و کوتاه از اموجی‌ها، تأیید کنید. + + تصویر رمز QR + + تأیید %s + %s تأیید شد + منتظر %s… + پیام‌های این اتاق، رمزنگاری سرتاسری نشده‌اند. + پیام‌های این اتاق، رمزنگاری سرتاسری شده‌اند. +\n +\nپیام‌هایتان با قفل‌هایی امن شده‌اند و فقط شما و گیرندگان دیگر، کلیدهای یکتا را برای قفل‌گشاییشان دارید. + امنیت + بثیش‌تر بدانید + بیش‌تر + کنش‌های مدیر + تنظمیات اتاق + + ۱ نفر + %1$d نفر + + بارگذاری‌ها + ترک اتاق + ترک کردن اتاق… + + مدیران + ناظم‌ها + سفارشی + دعوت‌ها + کاربران + + مدیر در %1$s + ناظم در %1$s + پیش گزیده در %1$s + سفارشی (%1$d) در %2$s + + پرش به رسیدِ خواندن + + چشم‌ناپوشی + + پس از به کار افتادن، رمزنگاری قابل از کار انداختن نیست. + + به کار انداختن رمزنگاری + + ورود چندگانه + ورود چندگانه به کار افتاده +\nکلیدهای خصوصی روی دستگاه. + ورود چندگانه به کار افتاده +\nکلیدها مورد اعتمادند. +\nکلیدهای خصوصی ناشناخته + ورود چندگانه به کار افتاده. +\nکلیدها مورد اعتماد نیستند + ورود چندگانه به کار نیفتاده + + مدیر کارسازتان رمزنگاری سرتاسری پیش‌گزیده را در اتاق‌های خصوصی و پیام‌های مستقیم از کار انداخته است. + خروج از این نشست + + تأیید این ورود + ممکن است دیگر کاربران، به آن اعتماد نکنند + تکمیل امنیت + + استفاده از نشستی موجود برای تأییدش که به پیام‌های رمزشده دسترسی می‌دهد. + + + تأیید + تأیید‌شده + هشدار + + مورد اعتماد + بدون اعتماد + + بازنشانی کلیدها + + رمز QR + + تقریباً تمام شد! آیا %s همین سپر را نشان می‌دهد؟ + بله + خیر + + اتّصال به کارساز از دست رفت + حالت هواپیما روشن است + + داده‌های حساب + + %d رأی + %d رأی + + + %d رأی - نتایج نهایی + %d رأی - نتایج نهایی + + انتخاب گزیده + ایجاد نظرسنجی‌ای ساده + استفاده از یک کلید یا عبارت بازیابی + اگر به نشست‌های موجود دسترسی ندارید + + ورود جدید + + هشدار: + برداشتن… + می‌خواهید این پیوست را به %1$s بفرستید؟ + + فرستادن تصویر با اندازهٔ اصلی + فرستادن تصویرها با اندازهٔ اصلی + + + تأیید برداشت + آوردن دلیل + المنت اندروید + + درخواست‌های کلید + + قفل‌گشایی از تاریخچهٔ پیام‌های رمزشده + + نوسازی + + ورود جدید. خودتان بودید؟ + برای بازبینی و تأیید بزنید + من نبودم + تأیید افزاره‌هایتان از تنظیمات. + تأیید لغو شد + + عبارت عبور بازیابی + کلید پیام + تنظیم یک %s + تولید یک کلید پیام + + تأیید %s + + برای ادامه %s تان را وارد کنید. + + برپایی بازیابی. + کلید بازیابیتان + رفع‌اشکال + تنظیم اهمّیت آگاهی بر حسب رویداد + + پیام… + + ارتقای رمزنگاری موجود است + استفاده از پرونده + + عبارت عبور بازیابی + این یک کلید بازیابی معتبر نیست + لطفاً‌یک کلید بازیابی وارد کنید + + بررسی کردن کلید پشتیبان + بررسی کردن کلید پشتیبان (%s) + %1$s (%2$s) + + برای ادامه،‌عبارت عبور پشتیبان کلیدتان را وارد کنید. + استفاده از کلید بازیابی پشتیبان کلیدتان + کلید بازیابی پشتیبان کلید + + جلوگیری از نماگرفت کاره + به کار انداختن این انتخاب، FLAG_SECURE را به تمامی فعّالیت‌ها می‌افزاید. برای اثرگذاری تغییر، برنامه را دوباره شروع کنید. + + پروندهٔ رسانه‌ای به جُنگ افزوده شد + المنت وب +\nالمنت میزکار + المنت آی‌اواس +\nالمنت اندروید + استفاده از کلید بازیابی + رمزشده به دست افزاره‌ای تأییدنشده + لطفاً نام کاربری‌ای برگزینید. + لطفاً گذرواژه‌ای برگزینید. + این پیوند را دوباره بررسی کنید + پیوند %1$s به پایگاه دیگری می‌بردتان: %2$s. +\n +\nمطمئنید که می‌خواهید ادامه دهید؟ + + افزودن اعضا + دعوت + دعوت کردن کاربران… + دعوت کاربران + دعوت برای %1$s فرستاده شد + دعوت برای %1$s و %2$s فرستاده شد + + دعوت برای %1$s و یک نفر دیگر فرستاده شد + دعوت برای %1$s و %2$d نفر دیگر فرستاده شد + + زبان جاری + دیگر زبان‌های موجود + بار کردن زبان‌های موچود… + + گشودن شرایط %s + توقّف دوربین + شروع دوربین + + برپایی پشتیبان امن + + پشتیبان امن + سپرامنیتی در برابر از دست دادن دسترسی به داده‌ها و پیام‌های رمزشده با پشتیبان گیری از کلیدها روی کارسازتان. + نام اتاق + موضوع + گرفتم + اطّلاعات بیش‌تر + + المنت + + diff --git a/vector/src/main/res/values-fi/strings.xml b/vector/src/main/res/values-fi/strings.xml index 07a5d3f84a..3a2e76e8da 100644 --- a/vector/src/main/res/values-fi/strings.xml +++ b/vector/src/main/res/values-fi/strings.xml @@ -1,17 +1,15 @@ - + fi FI - Viestit Huone Asetukset Jäsenen tiedot Historiallinen - OK Peruuta @@ -19,7 +17,8 @@ Poistu Lähetä Lähetä uudelleen - Poista + Poista + Lainaa Jaa Myöhemmin @@ -27,7 +26,6 @@ Pysyvä linkki Lähdekoodi Näytä salaamaton lähde - Poista @@ -47,7 +45,6 @@ Lähetä silti tai Kutsu - Kirjaudu ulos Äänipuhelu @@ -60,27 +57,22 @@ Sulje Kopioitu leikepöydälle Poista käytöstä - Vahvistus Varoitus - Koti Suosikit Ihmiset Huoneet - Suodata huoneista Suodata suosikeista Suodata henkilöistä Suodata huoneista - Kutsut Matala prioriteetti - Keskustelut Paikalliset yhteystiedot @@ -88,17 +80,15 @@ Ei keskusteluita Et ole sallinut Elementille pääsyä paikallisiin yhteystietoihisi Ei tuloksia - Huoneet Huoneluettelo Ei huoneita Ei julkisia huoneita saatavilla - 1 käyttäjä + yksi käyttäjä %d käyttäjää - Lähetä lokit Lähetä kaatumislokit Lähetä näytönkaappauskuva @@ -111,31 +101,25 @@ Virheraporttia ei voitu lähettää (%s) Lähetetään (%s%%) Sovellus kaatui viime kerralla. Haluatko tehdä kaatumisesta virheilmoituksen\? - Lähetä Luettu - Liity huoneeseen Käyttäjätunnus Luo tili Kirjaudu sisään Kirjaudu ulos Kotipalvelimen URL-osoite - Identiteettipalvelimen URL-osoite - Etsi - + Identiteettipalvelimen URL-osoite + + Etsi - Aloita uusi keskustelu Aloita puhelu Aloita videopuhelu - - Lähetä tiedostoja Ota kuva tai video - Kirjaudu sisään Luo tili @@ -172,15 +156,16 @@ Varmista ettet ole robotti Käyttäjätunnus on jo käytössä Kotipalvelin: - Identiteettipalvelin: + Identiteettipalvelin: + Olen varmistanut sähköpostiosoitteeni Palauttaaksesi salasanasi, anna tiliisi liitetty sähköpostiosoite: Anna tiliisi liitetty sähköpostiosoite. Anna uusi salasana. - Osoitteeseen %s on lähetetty sähköposti. Kun olet avannut siinä olevan linkin, paina alla olevaa nappia. + Osoitteeseen %s on lähetetty sähköposti. Kun olet avannut siinä olevan linkin, paina alla olevaa nappia. + Sähköpostiosoitteesi vahvistaminen epäonnistui. Varmista, että klikkasit sähköpostissa olevaa linkkiä Salasanasi on vaihdettu.\n\nSinut on kirjauduttu ulos kaikista laitteistasi, etkä enää saa viesti-ilmoituksia. Ottaaksesi käyttöön ilmoitukset uudelleen, kirjaudu sisään uudelleen kaikilla laitteillasi. - URL-osoitteen on alettava seuraavasti: http[s]:// Kirjautuminen epäonnistui: Verkkovirhe @@ -190,43 +175,35 @@ Rekisteröityminen epäonnistui: sähköpostin varmistaminen epäonnistui Syötäthän toimivan osoitteen - Väärä käyttäjätunnus tai salasana Annettua tunnistetta ei hyväksytty Epämuotoinen JSON Ei sisällä kelvollista JSON:ia Liian monta pyyntöä Käyttäjänimi on jo käytössä - Sähköpostiisi lähetetty linkki, jota ei ole vielä klikattu - + Sähköpostiisi lähetetty linkki, jota ei ole vielä klikattu + - Lukukuittaukset - - Valitse koko Alkuperäinen Iso Keskikokoinen Pieni - Peru lataus? Peru lähetys? %d s %1$d min %2$d s - Eilen Tänään - Huoneen nimi Huoneen aihe - Yhdistetty Yhdistetään… @@ -236,19 +213,17 @@ Saapuva videopuhelu Saapuva puhelu Puhelu käynnissä… - - Toinen puoli ei vastannut. + Toinen puoli ei vastannut. + Mediayhteys epäonnistui Kameran alustus epäonnistui puheluun vastattiin muualta - - Ota kuva tai video" - Videointi epäonnistui - + Videointi epäonnistui + Huomio Element tarvitsee käyttöluvan mediagalleriaasi lähettäkseen liitteitä.\n\nSalli tiedostojen käyttö seuraavalla näytöllä liittääksesi kuvia ja muita tiedostoja viesteihin. @@ -264,55 +239,47 @@ \n \nSalli mikrofonin ja kameran käyttö seuraavilla näytöillä aloittaaksesi tämän puhelun. Element voi tarkistaa yhteystietosi löytääkseen muita Matrixin käyttäjiä sähköpostiosoitteiden ja puhelinnumeroiden perusteella. Jos suostut jakamaan yhteystietosi tätä tarkoitusta varten, salli yhteystietojen käyttö seuraavassa ponnahdusikkunassa. - Element voi tarkistaa yhteystietosi löytääkseen muita Matrixin käyttäjiä sähköpostiosoitteiden sekä puhelinnumeroiden perusteella. -\nAnnatko Elementin käyttää yhteystietojasi tätä varten\? - + Element pystyy käyttämään yhteystietojasi, etsiäkseen muita Matrix-käyttäjiä sähköpostiosoitteiden sekä puhelinnumeroiden perusteella. +\n +\nSaako Element käyttää yhteystietojasi tätä varten\? Toimenpide epäonnistui puuttuvien käyttölupien takia - Tallennettu Tallenna latauskansioon\? KYLLÄ EI Jatka - Poista Liity Esikatsele Hylkää - Siirry ensimmäiseen lukemattomaan viestiin. - %s on kutsunut sinut huoneeseen Tämä kutsu lähetettiin osoitteeseen %s, jota ei ole liitetty tiliisi. \nVoit kirjautua sisään toisella tilillä tai lisätä tämän sähköpostiosoitteen tiliisi. Olet avaamassa huonetta %s. Haluatko liittyä huoneeseen osallistuaksesi keskusteluun? - huone + huone + Tämä on huoneen esikatselu. Liity huoneeseen osallistuaksesi keskusteluun. - Uusi keskustelu Lisää jäsen yksi jäsen - Poistu huoneesta Haluatko varmasti poistua huoneesta? Haluatko varmasti poistaa käyttäjän %s tästä keskustelusta\? Luo - kirjautuneena sisään saavuttamattomissa toimettomana - YLLÄPITÄJÄN TYÖKALUT PUHELUT YKSITYISKESKUSTELUT ISTUNNOT - Kutsu Poistu huoneesta Poista huoneesta @@ -327,23 +294,20 @@ Mainitse Näytä istuntolista Olet ylentämässä käyttäjää samalle tasolle kuin oma käyttäjätasosi. Et voi perua tätä toimintoa.\nOletko varma? - Haluatko kutsua käyttäjän %s tähän keskusteluun\? - Kutsu tunnisteella PAIKALLISET YHTEYSTIEDOT (%d) Vain Matrix-käyttäjät - Kutsu käyttäjä tunnisteella Syötä yksi tai useampi sähköpostiosoite tai Matrix-tunniste Sähköposti tai Matrix-tunniste - Etsi %s kirjoittaa… %1$s ja %2$s kirjoittavat… - %1$s, %2$s ja muita kirjoittaa… + %1$s, %2$s ja muita kirjoittaa… + Lähetä salattu viesti… Lähetä viesti (salaamaton)… Yhteys palvelimeen katkesi. @@ -355,30 +319,28 @@ Poista lähettämättömät viestit Tiedostoa ei löydy Sinulla ei ole oikeutta lähettää viestejä tähän huoneeseen - Luota Älä luota Kirjaudu ulos - Jätä huomiotta + Jätä huomiotta + Sormenjälki (%s): - Palvelimen identiteettiä ei voitu vahvistaa. + Palvelimen identiteettiä ei voitu vahvistaa. + Tämä voi tarkoittaa että joku yrittää kaapata sinun viestintääsi tai että laitteesi ei luota palvelimen varmenteeseen. Jos palvelimen ylläpitäjä on ilmoittanut, että tämä on odotettua, varmista että alla oleva sormenjälki on sama kuin hänen antamansa. Sertifikaatti johon laitteesi luotti aikaisemmin on vaihtunut. Tämä on HYVIN EPÄTAVALLISTA. On suositeltavaa, että ET hyväksy tätä uutta sertifikaattia. Sertifikaatti on vaihtunut ennestään luotetusta ei-luotettuun. Palvelin on voinut uusia sertifikaattinsa. Kysy palvelimen ylläpitäjältä, mikä sormenjäljen pitäisi olla. Hyväksy sertifikaatti vain, jos palvelimen ylläpitäjä on julkaissut sormenjäljen, joka täsmää yllä olevan kanssa. - Huoneen tiedot Henkilöt Tiedostot Asetukset - Epämuotoinen tunnus. Anna sähköpostiosoite tai Matrix-tunnus (esim. \'@tunnus:verkkotunnus\') KUTSUTUT JÄSENET - Syy sisällön ilmoittamiseen Haluatko piilottaa kaikki tämän käyttäjän viestit\? @@ -386,7 +348,6 @@ \nHuomaa, että tämä toiminto käynnistää sovelluksen uudelleen ja siinä saattaa kestää jonkin aikaa. Peru lähetys Peru lataus - Etsi Etsi huoneen jäsenistä @@ -395,31 +356,28 @@ VIESTIT HENKILÖT TIEDOSTOT - LIITY LUETTELO SUOSIKIT HUONEET - MATALA TÄRKEYS + MATALA TÄRKEYS + KUTSUT Aloita keskustelu Luo huone Liity huoneeseen Liity huoneeseen Syötä huonetunniste tai -alias - Selaa luetteloa Haetaan luettelosta… - Suosikki Matala tärkeys Yksityiskeskustelu Poistu keskustelusta Unohda - Viestit Asetukset @@ -428,9 +386,7 @@ Kolmannen osapuolen tiedot Tekijänoikeustiedot Tietosuojakäytäntö - - Profiilikuva Nimi Sähköposti @@ -439,22 +395,18 @@ Lisää puhelinnumero Näytä sovelluksen tiedot järjestelmän asetuksissa. Sovelluksen tiedot - Ota ilmoitukset käyttöön tällä tilillä Ota ilmoitukset käyttöön tässä istunnossa Näyttö päälle kolmeksi sekunniksi - Viestit yksityiskeskusteluissa Viestit ryhmäkeskusteluissa Kun minut kutsutaan huoneeseen Saapuvat puhelut Bottien lähettämät viestit - Taustasynkronointi Käytä taustasynkronointia Synkronointipyynnön aikakatkaisu Viive synkronointien välillä - Versio olm-versio Käyttöehdot @@ -463,8 +415,6 @@ Tietosuojakäytäntö Tyhjennä välimuisti - - Käyttäjäasetukset Ilmoitukset Piilotetut henkilöt @@ -490,19 +440,16 @@ Tunnistautuminen Salasana: Lähetä - Kirjautuneena nimellä Kotipalvelin - Identiteettipalvelin - + Identiteettipalvelin + Odotetaan vahvistusta Tarkista sähköpostisi ja klikkaa sinne saamaasi linkkiä. Kun olet tehnyt tämän, paina jatka. Sähköpostin vahvistaminen epäonnistui. Tarkista sähköpostisi ja avaa lähettämässämme viestissä oleva linkki. Tämän jälkeen paina painiketta ”jatka”. - Tämä sähköpostiosoite on jo käytössä. Tätä sähköpostiosoitetta ei löytynyt. Tämä puhelinnumero on jo käytössä. - Vaihda salasana Nykyinen salasana Uusi salasana @@ -512,13 +459,10 @@ Näytä kaikki viestit käyttäjältä %s\? \n \nHuomaa, että tämä toiminto käynnistää sovelluksen uudelleen ja siinä saattaa kestää jonkin aikaa. - Haluatko poistaa tämän ilmoituskohteen? - - Are you sure you want to remove the %1$s %2$s? - + Haluatko varmasti poistaa kohteen %1$s %2$s\? + Valitse maa - Maa Valitse maa Puhelinnumero @@ -528,21 +472,17 @@ Anna aktivointikoodi Puhelinnumeron validointi epäonnistui Aktivointikoodi - - Huoneen kuva Huoneen nimi Aihe Huoneen luokittelu Luokiteltu: - Suosikki Matala tärkeys Ei mikään - Näkyvyys ja pääsy Listaa tämä huone huoneluettelossa @@ -550,22 +490,18 @@ Huoneen historian luettavuus Kuka voi lukea huoneen historiaa\? Kuka pääsee tähän huoneeseen\? - Kuka tahansa Vain jäsenet (tämän asetuksen valitsemisesta alkaen) Vain jäsenet (heidän kutsumisestaan alkaen) Vain jäsenet (heidän liittymisestään alkaen) - Linkittääksesi huoneeseen, sillä pitää olla osoite. Vain kutsutut henkilöt Kaikki, jotka tietävät huoneen osoitteen (paitsi vieraat) Kaikki jotka tietävät huoneen osoitteen, mukaanlukien vieraat - Porttikiellon saaneet käyttäjät - Lisäasetukset Tämän huoneen sisäinen ID @@ -577,35 +513,29 @@ Kirjaudu ulos salauksen aktivoimiseksi. Lähetä salatut viestit vain vahvistetuille laitteille Älä lähetä tältä laitteelta salattuja viestejä tämän huoneen vahvistamattomille laitteille. - Huoneella ei ole paikallisia osoitteita Uusi osoite (esim. #foo:matrix.org") - Virheellinen aliaksen muoto \'%s\' ei ole kelvollinen muoto aliakselle Tälle huoneelle ei ole määritetty pääosoitetta. Pääosoitevaroitus - Aseta pääosoitteeksi Poista pääosoite Kopioi huoneen tunniste Kopioi huoneen osoite - Tämä huone on salattu. Tämä huone ei käytä salausta. Ota salaus käyttöön \n(varoitus: salausta ei voi poistaa käytöstä!) - Luettelo - - %s yritti ladata tietyn kohdan huoneen historiassa, mutta sitä ei löytynyt. - + %s yritti ladata tietyn kohdan huoneen historiassa, mutta sitä ei löytynyt. + - Päästä päähän -salauksen lisätiedot - + Päästä päähän -salauksen lisätiedot + Tapahtuman tiedot Käyttäjän Matrix-ID Curve25519-ID-avain @@ -613,15 +543,13 @@ Algoritmi Sessio-ID Salauksenpurkuvirhe - Lähettäjän istunnon tiedot Julkinen nimi Julkinen nimi - Tunnus + Istunnon tunnus Istunnon avain Vahvistus Ed25519-sormenjälki - Vie salatun huoneen avaimet Vie huoneen avaimet Vie avaimet paikalliseen tiedostoon @@ -631,36 +559,29 @@ Huoneen osapuolten välisen salauksen avaimet tallennettiin tiedostoon \'%s\' \n \nVaroitus: tämä tiedosto saatetaan poistaa, mikäli Element poistetaan. - Tuo päästä päähän -salatun huoneen avaimet Tuo huoneen avaimet Tuo avaimet paikallisesta tiedostosta Tuo Lähetä salatut viestit vain vahvistetuille laitteille Älä lähetä salattuja viestejä vahvistamattomille laitteille tästä laitteesta. - Vahvistamaton Vahvistettu Kielletty - tuntematon istunto ei mitään - Vahvista Poista vahvistus Kiellä Poista kielto - Vahvista laite - Vahvistaaksesi, että tähän laitteeseen voi luottaa, ota yhteyttä sen omistajaan jollain muulla tavalla (esimerkiksi soittamalla tai tapaamalla) ja varmista että hänen laitteensa avain on sama kuin alla oleva: - Jos avain täsmää, paina painiketta \"vahvista\". Jos avain ei täsmää, se tarkoittaa, että jokin tuntematon osapuoli lukee keskustelujanne. Tässä tapauksessa paina painiketta \"kiellä\". -\nTulevaisuudessa tämä varmennusprosessi tulee olemaan hienostuneempi. + Vahvistaaksesi, että tähän laitteeseen voi luottaa, ota yhteyttä sen omistajaan jollain muulla tavalla (esimerkiksi soittamalla tai tapaamalla) ja varmista että hänen laitteensa avain on sama kuin alla oleva: + + Jos avaimet eivät täsmää, keskustelusi eivät luultavasti ole turvassa. Vahvistan, että avaimet täsmäävät - Huoneessa on tuntemattomia istuntoja Huoneessa on tuntemattomia laitteita joita ei ole vahvistettu.\nLaitteet eivät välttämättä kuulu väitetyille omistajilleen.\nJokainen uusi laite kannattaa vahvistaa ennen kuin jatkat, mutta voit myös lähettää viestit vahvistamattomille laitteille.\n\nTuntemattomat laitteet: - Valitse huoneluettelo Palvelin saattaa olla tavoittamattomissa tai ylikuormitettu @@ -668,7 +589,6 @@ Kotipalvelimen URL-osoite Kaikki huoneet palvelimella %s Kaikki alkuperäiset %s huoneet - Etsi historiasta Käyttäjäluettelo @@ -676,21 +596,16 @@ Käynnistä automaattisesti Tyhjennä mediavälimuisti Säilytä media - Näytä aikaleimat kaikille viesteille Datansäästötila - Käyttöliittymä Kieli Valitse kieli - 3 päivää 1 viikko 1 kuukausi Ikuisesti - Teema - Kirjaisinkoko Erittäin pieni Pieni @@ -700,36 +615,28 @@ Isoin Valtava Offline - Vaalea teema Tumma teema Musta teema - Synkronoidaan… Kuunnellaan tapahtumia Korostetut ilmoitukset Hiljaiset ilmoitukset - Virheraportti - Ota kuva Kaappaa video - Puhelu Ilmoitusääni Viesti sisältää näyttönimeni Viesti sisältää käyttäjänimeni Näytä aikaleimat 12 tunnin muodossa - Analytiikka - Tarvitse oikeudet pienoissovellusten hallintaan tässä huoneessa Pienoissovelluksen luonti epäonnistui Luo konferenssipuheluita jitsin avulla Haluatko varmasti poistaa pienoissovelluksen tästä huoneesta\? - - Pienoissovellusta ei voitu luoda. + Sovelmaa ei voitu luoda. Pyynnön lähetys epäonnistui. Oikeustason täytyy olla positiivinen luku. Et ole tässä huoneessa. @@ -739,87 +646,64 @@ Huone %s ei ole näkyvillä. Lisää Matrix-sovelluksia Käytä järjestelmän kamerasovellusta - Lisäsit uuden istunnon \'%s\', joka pyytää salausavaimia. Vahvistamaton laitteesi \'%s\' pyytää salausavaimia. Aloita varmennus Jaa ilman varmennusta Hylkää pyyntö - Varoitus! Konferenssipuhelut ovat kehitysvaiheessa eivätkä välttämättä luotettavia. - Komentovirhe Tuntematon komento: %s - Off Äänekäs - Salattu viesti - Yhteisön tiedot - Ladataan… - Poistu Yhteisöt - Suodata yhteisöistä - Kutsu Yhteisöt Ei ryhmiä - Haluatko varmasti aloittaa uuden keskustelun käyttäjän %s kanssa\? Haluatko varmasti aloittaa äänipuhelun\? Haluatko varmasti aloittaa videopuhelun\? - Ryhmälistaus - - Haluatko varmasti antaa tälle käyttäjälle porttikiellon tähän keskusteluun\? - + Käyttäjän estäminen poistaa hänet tästä huoneesta ja estää häntä liittymästä huoneeseen uudelleen. Kaikki viestit (äänekäs) Kaikki viestit Vain maininnat Vaimenna - Lisää pikakuvake aloitusruudulle - + Lisää aloitusruudulle Osoitteen esikatselu Värise, kun käyttäjä mainitaan - Tyyli - Ilmoitukset Uusi yhteisötunnus (esim. +foo:matrix.org) Virheellinen yhteisötunnus \'%s\' ei ole sallittu yhteisötunnus - - Luo Luo yhteisö Yhteisön nimi Esimerkki Yhteisön ID esimerkki - Koti Ihmiset Huoneet Ei käyttäjiä - Huoneet Jäsenet Kutsuttu Rajaa jäsenlistaa Rajaa huonelistaa - Yhteisön pääkäyttäjä ei ole antanut pitkää kuvausta yhteisölle. - Sinut poistettiin %1$s käyttäjän %2$s toimesta Sinut estettiin %1$s käyttäjän %2$s toimesta Syy: %1$s @@ -829,53 +713,41 @@ Markdown-muotoilu Markdown on käytössä. Markdown on pois käytöstä. - Puhelut Valitse soittoääni puheluille: - Versio %s Salasana Kirjoita salasanasi jatkaaksesi: Kirjoita salasanasi. Status.im-teema - Lähetä kirjoitusilmoitukset Näytä lukukuittaukset Näytä liittymiset ja poistumiset Näytä tilin tapahtumat Esikatsele media ennen lähettämistä - Lähetä ääniviestejä Lähetä analytiikkatietoja Ravista raivokkaasti ilmoittaaksesi ongelmasta - Deaktivoi tili Deaktivoi tilini - Deaktivoi tili Deaktivoi tili - Lähetä tarra Oletko varma? Kolmannen osapuolen lisenssit - Lataa Hyväksy Ohita Oletko varma, että haluat kirjautua ulos? Virhe - Jos mahdollista, kirjoita kuvaus englanniksi. Lähetä tarra Sinulla ei ole yhtään tarrapakettia käytössä. - -Haluatko lisätä paketteja? - +\n +\nHaluatko lisätä paketteja\? Lue ja hyväksy tämän kotipalvelimen käytännöt: - Avainten varmuuskopiointi Käytä avainten varmuuskopiointia - Avainten varmuuskopio ei ole valmis, odotathan hetken… Menetät salatut viestisi, jos kirjaudut ulos nyt Avainten varmuuskopio on meneillään. Jos kirjaudut ulos, menetät pääsyn salattuihin viesteihisi. @@ -885,7 +757,6 @@ Haluatko lisätä paketteja? Käytä avainten varmuuskopiointia Varmuuskopioi Menetät pääsyn salattuihin viesteihisi, ellet varmuuskopioi avaimiasi ennen uloskirjautumista. - Pysy Puhu Tyhjennä @@ -893,31 +764,22 @@ Haluatko lisätä paketteja? Soita silti Valmis Keskeytä - Toiminnot Järjestelmähälytykset - Lähetä ääntä - jatka sovelluksella… Yhtään ulkopuolista sovellusta tämän toiminnon suorittamiseksi ei löytynyt. - Pyydä salausavaimia uudelleen muista istunnoistasi. - Avainpyyntö lähetetty. - Pyyntö lähetetty Käynnistä Element toisella laitteella, joka voi purkaa viestin, jotta se voi lähettää avaimet tähän istuntoon. - yksi jäsenyysmuutos %d jäsenyysmuutosta - Käytä Elementin oletussoittoääntä saapuville puheluille Saapuvien puheluiden soittoääni Videopuhelu menossa… - Käyttäjälista Avaa otsikko Synkronoidaan… @@ -945,24 +807,19 @@ Haluatko lisätä paketteja? 1 pv %d pv - Nyt %1$s Oli %1$s %2$s sitten - Poista huoneesta Syy - "%1$s, " %1$s ja %2$s %1$s %2$s - Lähetä salattu vastaus… Lähetä vastaus (salaamaton)… yksi uusi viesti %d uutta viestiä - yksi valittu %d valittu @@ -977,7 +834,6 @@ Haluatko lisätä paketteja? Yksityiskohtaiset ilmoitusasetukset Ilmoituksen tärkeys kohteittain - Ilmoitusten yksityisyys Ratkaise ilmoituksien ongelmia Vianmääritys @@ -986,70 +842,59 @@ Haluatko lisätä paketteja? Vianmäärityksessä ei löytynyt ongelmia. Jos et edelleenkään saa ilmoituksia, lähetä virheraportti, jotta meidän on helpompi tutkia ongelmaa. Yksi tai useampi testi epäonnistui, kokeile ehdotettuja korjauksia. Yksi tai useampi testi epäonnistui. Lähetäthän virheraportin, jotta meidän on helpompi tutkia ongelmaa. - - Järjestelmäasetukset + Järjestelmäasetukset. Ilmoitukset ovat käytössä järjestelmäasetuksissa. Ilmoitukset ovat pois käytöstä järjestelmäasetuksissa. \nTarkistathan järjestelmäasetukset. Avaa asetukset - Tilin asetukset. Ilmoitukset ovat käytössä tililläsi. Ilmoitukset eivät ole käytössä tililläsi. \nTarkista tilisi asetukset. Ota käyttöön - Istunnon asetukset. Ilmoitukset ovat käytössä tässä istunnossa. Ilmoitukset eivät ole käytössä tässä istunnossa. \nTarkista Elementin asetukset. Ota käyttöön - Mukautetut asetukset. Huomaathan, että osa viestityypeistä on asetettu olemaan hiljaisia (tuottavat ilmoituksen ilman ääntä). Osa ilmoituksista on otettu pois käytöstä mukautetuissa asetuksissasi. Mukautettujen sääntöjen lataaminen epäonnistui. Yritä uudelleen. Tarkista asetukset - Play Services -palvelun tarkistus Google Play Services APK on saatavilla ja ajan tasalla. Element käyttää Google Play Services -palvelua ilmoitusten välittämiseen, mutta se ei näytä olevan määritetty oikein: \n%1$s Korjaa Play Services -palvelu - Firebase-tunniste FCM-tunniste haettu onnistuneesti: \n%1$s FCM-tunnisteen haku epäonnistui: \n%1$s - [%1$s] -\nTämä virhe ei ole Elementin hallinnassa ja Googlen mukaan tämä virhe tarkoittaa, että tällä laitteella on liikaa FCM:ään liittyneitä sovelluksia. Tämä virhe ilmenee vain tapauksissa, jossa on on erittäin paljon sovelluksia asennettuna, joten sen ei pitäisi vaikuttaa normaaliin käyttäjään. + [%1$s] +\nElement ei voi vaikuttaa tähän virheeseen, ja Googlen mukaan tämä virhe tarkoittaa, että tällä laitteella on liikaa FCM:ään rekisteröityjä sovelluksia. Tämä virhe ilmenee vain tapauksissa, joissa on erittäin paljon FCM:ään rekisteröityjä sovelluksia asennettuna, joten tätä ei pitäisi tapahtua normaalisti. [%1$s] \nTämä virhe ei ole Elementin ratkaistavissa. Se voi johtua useasta eri syystä. Ehkä tämä toimii, jos yrität myöhemmin. Voit myös tarkistaa, että Google Play Services -palvelu ei ole rajoitettuna järjestelmäasetuksissa, ja että laitteesi kello on oikein. Tämä voi tapahtua myös mukautetun käyttöjärjestelmän kanssa. [%1$s] \nTämä virhe ei ole Elementin ratkaistavissa. Tässä puhelimessa ei ole Google-tiliä. Lisää laitteeseesi Google-tili tätä toimintoa varten. Lisää tili - Tunnisteen rekisteröinti FCM-tunniste rekisteröity onnistuneesti kotipalvelimelle. FCM-tunnisteen rekisteröinti kotipalvelimelle epäonnistui: \n%1$s - Ilmoituspalvelu Ilmoituspalvelu on käynnissä. Ilmoituspalvelu ei ole käynnissä. \nKokeile Elementin uudelleenkäynnistystä. Käynnistä palvelu - Ilmoituspalvelun automaattinen uudelleenkäynnistys Palvelu suljettiin ja se käynnistyi uudelleen automaattisesti. Palvelun uudelleenkäynnistäminen epäonnistui - Käynnistä laitteen käynnistyessä Palvelu käynnistetään, kun laite käynnistetään uudelleen. Palvelua ei käynnistetä laitteen uudelleenkäynnistyksen yhteydessä. Et tule saamaan ilmoituksia ennen kuin Element on käynnistetty uudelleen. Ota käyttöön automaattinen käynnistys - Tarkista taustapalveluiden rajoitukset Taustapalveluiden rajoitukset ovat pois käytöstä. Tämä testi tulee ajaa mobiilidatayhteydellä (ilman wlania). \n%1$s @@ -1057,12 +902,10 @@ Haluatko lisätä paketteja? \nTyötä, jota Element yrittää tehdä, rajoitetaan aggressiivisesti, kun se on taustalla, mikä saattaa vaikuttaa ilmoituksiin. \n%1$s Poista rajoitukset - Akunkäytön optimointi Akunkäytön optimointi ei vaikuta Elementin toimintaan. Jos käyttäjä jättää laitteen paikalleen ilman latausjohtoa niin, että näyttö on pois päältä, laite siirtyy torkkutilaan. Tämä estää sovelluksia käyttämästä verkkoyhteyksiä ja lykkää niiden töitä, synkronointeja ja perushälytyksiä. Jätä optimointi huomiotta - Normaali Heikentynyt yksityisyys Tämä sovellus tarvitsee oikeuden ajaakseen taustapalveluaan @@ -1071,13 +914,10 @@ Haluatko lisätä paketteja? • ilmoituksessa olevan viestin sisältö haetaan turvallisesti suoraan Matrix-kotipalvelimelta • ilmoitus sisältää metadataa ja itse viestin • ilmoitus ei näytä viestin sisältöä - Mukauta äänekkäitä ilmoituksia Mukauta soittoilmoituksia Mukauta hiljaisia ilmoituksia Valitse Ledin väri, värinä ja ääni… - - Salausavainten hallinta Esikatsele linkkejä keskustelussa (vaatii tuen kotipalvelimelta). Ilmoita muille, että olet kirjoittamassa viestiä. @@ -1089,31 +929,22 @@ Haluatko lisätä paketteja? Element voi ajaa itseään taustalla hallitakseen sinulle näytettäviä ilmoituksia turvallisesti ja yksityisesti. Tämä voi vaikuttaa akunkäyttöön. Anna oikeus Valitse toinen vaihtoehto - Taustayhteys Elementin tarvitsee käyttää taustayhteyttä, jotta se voi näyttää luotettavia ilmoituksia. \nSeuraavalla ruudulla sinulta kysytään lupaa, jotta Element voi pitää itsensä käynnissä taustalla. Anna oikeus - Element kerää anonyymiä analytiikkaa sovelluksen parantamiseksi. Otathan analytiikan käyttöön Elementin parantamiseksi. Kyllä, haluan auttaa! - Datansäästötila ottaa käyttöön erityisen suodattimen, joka poistaa paikallaoloilmoitukset ja kirjoittamisen ilmoitukset. - Sähköpostiasi varmennettaessa tapahtui virhe. - Puhelinnumeroasi varmennettaessa tapahtui virhe. Lisätietoa: %s - Et ole yhdenkään yhteisön jäsen. - Luo salalause salataksesi viedyt avaimet. Tarvitset saman salalauseen avainten tuomiseen. Salattujen viestien palautus Hallitse avainten varmuuskopiointia - Kirjoita tähän… - yksi lukematon ilmoitettu viesti %d lukematonta ilmoitettua viestiä @@ -1130,13 +961,11 @@ Haluatko lisätä paketteja? yksi aktiivinen sovelma %d aktiivista sovelmaa - Vaadittu parametri puuttuu. Parametri ei ole kelvollinen. Käynnistä järjestelmän kamera Elementin kameraruudun sijaan. Käytä näppäimistön rivinvaihtopainiketta viestin lähettämiseen Tämä valinta vaatii kolmannen osapuolen sovelluksen viestien tallennukseen. - Komento ”%s” vaatii enemmän parametreja, tai jotkin parametrit ovat virheellisiä. Näyttää toiminnon Estää käyttäjän annetulla id:llä @@ -1151,13 +980,11 @@ Haluatko lisätä paketteja? Vaihtaa näytettävän nimimerkkisi Markdown päällä/pois Matrix-sovellusten hallinnan korjaamiseen - Hiljainen yksi jäsen %d jäsentä - yksi huone %d huonetta @@ -1165,10 +992,8 @@ Haluatko lisätä paketteja? Kuittauksen hahmokuva Ilmoituksen hahmokuva Hahmokuva - Jatkaaksesi kotipalvelimen %1$s käyttöä, sinun täytyy hyväksyä palvelun käyttöehdot. Näytä ehdot - Tämä tekee tilistäsi lopullisesti käyttökelvottoman. Et voi kirjautua sisään eikä kukaan pysty rekisteröitymään samalla käyttäjätunnuksella. Tämä saa tilisi poistumaan kaikista huoneista, joissa se on osallisena, ja poistaa tilin tiedot identiteettipalvelimelta. Tämä toiminto on peruuttamaton. \n \nTilin deaktivointi ei oletuksena saa meitä unohtamaan lähettämiäsi viestejä. Jos haluat meidän unohtavan viestisi, merkitse alapuolella oleva valintaruutu. @@ -1180,49 +1005,36 @@ Haluatko lisätä paketteja? Keskustelu jatkuu täällä Tämä huone on jatkoa toiselle keskustelulle Paina tästä nähdäksesi vanhemmat viestit - Resurssiraja saavutettu Ota yhteys ylläpitäjään - ota yhteys palvelun ylläpitäjään - Tämä kotipalvelin on ylittänyt yhden resurssirajoistaan, joten osa käyttäjistä ei pysty kirjautumaan sisään. Tämä kotipalvelin on ylittänyt yhden resurssirajoistaan. - Tämä kotipalvelin on saavuttanut kuukausittaisten aktiivisten käyttäjien rajansa, joten osa käyttäjistä ei pysty kirjautumaan sisään. Tämä kotipalvelin on saavuttanut kuukausittaisten aktiivisten käyttäjien rajansa. - Tee %s saadaksesi tätä rajaa korotettua. Tee %s jatkaaksesi palvelun käyttöä. - Lataa huoneen käyttäjät laiskasti Paranna suorituskykyä lataamalla huoneen jäsenet vasta, kun huone näytetään ensimmäisen kerran. Kotipalvelimesi ei tue huoneen jäsenten laiskaa latausta. Yritä myöhemmin. - Törmäsimme virheeseen - laajenna supista - Näytä infoalue Aina Viesteille ja virheille Vain virheille - %1$s: %1$s: %2$s +%d %d+ Kelvollista Google Play Services APK:ta ei löytynyt. Ilmoitukset eivät ehkä toimi oikein. - Luo salalause Salalause ei täsmää Syötä salalause Salalause on liian heikko - Poista salalause, jos haluat Element generoivan palautusavaimen. Matrix-istuntoa ei ole saatavilla - Älä koskaan menetä salattuja viestejä Salatuissa huoneissa viestit ovat suojattuna osapuolten välisellä salauksella. Vain sinä ja vastaanottaja(t) omistavat avaimet näiden viestien lukemiseen. \n @@ -1230,7 +1042,6 @@ Haluatko lisätä paketteja? Aloita avainten varmuuskopioinnin käyttö (Edistynyt) Tallenna avaimet käsin - Turvaa varmuuskopiosi salalauseella. Tallennamme salatun kopion avaimistasi kotipalvelimellesi. Suojaa varmuuskopiosi salalauseella pitääksesi sen turvattuna. \n @@ -1252,7 +1063,6 @@ Haluatko lisätä paketteja? Palautusavain on tallennettu kohteeseen \'%s\'. \n \nVaroitus: tämä tiedosto saatetaan poistaa, mikäli Element poistetaan. - Teethän kopion Jaa palautusavain kohteelle… Luodaan palautusavainta käyttäen salalausetta. Tässä saattaa kestää useampi sekunti. @@ -1260,25 +1070,18 @@ Haluatko lisätä paketteja? Odottamaton virhe Varmuuskopiointi aloitettu Salausavaimiasi siirretään kotipalvelimellesi tausta-ajona. Ensimmäisessä varmuuskopioinnissa saattaa mennä useampi minuutti. - - Oletko varma\? Saatat menettää pääsyn viesteihisi jos kirjaudut ulos tai menetät tämän laitteen. - Haetaan varmuuskopion versiota… Käytä palautuksen salalausetta ottaaksesi käyttöön salatun viestihistoriasi käyttää palautusavaintasi Jos et tiedä palautuksen salalausettasi, voit %s. - Käytä palautusavainta ottaaksesi salatun viestihistoriasi käyttöön Syötä palautusavain - Viestien palautus - Menetitkö palautusavaimesi\? Voit asettaa uuden asetuksissa. Varmuuskopiota ei pystytty purkamaan tällä salalauseella. Tarkista, että syötit oikean palautuksen salalauseen. Verkkovirhe: tarkista yhteytesi ja yritä uudelleen. - Palautetaan varmuuskopiota: Lasketaan palautusavainta… Ladataan avaimia… @@ -1286,7 +1089,6 @@ Haluatko lisätä paketteja? Otetaan historia käyttöön Syötä palautusavain Varmuuskopiota ei pystytty purkamaan tällä palautusavaimella. Tarkista, että olet syöttänyt oikean palautusavaimen. - Varmuuskopio palautettu %s ! Varmuuskopio palautettiin yhdellä avaimella. @@ -1296,18 +1098,13 @@ Haluatko lisätä paketteja? Yksi uusi avain lisätty tähän istuntoon. %d uutta avainta lisätty tähän listuntoon. - Uusimman palautusavaimen version hakeminen epäonnistui (%s). Istunnon kryptografia ei ole aktivoitu - - Palauta varmuuskopiosta Poista varmuuskopio - Avaimien varmuuskopiointi on käytössä tällä laitteella. Avaimien varmuuskopiointi ei ole käytössä tässä istunnossa. Avaimiasi ei varmuuskopioida tästä istunnosta. - Varmuuskopiossa on allekirjoitus tuntemattomasta laitteesta ID:llä %s. Varmuuskopiossa on pätevä allekirjoitus tästä istunnosta. Varmuuskopiossa on pätevä allekirjoitus varmennetulta laitteelta %s. @@ -1315,51 +1112,38 @@ Haluatko lisätä paketteja? Varmuuskopiossa on epäkelpo allekirjoitus varmennetulta laitteelta %s Varmuuskopiossa on epäkelpo allekirjoitus tuntemattomalta laitteelta %s Varmuuskopion luotettavuustietojen hakeminen epäonnistui (%s). - Käyttääksesi avainten varmuuskopiointia tällä laitteella, palauta salalauseellasi tai palautusavaimella nyt. Poistetaan varmuuskopiota… Varmuuskopion poisto epäonnistui (%s) - Poista varmuuskopio Poista varmuuskopioidut salausavaimesi palvelimelta\? Et voi sen jälkeen käyttää palautusavaintasi lukeaksesi salattuja viestejäsi. - Uusi avainvarmuuskopio Se oli minä Älä koskaan menetä salattuja viestejäsi Aloita avainten varmuuskopioinnin käyttö - Älä koskaan menetä salattuja viestejäsi Käytä avainten varmuuskopiointia - Uudet salattujen viestien avaimet Hallinnoi avainten varmuuskopioinnissa - Varmuuskopioidaan avaimia… - Kaikki avaimet varmuuskopioitu Varmuuskopioidaan yhtä avainta… Varmuuskopioidaan %d avainta… - Versio Algoritmi Allekirjoitus - Jätä huomiotta - Kirjaudu sisään kertakirjautumisella Tämä osoite ei ole saavutettavissa. Tarkistathan osoitteen Laitteesi käyttää vanhentunutta, haavoittuvaista TLS-protokollan versiota. Turvallisuutesi tähden et voi muodostaa yhteyttä Lähetä viesti enter-näppäimellä Näppäimistön enter-näppäin lähettää viestin sen sijaan, että se lisäisi rivinvaihdon - Päivitä salasana Salasana ei ole kelvollinen Salasanat eivät täsmää - %1$s -> %2$s - Uusi avainvarmuuskopio löydetty. \n \nJos et asettanut uutta palautustapaa, hyökkääjä saattaa yrittää päästä käsiksi tiliisi. Vaihda tilisi salasana ja aseta uusi palautustapa asetuksissa välittömästi. @@ -1368,7 +1152,6 @@ Haluatko lisätä paketteja? Element löysi mukautetun palvelinasetuksen userId:si domainille ”%1$s”: \n%2$s Käytä asetuksia - Alustetaan palvelua Media Oletuksena oleva pakkauksen määrä @@ -1376,7 +1159,6 @@ Haluatko lisätä paketteja? Oletuksena oleva medialähde Valitse Toista sulkimen ääni - Merkitse luetuksi Sovelluksen ei tarvitse pitää yhteyttä kotipalvelimeen taustalla, minkä pitäisi vähentää akunkäyttöä @@ -1387,51 +1169,42 @@ Haluatko lisätä paketteja? yksi ilmoitus %d ilmoitusta - Uusi tapahtuma Huone Uusia viestejä Uusi kutsu Minä ** Lähetys epäonnistui — avaathan huoneen - Valitettavasti konferenssipuhelut Jitsin kanssa eivät toimi vanhoissa laitteissa (laitteet, joissa on Android 5.0 tai vanhempi) - tuntematon IP-osoite - Uusi laite pyytää salausavaimia. -\nLaitteen nimi: %1$s -\nViimeksi nähty: %2$s -\nJos et kirjautunut toisella laitteella, voit jättää tämän pyynnön huomiotta. + Uusi istunto pyytää salausavaimia. +\nIstunnon nimi: %1$s +\nNähty viimeksi: %2$s +\nJos et kirjautunut toisessa paikkaa, jätä tämä pyyntö huomiotta. Jaa Parhaan turvallisuuden takaamiseksi suosittelemme, että teet tämän kasvotusten tai muun luotetun viestintäkeinon avulla. Näytä pyyntö Selvä - Pyyntö peruttu Istunto vastaanotti odottamattoman viestin Virheellinen viesti vastaanotettu Avain ei täsmää Käyttäjä ei täsmää Tuntematon virhe - Kotipalvelimellasi on jo varmuuskopio Näyttää, että olet jo asettanut avainten varmuuskopioinnin toisesta istunnosta. Halutatko korvata sen tällä\? Korvaa Seis - Tarkistetaan varmuuskopion tilaa Odotetaan vastapuolen varmistusta… - Istunto ei ole tietoinen kyseisestä transaktiosta SAS ei täsmännyt Muokkaa Vastaa - Yritä uudelleen Aloita sovelluksen käyttö liittymällä huoneeseen. Lähetti sinulle kutsun %s kutsui - Sinulla ei ole enempää lukemattomia viestejä Tervetuloa kotiin! Löydät täältä lukemattomat viestit @@ -1439,14 +1212,12 @@ Haluatko lisätä paketteja? Tässä näytetään yksityiset keskustelusi Huoneet Huoneesi näytetään tässä - Reaktiot Samaa mieltä Tykkää Lisää reaktio Näytä reaktiot Reaktiot - Käyttäjä poisti tapahtuman Huoneen ylläpitäjä moderoi tapahtuman Epämuotoinen tapahtuma, ei voida näyttää @@ -1454,13 +1225,10 @@ Haluatko lisätä paketteja? Ei verkkoa. Tarkista internet-yhteytesi. Odota… Kaikki yhteisöt - Tätä huonetta ei voi esikatsella Element ei vielä tue täysin julkisen huoneen esikatselua - Huoneet Yksityisviestit - Uusi huone LUO Huoneen nimi @@ -1468,68 +1236,47 @@ Haluatko lisätä paketteja? Kuka tahansa voi liittyä tähän huoneeseen Luottamustietoa haettaessa tapahtui virhe Avainten varmuuskopioinnin tietoja haettaessa tapahtui virhe - Tuntematon laite pyytää salausavaimia \nLaitteen nimi: %1$s \nViimeksi nähty: %2$s \nJos et kirjautunut toisella laitteella, voit jättää tämän pyynnön huomiotta. - Matrix SDK:n versio Muut kolmansien osapuolten huomautukset Pikareaktiot - Asetukset Tietoturva ja yksityisyys Olet ajan tasalla! Viimeksi muokannut %1$s %2$s - - Vaihda verkkoa Katselet jo tätä huonetta! - Yleiset Ääni ja video Ohje ja tietoa - - Tee ehdotus Kirjoita ehdotuksesi alle. Kuvaile ehdotuksesi tässä Kiitos, ehdotus on lähetetty Ehdotuksen lähettäminen epäonnistui (%s) - Näytä piilotetut tapahtumat aikajanalla - Yksityisviestit - Odotetaan… Salataan tiedostoa… Lähetetään tiedostoa (%1$s / %2$s) - Ladataan tiedostoa %1$s… Tiedosto %1$s ladattu! - (muokattu) - - Muokkauksia ei löytynyt - Suodata keskusteluja… Etkö löydä, mitä etsit\? Luo uusi huone Lähetä uusi yksityisviesti Näytä huoneluettelo - Linkki kopioitu leikepöydälle - Luodaan huonetta… Näet tuloksia kirjoittamalla jotain Liitytään huoneeseen… - Näytä muokkaushistoria - Vahvista istunto - Vahvista Avaimen jakopyyntö Aloita vahvistaminen @@ -1542,55 +1289,40 @@ Haluatko lisätä paketteja? \n%s Vahvistus on peruttu. \nSyy: %s - Vuorovaikutteinen laitteen vahvistus Vahvistuspyyntö %s haluaa vahvistaa laitteesi - Käyttäjä perui vahvistuksen Vahvistustoimenpide aikakatkaistiin Huoneluettelo Julkaise tämä huone huoneluettelossa - Viesti-ilmoitusten säännöt Katkaise yhteys Kieltäydy - Identiteettipalvelinta ei ole määritetty. - Puhelu epäonnistui väärin määritetyn palvelimen takia Älä kysy uudestaan - Kotipalvelinta ei voi tavoittaa tästä URL-osoitteesta, tarkista osoite Lisää identiteettipalvelin asetuksissasi, jotta voit tehdä tämän toiminnon. - Taustasynkronointitila (kokeellinen) + Taustasynkronointitila Ei taustasynkronointia Et saa ilmoituksia saapuvista viesteistä, kun sovellus on taustalla. Asetusten päivittäminen epäonnistui. - - Haluttu synkronointiväli %s \nSynkronointia saatetaan lykätä resursseista (akusta) tai laitteen tilasta (virransäästö) riippuen. Julkinen nimi (näkyy ihmisille, joihin olet yhteydessä) Istunnon julkinen nimi näkyy ihmisille, joihin olet yhteydessä Jatkaaksesi sinun täytyy hyväksyä palvelun käyttöehdot. - Et käytä identiteettipalvelinta Identiteettipalvelinta ei ole määritetty, salasanan palautus vaaditaan. - Näyttää, että yrität yhdistää toiseen kotipalvelimeen. Haluatko kirjautua ulos\? - URL-osoite: Ota käyttöön pyyhkäisemällä vastaaminen aikajanalla - Käyttöehdot Lue ehdot Käytä botteja, siltoja, sovelmia ja tarrapaketteja - Lue osoitteessa - - Identiteettipalvelin Katkaise yhteys identiteettipalvelimeen Määritä identiteettipalvelin @@ -1598,7 +1330,6 @@ Haluatko lisätä paketteja? Käytät palvelinta %1$s löytääksesi tuntemiasi ihmisiä ja ollaksesi heidän löydettävissään. Et käytä tällä hetkellä identiteettipalvelinta. Jotta voit löytää tuntemiasi ihmisiä ja jotta he löytävät sinut, määritä identiteettipalvelin alla. Odottaa - Syötä uusi identiteettipalvelin Identiteettipalvelimeen ei saatu yhteyttä Syötä identiteettipalvelimen URL-osoite @@ -1612,24 +1343,19 @@ Haluatko lisätä paketteja? Lähetimme sinulle vahvistussähköpostin osoitteeseen %s, tarkista sähköpostisi ja klikkaa vahvistuslinkkiä Ota yksityiskohtaiset lokit käyttöön. Yritä uudelleen, kun olet hyväksynyt kotipalvelimesi käyttöehdot. - Palvelimen vastaus näyttäisi olevan liian hidas. Tämä voi johtua kehnosta yhteydestä tai palvelimella olevasta ongelmasta. Yritä hetken kuluttua uudelleen. - Lähetä liite - Luo uusi huone Näytä salasana Piilota salasana Siirry loppuun - %1$s, %2$s ja %3$s lukivat %1$s ja %2$s lukivat %s luki - 1 käyttäjä luki - %d käyttäjää luki + 1 käyttäjä on lukenut + %d käyttäjää on lukenut - Liitettä noudettaessa tapahtui virhe. Tiedosto Kamera @@ -1638,7 +1364,6 @@ Haluatko lisätä paketteja? Se on roskapostia Se on sopimaton Verkkoyhteyttä ei ole juuri nyt - Ei mitään Pyydä kotipalvelimesi (%1$s) ylläpitäjää määrittämään TURN-palvelin, jotta puhelut toimivat luotettavasti. \n @@ -1654,11 +1379,9 @@ Haluatko lisätä paketteja? Viestimuokkaukset Ole löydettävissä Tekstiviesti on lähetetty numeroon %s. Syötä sen sisältämä varmistuskoodi. - Viesti-ilmoitusten sääntöjä ei ole määritetty Luo uusi yksityiskeskustelu Latn - Kumoa Tarkasta Aseta puhelinnumerosi, ja voit myöhemmin antaa muiden löytää sinut puhelinnumerosi perusteella. @@ -1667,8 +1390,6 @@ Haluatko lisätä paketteja? Vahvista salasanasi Et voi tehdä tätä mobiili-Elementista Tunnistautuminen vaaditaan - - Integraatiot Käytä integraatioiden lähdettä bottien, siltojen ja tarrapakettien hallintaan. \nIntegraatioiden lähteet vastaanottavat asetusdataa ja voivat muokata sovelmia, lähettää kutsuja huoneeseen ja muokata oikeustasoja puolestasi. @@ -1676,7 +1397,6 @@ Haluatko lisätä paketteja? Muokkaa etsinnän asetuksia. Salli integraatiot Integraatioiden lähde - Sovelma Lataa sovelma Sovelman lisäsi: @@ -1687,107 +1407,82 @@ Haluatko lisätä paketteja? Lataa sovelma uudelleen Avaa selaimessa Kumoa minun pääsy - Näyttönimesi Profiilikuvasi osoite Käyttäjätunnisteesi Teemasi Sovelman tunniste Huoneen tunniste - - Tämä sovelma haluaa käyttää seuraavia resursseja: Salli Estä kaikki Käytä kameraa Käytä mikrofonia Lue DRM-suojattua mediaa - Ei integraatioiden lähteitä asetettuna. Huomiotta - Olet kirjautunut ulos epäkelpojen tai vanhentuneiden pääsytietojen takia. - Varmenna vertaamalla lyhyttä tekstijonoa. Varmenna tämä laite merkkaamalla se luotetuksi. Kumppaneiden laitteisiin luottaminen antaa sinulle ylimääräistä mielenrauhaa, kun käytät osapuolten välistä salausta. Tämän laitteen varmentaminen merkkaa sen luotetuksi, ja samoin sinun laitteesi merkataan luotetuksi kumppanisi näkökulmasta. - Varmenna tämä laite varmistamalla, että seuraava emoji ilmestyy kumppanisi näytölle Varmenna tämä laite varmistamalla, että seuraavat numerot ilmestyvät kumppanisi näytölle - Turvalliset viestit tämän käyttäjän kanssa ovat salattuja päästä päähän, eivätkä kolmannet osapuolet voi lukea niitä. Mitään ei tule näytölle\? Kaikki asiakasohjelmat eivät vielä tue interaktiivista varmennusta. Käytä vanhaa varmennustapaa. Käytä vanhaa varmennustapaa. - Laitteet eivät pysty sopimaan avaimista, tiivisteestä, MAC:sta tai SAS-metodista Tiivisteet eivät täsmänneet - Muuta - Tuo osapuolten välisen salauksen avaimet tiedostosta ”%1$s”. - Edistynyt Ei rekisteröityjä viesti-ilmoitusten yhdyskäytäviä - app_id: push_key: app_display_name: device_name: Formaatti: - Rekisteröi tunniste - Salataan pikkukuvaa… Lähetetään pikkukuvaa (%1$s / %2$s) Nimi tai tunniste (#example:matrix.org) - Lisää Matrix-tunnisteella Tuloksia ei löytynyt. Käytä ”Lisää Matrix-tunnisteella” etsiäksesi palvelimelta. Suodata käyttäjätunnuksella tai tunnisteella… - Etsittävät sähköpostiosoitteet Vaihtoehdot ilmestyvät, kunhan olet lisännyt sähköpostiosoitteen. Vaihtoehdot ilmestyvät, kunhan olet lisännyt puhelinnumeron. Etsittävät puhelinnumerot Runsassanaiset lokit auttavat antamalla enemmän tietoa kehittäjille, kun lähetät virheilmoituksen. Vaikka runsaammat lokit ovat käytössä, sovellus ei lähetä viestien sisältöjä tai mitään muuta yksityistä tietoa. - - Avaa navigaatiovalikko Avaa huoneen luontivalikko Sulje huoneen luontivalikko… Sulje avainten varmuuskopion mainos Tiedosto ”%1$s” (%2$s) on liian iso lähetettäväksi. Raja on %3$s. - Yhteystieto Ääni Jakotiedon käsittely epäonnistui - Muokattu ilmianto… Ilmianna tämä sisältö Sisällön ilmiannon syy ILMIANNA - ESTÄ KÄYTTÄJÄ - + JÄTÄ KÄYTTÄJÄ HUOMIOTTA Sisältö ilmiannettu - Tämä sisältö on ilmiannettu. -\n -\nJos et halua nähdä enempää sisältöä tältä käyttäjältä, voit estää hänet piilottaaksesi hänen viestit + Tämä sisältö on ilmiannettu. +\n +\nJos et halua nähdä enempää sisältöä tältä käyttäjältä, voit estää hänet piilottaaksesi hänen viestit. Ilmiannettu roskapostina - Tämä sisältö on ilmiannettu roskapostina. -\n -\nJos et halua nähdä enempää sisältöä tältä käyttäjältä, voit estää hänet piilottaaksesi hänen viestit + Tämä sisältö on ilmiannettu roskapostina. +\n +\nJos et halua nähdä enempää sisältöä tältä käyttäjältä, voit estää hänet piilottaaksesi hänen viestit. Ilmiannettu epäsopivana - Tämä sisältö on ilmiannettu epäsopivana. -\n -\nJos et halua nähdä enempää sisältöä tältä käyttäjältä, voit estää hänet piilottaaksesi hänen viestit - + Tämä sisältö on ilmiannettu epäsopivana. +\n +\nJos et halua nähdä enempää sisältöä tältä käyttäjältä, voit estää hänet piilottaaksesi hänen viestit. Element tarvitsee oikeuden tallentaakseen osapuolten välisen salauksen avaimesi talteen. \n \nSalli pääsy tiedostoihin seuraavassa ponnahdusikkunassa, jotta voit viedä avaimesi käsin. - Tämä ei ole kelvollinen Matrix-palvelimen osoite - Estä käyttäjä - + Jätä käyttäjä huomiotta Kaikki viestit (äänekäs) Kaikki viestit Vain maininnat @@ -1798,22 +1493,16 @@ Haluatko lisätä paketteja? Lähettää annetun viestin ilonpilaajana Ilonpilaaja Syötä avainsanat löytääksesi reaktion. - Et jätä yhtään käyttäjää huomiotta - Tee pitkä klikkaus huoneelle nähdäksesi lisää asetuksia - - %1$s asetti huoneen julkiseksi kelle tahansa, joka tietää huoneen osoitteen. %1$s muutti huoneeseen liittymisen vaatimaan kutsua. Lukemattomia viestejä - - Vapauta keskustelusi + Sinun keskustelusi. Pidä ne hallussasi. Keskustele kaksistaan tai ryhmissä Pidä keskustelut yksityisinä ja salattuina Laajenna ja muokkaa kokemustasi Aloita - Valitse palvelin Kuten sähköpostissa, tunnuksilla on yksi koti, mutta voit keskustella koko maailman kanssa Liity miljoonien joukkoon suurimmalla julkisella palvelimella @@ -1821,7 +1510,6 @@ Haluatko lisätä paketteja? Lue lisää Muut Mukautetut ja monimutkaiset asetukset - Jatka Yhdistä palvelimeen %1$s Yhdistä Element Matrix Services @@ -1830,7 +1518,6 @@ Haluatko lisätä paketteja? Rekisteröidy Kirjaudu sisään Jatka kertakirjautumiseen - Element Matrix Services in osoite Osoite Korkealuokkaista isännöintiä organisaatioille @@ -1843,58 +1530,46 @@ Haluatko lisätä paketteja? Sovellus ei pysty luomaan uusia tunnuksia tälle kotipalvelimelle. \n \nHaluatko rekisteröityä web-klientillä\? - Tämä sähköpostiosoite ei ole liitettynä mihinkään tunnukseen. - Palauta salasana palvelimella %1$s Sähköpostiisi lähetetään viesti uuden salananan asettamiseksi. Seuraava Sähköposti Uusi salasana - Varoitus! Salasanan vaihtaminen nollaa kaikki osapuolten välisen salauksen avaimet kaikilla laitteillasi, joka estää sinua lukemasta vanhoja viestejä. Ota käyttöön avainten varmuuskopiointi tai vie huoneen avaimet toiselta laitteelta ennen kuin vaihdat salasanasi. Jatka - Tämä sähköposti ei ole liitettynä mihinkään tunnukseen - Tarkista sähköpostisi Vahvistusviesti lähetettiin osoitteeseen %1$s. Näpäytä linkkiä vahvistaaksesi uuden salasanasi. Seurattuasi siinä olevaa linkkiä, klikkaa alapuolelta. Olen vahvistanut sähköpostiosoitteeni - Valmis! Salasanasi on vaihdettu. Olet kirjautunut ulos kaikilta laitteilta, etkä saa enää viesti-ilmoituksia. Ottaaksesi viesti-ilmoitukset uudelleen käyttöön, kirjaudu sisään jokaisella laitteellasi. Takaisin sisäänkirjautumiseen - Varoitus Salasanaasi ei ole vielä vaihdettu. \n \nPeru salasananvaihtoprosessi\? - Aseta sähköpostiosoite Aseta sähköpostiosoite palauttaaksesi tunnuksesi. Myöhemmin, voit antaa muiden löytää sinut sähköpostillasi. Sähköposti Sähköposti (vapaaehtoinen) Seuraava - Aseta puhelinnumero Aseta puhelinnumero antaaksesi muiden löytää sinut puhellinumerosi perusteella. Käytä maailmanlaajuista puhelinnumeron muotoa. Puhelinnumero Puhelinnumero (vapaaehtoinen) Seuraava - Vahvista puhelinnumero Lähetimme sinulle koodin numeroon %1$s. Syötä se alapuolelle vahvistaaksesi numeron. Syötä koodi Lähetä uudelleen Seuraava - Kansainvälisten puhelinnumeroiden pitää alkaa merkillä ”+” Puhelinnumero vaikuttaa epäkelvolta. Tarkista numero - Rekisteröidy palvelimelle %1$s Käyttäjätunnus tai sähköpostiosoite Salasana @@ -1904,30 +1579,24 @@ Haluatko lisätä paketteja? Tunnustasi ei ole vielä luotu. \n \nPeru rekisteröintiprosessi\? - Valitse matrix.org Valitse Element Matrix Services Valitse muu kotipalvelin Ratkaise seuraava kuvavarmennushaaste Hyväksy ehdot jatkaaksesi - Tarkista sähköpostisi Lähetimme sähköpostin osoitteeseen %1$s. \nKlikkaa siinä olevaa linkkiä jatkaaksesi tunnuksen luontia. Syöttämäsi koodi ei ole kelvollinen. Tarkista se. Vanhentunut kotipalvelin Tämä kotipalvelin pyörii liian vanhalla versiolla, jotta pystyisimme yhdistämään siihen. Pyydä kotipalvelimesi ylläpitäjää päivittämään palvelimensa. - Liian monta pyyntöä lähetettiin. Voit yrittää uudelleen 1 sekunnissa… Liian monta pyyntöä lähetettiin. Voit yrittää uudelleen %1$d sekunnissa… - Nähty toimesta - Olet kirjautunut ulos Kirjaudu sisään uudelleen - Olet kirjautunut ulos Kirjaudu sisään Kotipalvelimen (%1$s) ylläpitäjä on kirjannut sinut ulos tunnukseltasi %2$s (%3$s). @@ -1938,7 +1607,6 @@ Haluatko lisätä paketteja? \n \nPoista ne jos olet lopettanut tämän laitteen käytön, tai haluat kirjautua sisään toiselle tunnukselle. Poista kaikki tiedot - Poista tiedot Poista kaikki tälle laitteelle tallennetut tiedot\? \nKirjaudu sisään päästäksesi käsiksi tunnuksesi tietoihin ja viesteihin. @@ -1946,12 +1614,9 @@ Haluatko lisätä paketteja? Poista tiedot Nykyinen istunto on käyttäjälle %1$s, ja yritit kirjautuas isään käyttäjälle %2$s. Element ei tue tätä. \nPoista ensin tietosi ja kirjaudu sen jälkeen toisella tunnuksella. Voit vaihtoehtoisesti käyttää Elementin selainversiota. - matrix.to-linkkisi oli epämuodostunut Kuvaus on liian lyhyt - Syötä palvelin tai sen Elementin osoite, mihin haluat yhdistää - Se voi johtua monesta eri syystä: \n \n• olet vaihtanut salasanasi toisella laitteella @@ -1965,17 +1630,14 @@ Haluatko lisätä paketteja? Asetukset Nykyinen istunto Muut istunnot - Ota salaus käyttöön Salausta ei voi poistaa käytöstä, kun se on kerran otettu käyttöön. - Odotetaan… %s peruutti Sinä peruutit %s hyväksyi Sinä hyväksyit Sinä - Lue lisää Ilmoitukset @@ -1984,10 +1646,8 @@ Haluatko lisätä paketteja? Poistu huoneesta Poistutaan huoneesta… - Käyttäjätunnus Synkronoidaan tietoja ensimmäistä kertaa… - Kehittäjämoodi Kehittäjämoodi aktivoi piilotettuja ominaisuuksia, mutta voi tehdä sovelluksesta epävakaan. Vain kehittäjille! Raivoravistus @@ -1995,13 +1655,9 @@ Haluatko lisätä paketteja? Ravista puhelintasi testataksesi tunnistusrajan Ravistus tunnistettu! Näytetään vain ensimmäiset tulokset, kirjoita lisää kirjaimia… - Element voi kaatuilla tavallista useammin odottamattomien virheiden vuoksi - Lisää ¯\\_(ツ)_/¯ tavallisen viestin alkuun - Käyttämäsi sähköpostipalvelun ei ole sallittu rekisteröityä tälle palvelimelle - Täsmäävät Eivät täsmää Vahvista käyttäjä tarkastamalla että seuraavat emojit vastaavat täysin heidän ruudullaan näkyviä. @@ -2015,29 +1671,20 @@ Haluatko lisätä paketteja? \n - Varmentamasi toisen käyttäjän kotipalvelin \n - Sinun tai toisen käyttäjän Internet-yhteys \n - Sinun tai toisen käyttäjän käyttämä laite - Äänitiedosto Tiedosto - Varmennus lähetetty Varmennuspyyntö - - Varmenna istunto Varmenna manuaalisesti - Lue koodi toisen käyttäjän laitteesta varmentaaksenne toisenne tietoturvallisesti Lue toisen käyttäjän koodi Lukeminen ei onnistu Vertailkaa emojeilla jos et ole toisen käyttäjän luona - Varmenna vertaamalla emojeja - Varmenna emojeilla Jos koodin lukeminen ei onnistu, varmenna vertaamalla lyhyttä sarjaa emojeja. - QR-koodi - Varmenna %s Varmennettu %s Odotetaan käyttäjää %s… @@ -2055,79 +1702,56 @@ Haluatko lisätä paketteja? Ylläpitäjät Moderaattorit Käyttäjät - Ylläpitäjä %1$s:ssä Moderaattori %1$s:ssä Siirry lukukuittaukseen - Element ei osaa käsitellä tapahtumia joiden tyyppi on \'%1$s\' Element ei osaa käsitellä viestejä joiden tyyppi on \'%1$s\' Element ei osannut piirtää tapahtuman jonka tunniste on \'%1$s\' sisältöä - Viimeaikaiset huoneet Muut huoneet - Lähettää annetun viestin väritettynä sateenkaaren väreillä Ota käyttöön osapuolten välinen salaus Salausta ei voi enää poistaa käytöstä sen jälkeen kun se on otettu käyttöön. - Otetaanko salaus käyttöön\? Salausta ei voi ottaa pois käytöstä sen jälkeen kun se on otettu käyttöön. Salattuja viestejä ei pysty lukemaan edes palvelin, vain ainoastaan huoneessa olijat. Salauksen käyttöönotto voi estää bottien ja siltojen toiminnan huoneessa. Ota salaus käyttöön - Salauksen mahdollistamiseksi varmenna %s tarkastamalla kertakäyttöinen koodi. Tee tämä toisen käyttäjän ollessa läsnä tai käyttäkää toista viestintävälinettä tietoturvan varmistamiseksi. - Verratkaa emojeja ja varmistakaa että ne ovat samassa järjestyksessä kummallakin. Vertaa koodia joka näkyy toisen käyttäjän ruudulla. Tämän käyttäjän kanssa käyty viestintä on nyt päästä päähän salattu ja sitä ei pysty kukaan ulkopuolinen vakoilemaan. Istuntosi on nyt vahvistettu. Sillä on pääsy salattuihin viesteihisi ja muut käyttäjät näkevät sen luotettuna. - - Aktiiviset istunnot Näytä kaikki istunnot Istuntojen hallinta Kirjaudu ulos tästä istunnosta - Ei salaukseen liittyvää tietoa - Istunto on luotettu, koska olet vahvistanut sen: Vahvista tämä istunto jotta se merkitään luotetuksi ja se saa pääsyn salattuihin viesteihin. Jos et ole kirjautunut tähän istuntoon, tunnuksesi on saattanut vuotaa hyökkääjälle: - %d käynnissä oleva istunto %d käynnissä olevaa istuntoa - Varmenna tämä kirjautuminen Muut käyttäjät eivät välttämättä luota siihen Käytä olemassaolevaa istuntoa tämän istunnon varmentamiseksi jotta se saa oikeudet salattuihin viesteihin. - - Varmenna Varmennettu Varoitus - Ei saatu istuntoja Istunnot Luotettu Ei luotettu - - "Tämä istunto on luotettu salattuun viestintään koska %1$s (%2$s) varmisti sen:" + "Tähän istuntoon luotetetaan salatussa viestinnässä, koska %1$s (%2$s) varmisti sen:" %1$s (%2$s) kirjautui sisään uuteen istuntoon: Tämän käyttäjän kanssa käyty viestintä merkitään virheiksi kunnes käyttäjä luottaa tähän istuntoon. Voit vaihtoehtoisesti käsin varmentaa sen. - - Nollaa avaimet - QR-koodi - Melkein valmis! Näkyykä %s:lla sama kilven kuva\? Kyllä Ei - Yhteys kotipalvelimeen on poikki - Kehittäjän työkalut %d ääni @@ -2139,9 +1763,7 @@ Haluatko lisätä paketteja? Luo yksinkertaisen äänestyksen Jos et pääse käsiksi olemassaolevaan istuntoon - Uusi sisäänkirjautuminen - Varoitus: Poista… Haluatko lähettää tämän liitteen %1$s\?:lle\? @@ -2149,53 +1771,38 @@ Haluatko lisätä paketteja? Lähetä kuva alkuperäisessä koossa Lähetä kuvat alkuperäisessä koossa - Vahvista poisto Haluatko varmasti poistaa tämän tapahtuman\? Huomaa, että jos poistat huoneen nimen tai otsikon muutostapahtuman, se voi perua muutoksen. Anna syy Käyttäjä poistanut tapahtuman, syynä: %1$s Tapahtuma moderoitu huoneen ylläpitäjän toimesta, syynä: %1$s - Avaimet ovat jo ajan tasalla! - Element Android - Avainpyynnöt - Päivitä - Uusi kirjautuminen. Olitko se sinä\? Paina tarkastellaksesi ja varmentaaksesi En ollut Tilillesi saatetaan olla murtauduttu - Jos perut, et voi lukea salattuja viestejäsi tällä laitteella eivätkä muut käyttäjät luota siihen Jos perut, et voi lukea salattuja viestejäsi uudella laitteellasi eivätkä muut käyttäjät luota siihen Varmenna laitteesi ohjelman asetuksista. Vahvistus peruttu - Palautussalasana Tilin salasana - Aseta %s Vahvista %s - Anna %s jatkaaksesi. - - Pura ja salaa salatut viestit ja luottamukset asettamalla %s + Pura ja salaa salatut viestit ja luottamukset asettamalla %s. Anna %s uudestaan vahvistaaksesi sen. Älä käytä tilisi salasanaa muualla. - - Odota hetki, kiitos. Alustetaan palautusta. Palautusavaimesi Valmista! Pidä se turvassa Lopeta - Käytä %1$s:tä turvaverkkona jos onnistut unohtamaan %2$s:n. - Julkaistaan luodut identiteettiavaimet Luodaan salausavain salasanasta Määritetään SSSS-oletusavain @@ -2203,22 +1810,15 @@ Haluatko lisätä paketteja? Synkronoidaan käyttäjän avain Synkronoidaan allekirjoitusavain Alustetaan avainten varmuuskopiointi - - %2$s ja %1$s asetettu. \n \nPidä ne tallessa. Tarvitset ne salattujen viestiesi ja tietojesi avaamiseen, jos suljet kaikki istuntosi. - Tulosta se jos mahdollista ja säilytä tuloste turvallisessa paikassa Tallenna se muistitikulle tai varmuuskopiolevylle talteen Kopioi se henkilökohtaiseen pilvitallennustilaasi - Tätä ei pysty tekemään kännykällä - Mukautettu Mukautettu (%1$d) %2$s:ssä - Syy poistoon - Viestin avain - + \ No newline at end of file diff --git a/vector/src/main/res/values-fr/strings.xml b/vector/src/main/res/values-fr/strings.xml index d58f22b399..70b1772fd3 100644 --- a/vector/src/main/res/values-fr/strings.xml +++ b/vector/src/main/res/values-fr/strings.xml @@ -1583,7 +1583,7 @@ Si vous n’avez pas configuré de nouvelle méthode de récupération, un attaq Autoriser le serveur d’assistance d’appel de secours Utilisera %s comme assistant quand votre serveur d’accueil n’en offre pas (votre adresse IP sera partagée lors d’un appel) Ajoutez un serveur d’identité dans vos paramètres pour réaliser cette action. - Mode de synchronisation en arrière-plan (expérimental) + Mode de synchronisation en arrière-plan Optimisé pour la batterie Element se synchronisera en arrière-plan de façon à préserver les ressources limitées de l’appareil (batterie). \nSelon l’état des ressources de votre appareil, la synchronisation peut être retardée par le système d’exploitation. @@ -2377,4 +2377,54 @@ Si vous n’avez pas configuré de nouvelle méthode de récupération, un attaq Renseignez l’URL d’un serveur d’identité Valider + Lancer + Pause + Ignorer + + + Vous n\'avez pas la permission de commencer une téléconférence dans ce salon + Vous n\'avez pas la permission de commencer un appel dans ce salon + Une téléconférence est déjà en cours ! + Commencer une réunion vidéo + Commencer une réunion audio + Les réunions utilisent les règles de sécurités et les permissions de Jitsi. Toutes les personnes actuellement dans ce salon recevrons une invitation à participer à la réunion. + Vous ne pouvez pas passer un appel avec vous-même + Vous ne pouvez pas passer un appel avec vous-même, attendez que les participants accepte l\'invitation + Échec de l\'ajout du module d\'information (widget) + Échec de la suppression du module d\'information (widget) + Accepter + Refuser + Raccrocher + + Copier + Succès + + Notifications + Impossible d\'établir une connexion en temps réel. +\nVeuillez demander à l\'administrateur de votre serveur domestique de configurer un serveur TURN afin que les appels fonctionnent de manière fiable. + + Sélectionner un appareil audio + Téléphone + Hauts-parleurs + Écouteurs + Écouteurs sans fil + Changer de caméra + Le numéro de téléphone est déjà défini. + Erreur SSL : l\'identité du pair n\'a pas été vérifiée. + Erreur SSL. + Prévenir les appels accidentels + Demandez une confirmation avant de commencer un appel + Appel actif (%s) + Retour à l\'appel + + Annuler l\'invitation + Ignorer l\'utilisateur + Ignorer cet utilisateur aura pour effet de supprimer ses messages des espaces que vous partagez. +\n +\nVous pouvez annuler cette action à tout moment dans les paramètres généraux. + Annuler l\'invitation + Êtes-vous sûr de vouloir annuler l\'invitation pour cet utilisateur \? + Bloquer l\'utilisateur + Motif du blocage + Débloquer l\'utilisateur diff --git a/vector/src/main/res/values-hu/strings.xml b/vector/src/main/res/values-hu/strings.xml index 2516d203e0..657f661264 100644 --- a/vector/src/main/res/values-hu/strings.xml +++ b/vector/src/main/res/values-hu/strings.xml @@ -2481,7 +2481,6 @@ Ha nem te állítottad be a visszaállítási metódust, akkor egy támadó pró \n \n%s - Az egyesítésnek nem sikerült visszafejtenie egy üzenetet az idővonalon Külön lap hozzáadása az olvasatlan értesítések számára a főképernyőn. diff --git a/vector/src/main/res/values-it/strings.xml b/vector/src/main/res/values-it/strings.xml index 190e1610aa..b1ff89ccef 100644 --- a/vector/src/main/res/values-it/strings.xml +++ b/vector/src/main/res/values-it/strings.xml @@ -1,18 +1,15 @@ - + - it IT - Messaggi Stanza Impostazioni Dettagli sui membri Cronologia - OK Annulla @@ -46,7 +43,6 @@ o Invita Disconnesso - Disconnetti Chiamata audio @@ -59,27 +55,22 @@ Chiudi Copiato negli appunti Disabilita - Conferma Attenzione - Home Preferiti Chat dirette Stanze - Cerca Cerca tra i preferiti Cerca tra le chat dirette Cerca tra le stanze - Inviti Bassa priorità - Conversazioni Rubrica locale @@ -88,17 +79,15 @@ Nessuna conversazione Element non ha avuto l\'autorizzazione ad accedere alla tua Rubrica locale Nessun risultato - Stanze Elenco stanze Nessuna stanza Nessuna stanza pubblica disponibile - 1 utente + %d utente %d utenti - Invia i registri Invia i registri di crash Invia schermata @@ -108,14 +97,11 @@ Per permettere una diagnosi del problema, oltre alla segnalazione errore verranno inviati anche i registri di Element. Rapporto, registri e schermata non saranno resi pubblici. Tuttavia, se preferisci inviare solamente la segnalazione, deseleziona: Sembra tu stia scuotendo il dispositivo con rabbia. Vuoi segnalare un errore\? L\'ultima volta l\'applicazione è andata in crash. Vuoi inviare una segnalazione\? - La segnalazione errore è stata spedita L\'invio della segnalazione errore è fallito (%s) Avanzamento (%s%%) - Invia file in Leggi - Entra Nome utente Crea account @@ -124,18 +110,14 @@ URL dell\'Home Server URL dell\'Identity Server Cerca - Avvia una nuova chat diretta Avvia chiamata audio Avvia chiamata video - Invia file Fai una foto o un video - Accetta Rifiuta Riaggancia - Accedi Crea account @@ -168,7 +150,9 @@ Hai dimenticato la password\? Usa opzioni server personalizzate (avanzate) Per favore controlla la tua email per proseguire la registrazione - La registrazione con email e numero di telefono in una volta sola non è ancora supportata. Sarà utilizzato solo il numero di telefono. Potrai aggiungere l\'indirizzo email al tuo profilo dalle impostazioni. + La registrazione con email e numero di telefono in una volta sola non è ancora supportata. Sarà utilizzato solo il numero di telefono. +\n +\nPotrai aggiungere l\'indirizzo email al tuo profilo dalle impostazioni. Questo Home Server vorrebbe assicurarsi che tu non sia un robot Nome utente già in uso Home Server: @@ -182,7 +166,6 @@ La tua password è stata reimpostata. \n \nSei stato disconnesso da tutte le sessioni e non riceverai più alcuna notifica. Per ripristinare le notifiche, riconnettiti su ciascun dispositivo. - L\'URL deve iniziare con http[s]:// Impossibile accedere: errore di rete @@ -191,7 +174,6 @@ Impossibile registrarsi Impossibile registrarsi: difetto proprietà dell\'email Inserisci un URL valido - Nome utente/password non corretto Il Token di accesso specificato non è stato riconosciuto JSON malformato @@ -199,35 +181,27 @@ Sono state inviate troppe richieste Questo nome utente è già in uso Il link nella mail non è ancora stato cliccato - - Elenco ricevute lette - - Invia come Originario Grande Medio Piccolo - "Annullare il download? Annullare l\'upload? %d s %1$dm %2$ds - Ieri Oggi - Nome stanza Argomento stanza - Chiamata in corso Chiamata in connessione… @@ -237,16 +211,13 @@ Chiamata video in arrivo Chiamata audio in arrivo Chiamata in corso… - Ricezione fallita da parte del destinatario. La connessione al supporto multimediale è fallita Impossibile avviare la fotocamera chiamata risposta altrove - Fai una foto o un video Impossibile registrare video - Informazione Element deve essere autorizzato ad accedere alla tua Galleria di foto e video per poter inviare e salvare allegati. @@ -267,25 +238,20 @@ Element può usare tua Rubrica locale per trovare altri utenti Matrix grazie alle loro email e numeri di telefono. \n \nTi sta bene comunicare i dati di tutti i tuoi contatti per questo scopo\? - Purtroppo l\'azione non è stata eseguita poiché mancano i permessi - Salvato Salvare nei download? NO Continua - Rimuovi Unisciti Anteprima Rifiuta - Vai al primo messaggio non letto. - Sei stato invitato ad entrare in questa stanza da %s Questo invito è stato spedito a %s, che non è associato a questo account. @@ -293,27 +259,22 @@ Stai provando ad accedere a %s. Desideri entrare per partecipare alla discussione? una stanza Questa è l\'anteprima della stanza. Le interazioni sono disabilitate. - Nuova chat Aggiungi utente 1 utente - Esci dalla stanza Sei sicuro di voler uscire dalla stanza? Sei sicuro di voler rimuovere %s da questa chat? Crea - Online Offline Inattivo - STRUMENTI ADMIN CHIAMATA CHAT DIRETTE SESSIONI - Invita Esci da questa stanza Butta fuori da questa stanza @@ -329,19 +290,15 @@ Mostra l\'elenco delle sessioni La nomina non potrà essere annullata perché darai a questo utente i tuoi stessi poteri. \nSicuro di voler procedere\? - Sicuro di voler invitare %s\? - Invita tramite ID utente CONTATTI LOCALI (%d) ELENCO UTENTI (%s) Solo utenti Matrix - Invita tramite ID utente Per favore inserisci uno o più indirizzi email o ID utente Email o ID utente - Cerca %s sta scrivendo… @@ -358,7 +315,6 @@ Elimina i messaggi non inviati File non trovato Non hai il permesso di pubblicare in questa stanza - Fidati Non fidarti @@ -371,7 +327,6 @@ Il certificato è diverso da quello precedentemente contrassegnato sul tuo telefono come \"affidabile\". Questa cosa è MOLTO INSOLITA. Si raccomanda di NON ACCETTARE questo nuovo certificato. Il certificato del server è cambiato: quello precedente era stato contrassegnato come affidabile ma quello attuale no. Può darsi che il certificato precedente sia scaduto e sia stato semplicemente sostituito con uno nuovo. Contatta l\'amministratore del server per verificare l\'impronta digitale in uso. Contrassegna il certificato come affidabile solo se l\'mpronta digitale comunicata dall\'amministratore del server corrisponde a quella qua sopra. - Dettagli stanza Utenti @@ -380,7 +335,6 @@ ID malformato. Dovrebbe essere un indirizzo email o un ID utente come \'@localpart:domain\' INVITATI MEMBRI - Motivo per cui segnali questo contenuto Desideri nascondere tutti i messaggi di questo utente\? @@ -388,7 +342,6 @@ \nTieni presente che questa azione riavvierà l\'app e ciò potrebbe richiedere molto tempo. Annulla l\'upload Annulla il download - Cerca Cerca tra i membri della stanza @@ -397,7 +350,6 @@ MESSAGGI UTENTI FILE - ENTRA ELENCO @@ -410,18 +362,15 @@ Entra nella stanza Entra in una stanza Digita l\'ID stanza o il suo nome - Esplora l\'elenco Ricerca negli elenchi… - Preferito Bassa priorità Chat diretta Esci dalla conversazione Dimentica - Messaggi Impostazioni @@ -430,9 +379,7 @@ Avvisi di terze parti Copyright Politica sulla privacy - - Immagine del profilo Nome visualizzato Email @@ -441,23 +388,19 @@ Aggiungi numero di telefono Mostra le informazioni dell\'App nelle Impostazioni di sistema. Informazioni sull\'App - Abilita le notifiche per questo account Attiva le notifiche per questa sessione Accendi lo schermo per 3 secondi alla notifica - Messaggi nelle chat dirette Messaggi nelle chat di gruppo Invito ad entrare in una stanza Invito ad una chiamata Messaggi inviati da bot - Esegui all\'avvio Sincronizzazione in background Abilita la sincronizzazione in background La richiesta di sincronizzazione sta impiegando troppo tempo Ritardo tra ogni sincronizzazione - Versione Versione olm Termini e condizioni @@ -467,7 +410,6 @@ Svuota la cache Svuota la cache dei file multimediali Conserva i file multimediali - Impostazioni utente Notifiche Utenti ignorati @@ -484,7 +426,6 @@ Sessioni Mostra data e ora di tutti i messaggi Modalità risparmio dati - Informazioni sulla sessione ID Nome pubblico @@ -495,22 +436,18 @@ Autenticazione Password: Invia - Autenticato come Home Server Server identità - Interfaccia utente Lingua Scegli una lingua - In attesa di verifica Controlla la tua email e clicca sul link che ti è stato spedito. Fatto questo, clicca su Continua. Impossibile verificare l\'indirizzo email. Controlla la tua email e clicca sul link che ti è stato spedito. Fatto questo, clicca su Continua. Questo indirizzo email è già in uso. Questo indirizzo email non è stato trovato. Questo numero di telefono è già in uso. - Cambia la password Password attuale Nuova password @@ -520,13 +457,9 @@ Mostrare tutti i messaggi di %s\? \n \nTieni presente che questa azione riavvierà l\'app e ciò potrebbe richiedere molto tempo. - Sicuro di voler rimuovere questo target di notifica? - Sicuro di voler rimuovere %1$s %2$s? - Scegli un paese - Paese Per favore scegli un paese Numero di telefono @@ -536,27 +469,22 @@ Inserisci un codice di attivazione Errore durante la convalida del numero di telefono Codice - 3 giorni 1 settimana 1 mese Per sempre - - Icona della stanza Nome della stanza Argomento Etichetta Etichetta come: - Preferito Bassa priorità Nessuna etichetta - Accesso e visibilità Mostra questa stanza nell\'elenco delle stanze pubbliche @@ -564,22 +492,18 @@ Accesso alla Timeline Chi può leggere la Timeline\? Chi può entrare in questa stanza? - Chiunque Solo i membri (dal momento in cui questa opzione è stata selezionata) Solo i membri (dal momento in cui vengono invitati) Solo i membri (dal momento in cui entrano nella stanza) - Una stanza deve avere un indirizzo per poter essere linkata. Solo le persone che sono state invitate Chiunque conosca il link della stanza, eccetto gli ospiti Chiunque conosca il link della stanza, compresi gli ospiti - Utenti bannati - Avanzate ID interno della stanza @@ -591,35 +515,27 @@ Devi disconnetterti per abilitare la crittografia. Cripta solo per le sessioni verificate Non inviare mai da questa sessione messaggi cifrati verso sessioni in questa stanza non verificate. - Questa stanza non ha indirizzi locali Nuovo indirizzo stanza (es. #foo:matrix.org) - Il formato del nome della stanza non è corretto \'%s\' non è un formato valido per un nome di stanza Non avrai un indirizzo principale specifico per questa stanza. Avvisi per l\'indirizzo principale - Imposta come indirizzo principale Non usare più come indirizzo principale Copia ID stanza Copia indirizzo stanza - Crittografia abilitata in questa stanza. Crittografia disabilitata in questa stanza. Abilita crittografia\n(attenzione: non può più essere disabilitata!) - Elenco Tema - %s stava cercando di caricare un punto specifico tra i messaggi passati di questa stanza, ma non l\'ha trovato. - Informazioni sulla crittografia E2E - Informazioni sull\'evento ID utente Chiave identità Curve25519 @@ -627,7 +543,6 @@ Algoritmo ID sessione Errore di decriptazione - Informazioni sessione del mittente Nome pubblico Nome pubblico @@ -635,7 +550,6 @@ Chiave sessione Verifica Impronta digitale Ed25519 - Esporta le chiavi di crittografia delle stanze Esporta le chiavi delle stanze Esporta le chiavi in un file locale @@ -645,31 +559,25 @@ Le chiavi di crittografia della stanza sono state salvate su \'%s\'. \n \nAttenzione: se si disinstalla quest\'applicazione il file, viene eliminato. - Importa le chiavi di crittografia della stanza Importa le chiavi della stanza Importa le chiavi da un file locale Importa Cripta solo per le sessioni verificate Non inviare mai da questa sessione messaggi cifrati verso sessioni non verificate. - NON verificato Verificato Metti in lista nera - sessione sconosciuta niente - Conferma Rimuovi la conferma Lista nera Togli dalla lista nera - Verifica la sessione Conferma confrontando la seguente con le impostazioni utente della tua altra sessione: Se non corrispondono, la sicurezza delle tue comunicazioni potrebbe essere compromessa. Ho verificato che le chiavi corrispondono - La stanza contiene sessioni sconosciute Questa stanza contiene sessioni sconosciute che non sono state verificate. @@ -677,7 +585,6 @@ \nTi consigliamo di verificare ogni sessione prima di continuare, ma se lo preferisci, è comunque possibile inviare il messaggio anche senza la verifica. \n \nSessioni sconosciute: - Scegli un elenco di stanze Il server potrebbe essere non disponibile o sovraccarico @@ -685,10 +592,8 @@ URL dell\'Home Server Tutte le stanze sull\'Home Server %s Tutte le stanze native %s - Cerca tra i messaggi passati - Grandezza font Minuscolo @@ -701,14 +606,12 @@ Tema Chiaro Tema Scuro Tema Nero - Sincronizzazione… Suono delle notifiche Mostra gli orari in formato 12 ore Devi avere il permesso per poter gestire i widget in questa stanza Avvia una conferenza usando Jitsi Vuoi davvero eliminare questo widget dalla stanza\? - Impossibile creare il widget. L\'invio della richiesta è fallito. @@ -719,61 +622,46 @@ Aggiungi un Widget Fai una foto Fai un video - Messaggi contenenti il nome mostrato Messaggi contenenti il nome utente - Notifiche silenziose - Notifiche audio Rilevazione eventi Segnalazione errore - Chiama Messaggio criptato - Dettagli sulla comunità - Caricamento… - Esci Azioni Comunità - Cerca tra le e comunità - Invita Comunità Nessun gruppo - Per segnalare un errore agita il dispositivo con rabbia - Sicuro di voler avviare una nuova chat con %s\? Sicuro di voler fare una chiamata audio\? Sicuro di voler fare una chiamata video\? - Elenco gruppi - Elenco dei membri Apri descrizione Sincronizzazione… - 1 utente attivo + %d utente attivo %d utenti attivi - 1 utente + %d utente %d utenti Bandire l\'utente lo espellerà dalla stanza e gli impedirà di rientrare. - - 1 nuovo messaggio + %d nuovo messaggio %d nuovi messaggi - - 1 stanza + %d stanza %d stanze @@ -784,101 +672,81 @@ Tutti i messaggi Solo le citazioni Silenzioso - Aggiungi scorciatoia sulla schermata iniziale - + Aggiungi alla schermata iniziale Anteprima degli URL Vibra quando menzionano un utente - Statistiche - Notifiche Nuovo ID della comunità (es. +foo:matrix.org) L\'ID comunità non è valido \'%s\' non è un ID comunità valido - - - 1 messaggio notificato non letto + %d messaggio notificato non letto %d messaggi notificati non letti - 1 messaggio notificato non letto + %d messaggio notificato non letto %d messaggi notificati non letti - 1 stanza + %d stanza %d stanze %1$s in %2$s - La creazione del widget è fallita - 1 widget attivo + %d widget attivo %d widget attivi - La stanza %s non è visibile. Usa la fotocamera di sistema - Hai aggiunto una nuova sessione \'%s\' che sta richiedendo le chiavi crittografiche. La tua sessione non verificata \'%s\' sta chiedendo le chiavi crittografiche. Avvia la verifica Condividi senza verificare Ignora la richiesta - Attenzione! Le conferenze sono in fase di sviluppo e potrebbero non essere affidabili. - Errore di comando Comando non riconosciuto: %s - Spento Rumoroso - Crea Crea una comunità Nome della comunità Esempio ID comunità esempio - Home Stanze Nessun utente - Stanze Si è unito Invitato Cerca tra i membri del gruppo Cerca tra le stanze del gruppo - L\'amministratore non ha fornito una descrizione estesa di questa comunità. - %2$s ti ha buttato fuori da %1$s %2$s ti ha bannato da %1$s Motivo: %1$s Rientra Dimentica la stanza - Avatar - Predisposizione - Questa stanza non mostra predisposizione per alcuna comunità Il livello di potere deve essere un intero positivo. Avatar di avviso Avatar di ricezione - 1 cambio d\'appartenenza + %d cambio d\'appartenenza %d cambi d\'appartenenza - Privacy delle notifiche Normale Privacy ridotta @@ -888,32 +756,25 @@ • Il contenuto del messaggio di una notifica è emesso direttamente dall\'Home Server Matrix • Le notifiche contengono metadati e contenuto del messaggio • Le notifiche non mostreranno il contenuto del messaggio - Privacy delle notifiche Element può esser sempre attivo in background in modo da gestire le tue notifiche in modo costante e sicuro. Ciò può influire sulla durata della batteria. Concedi l\'autorizzazione Scegli un\'altra opzione - Invia uno sticker - Invia sticker Non ci sono pacchetti di sticker attivi. \n \nVuoi aggiungerne qualcuno\? - Disattiva l\'account Disattiva il mio account - Invia le statistiche di utilizzo Element raccoglie statistiche anonime per permettere il miglioramento dell\'applicazione. Attiva le statistiche per aiutare a migliorare Element. Sì, voglio aiutare! - Manca un parametro indispensabile. Uno dei parametri non è valido. Per continuare ad usare l\'Home Server %1$s devi leggerne e accettarne i termini di servizio. Leggi ora - Disattiva l\'account Ciò renderà il tuo account inutilizzabile per l\'eternità. Non potrai più accederci e nessuno potrà ri-registrare lo stesso ID utente. Il tuo account uscirà da tutte le stanze in cui si trova attualmente e di dettagli del tuo account saranno rimossi dall\'Identity Server. Questa azione è irreversibile. \n @@ -923,38 +784,26 @@ Per favore dimenticate tutti i messaggi che ho inviato fino al momento della disattivazione del mio account (Attenzione: in questo modo i nuovi utenti potrebbero vedere delle conversazioni incomplete) Per continuare, inserisci la tua password: Disattiva l\'account - Licenze di terze parti - Scarica Parla Svuota Richiedi di nuovo le chiavi di crittografia dalle tue altre sessioni. - La richiesta della chiave è stata inviata. - Richiesta inviata Avvia Element su un altro dispositivo che possa decifrare il messaggio, in modo che possa inviare le chiavi a questa sessione. - Digita qui… - Invia messaggio vocale - prosegui con… Spiacenti, ma non è stata trovata alcuna applicazione esterna per completare l\'azione. - Invia messaggi vocali - Persone Per favore, inserisci la tua password. - Se possibile, scrivi in inglese, grazie. Invia risposta criptata… Risposta non criptata… Mostra file multimediale prima dell\'invio - Al momento non fai parte di alcuna comunità. - Usa il tasto invio per spedire i messaggi Mostra l\'azione Banna utente con l\'ID specificato @@ -969,97 +818,77 @@ Cambia il tuo nome visualizzato Markdown attivo / spento Per correggere la gestione dei widget - Questa stanza è stata sostituita da un\'altra e non è più attiva La conversazione continua qui Questa stanza contiene una conversazione cominciata altrove Clicca qui per vedere i messaggi precedenti - Non hai permessi sufficienti per effettuare questa azione. - 1s + %ds %ds - 1m + %dm %dm - 1o - %do + %dh + %dh - 1g - %dg + %dd + %dd - %1$s ora %1$s %2$s fa - "%1$s, " %1$s e %2$s %1$s %2$s - - 1 selezionato + %d selezionato %d selezionati - 1 membro - %d membri + %d utente + %d utenti - - 1 stanza + %d stanza %d stanze Avvisi di sistema - Limite di risorse superato Contatta l\'amministratore - contatta l\'amministratore del servizio - L\'Home Server ha superato uno dei limiti delle risorse, pertanto alcuni utenti non potranno accedere. L\'Home Server ha superato uno dei limiti delle risorse. - L\'Home Server ha raggiunto il limite mensile di utenti attivi, pertanto alcuni utenti non potranno accedere. L\'Home Server ha raggiunto il limite mensile di utenti attivi. - Per favore %s per aumentare questo limite. Per favore %s per continuare ad usare questo servizio. - Errore - Aumenta la performance caricando i dati dei membri della stanza solo quando questi si fan vedere. Il tuo Home Server non supporta ancora il caricamento differito dei membri delle stanze. Prova più avanti. - Spiacente, si è verificato un errore - Caricamento differito dei membri della stanza Tema Status.im - Versione %s Crea una password per mettere al sicuro le chiavi esportate. La stessa password dovrà essere usata per poter importare le chiavi. Crea una password Le password devono corrispondere espandi riduci - Mostra l\'area informazioni Sempre Per messaggi ed errori Solo per gli errori - %1$s: %1$s: %2$s +%d %d+ - Chiama comunque Butta fuori Motivo - Se il tuo Home Server supporta questa funzione, all\'interno delle chat verrà visualizzata un\'anteprima dei link postati. Invia notifiche di digitazione Fai sapere agli altri utenti che stai scrivendo. @@ -1074,20 +903,15 @@ Password Avvia la fotocamera di sistema invece della fotocamera di Element. Questa opzione richiede un\'applicazione di terze parti per registrare i messaggi. - Il comando \"%s\" necessita di più parametri, oppure alcuni parametri non sono corretti. Markdown è stato abilitato. Markdown è stato disabilitato. - Chiamate Usa la suoneria predefinita di Element per le chiamate in arrivo Suoneria delle chiamate in arrivo Scegli la suoneria per le chiamate: - Accetta - Per favore, leggi e accetta i termini di servizio di questo Home Server: - Diagnostica delle notifiche Diagnosi Esegui i test @@ -1095,56 +919,47 @@ La diagnostica di base risponde correttamente. Se continui a non ricevere notifiche, invia una segnalazione errore per aiutarci a indagare. Uno o più test hanno fallito, prova le soluzioni suggerite. Uno o più test hanno fallito, per favore invia un rapporto di errore per aiutarci a indagare. - Impostazioni di sistema. Le notifiche sono abilitate nelle Impostazioni di sistema. Le notifiche sono disabilitate nelle Impostazioni di sistema. \nControlla le Impostazioni di sistema. Apri le Impostazioni - Impostazioni account. Le notifiche sono abilitate per il tuo account. Le notifiche sono disabilitate per il tuo account. \nControlla le impostazioni dell\'account. Abilita - Impostazioni sessione. Le notifiche sono attive per questa sessione. Le notifiche non sono attive per questa sessione. \nControlla le impostazioni di Element. Abilita - Esegui un controllo dei servizi L\'APK Google Play Services è disponibile e aggiornato. Element usa Google Play Services per consegnare i messaggi a comparsa, ma sembra non sia stato configurato correttamente: \n%1$s Correggi i Play Services - Token di Firebase Token FCM recuperato con successo: -%1$s - Il recupero del Token FCM: %1$s è fallito - +\n%1$s + Recupero fallito del token FCM: +\n%1$s Registrazione Token Token FCM registrato con successo sull\'Home Server. E\'fallita la registrazione del Token FCM sull\'Home Server: \n %1$s - Servizio di notifiche Servizio di notifiche in esecuzione. Servizio di notifiche non in esecuzione. \nProva a riavviare l\'applicazione. Avvia il servizio - Auto riavvio del servizio di notifiche Il servizio è stato chiuso e si è riavviato automaticamente. L\'avvio del servizio è fallito - Esegui all\'avvio Il servizio inizierà quando il dispositivo sarà riavviato. Il servizio non partirà al riavvio del dispositivo. Non riceverai notifiche finché Element non verrà aperto almeno una volta. Abilita l\'esecuzione all\'avvio - Verifica se Element sia stato configurato per funzionare in modo limitato quando lavora in background Element non funziona senza alcuna restrizione anche quando è eseguito in background. Questo test andrebbe eseguito usando dati mobili (non WIFI). \n%1$s @@ -1152,33 +967,24 @@ \nIl funzionamento dell\'App, quando è eseguita in background, è stato fortemente limitato e ciò potrebbe influenzare la ricezione delle notifiche. \n%1$s Non limitare - Ottimizzazione della batteria Element non è influenzato dall\'ottimizzazione della batteria. Se si lascia un dispositivo scollegato, fermo e con lo schermo spento, dopo un certo tempo questo entra in modalità Doze. Ciò impedisce alle App di accedere alla rete e ritarda le attività, le sincronizzazioni e la ricezione dei normali allarmi. Ignora l\'ottimizzazione - Connessione in background Per poter ricevere le notifiche in tempo reale, Element deve potersi sempre connettere. Anche quando funziona in background. \nNella schermata successiva ti verrà chiesto di consentire a Element di funzionare anche quando è in background, accetta per favore. Concedi il permesso - Si è verificato un errore durante la verifica dell\'indirizzo email. - Si è verificato un errore durante la verifica del numero di telefono. Ulteriori informazioni: %s - Non è stato trovato nessun APK Google Play Services valido. Le notifiche non funzioneranno correttamente. - Chiamata video in corso… - Backup delle chiavi Usa il Backup delle chiavi Il Backup delle chiavi non è concluso, attendere prego… - Salta Fatto - Impostazioni di notifica avanzate Se ti disconnetti adesso perderai i tuoi messaggi cifrati Il Backup delle chiavi è in corso. Se ti disconnetti adesso perderai i tuoi messaggi cifrati. @@ -1189,23 +995,19 @@ Sei sicuro\? Backup Perderai l\'accesso ai tuoi messaggi cifrati a meno che tu non faccia il Backup delle chiavi prima di disconnetterti. - Rimani Interrompi Ignora - Sei sicuro di volerti disconnettere\? Accedi con single sign-on Questo URL è irraggiungibile. Per favore controllalo - "Il tuo dispositivo usa un protocollo di sicurezza TLS obsoleto e vulnerabile agli attacchi. Per la tua sicurezza, ti viene impedito di connetterti" + Il tuo dispositivo usa un protocollo di sicurezza TLS obsoleto e vulnerabile agli attacchi. Per la tua sicurezza, ti viene impedito di connetterti Importanza delle notifiche in base al tipo - Impostazioni personalizzate. Nota che alcuni tipi di messaggio sono impostati come silenziosi (ossia generano notifiche senza suono). Alcune notifiche sono disattivate nelle tue impostazioni personalizzate. Il caricamento delle regole personalizzate è fallito, riprova. Controlla le Impostazioni - [%1$s] \nQuesto errore non dipende da Element. Secondo Google dipende dal fatto che questo dispositivo ha troppe App registrate con FCM. L\'errore si verifica solo in casi in cui ci sia un numero estremo di app, quindi non dovrebbe affliggere l\'utente medio. [%1$s] @@ -1213,34 +1015,25 @@ [%1$s] \nQuesto errore non dipende da Element. Non c\'è alcun account Google nel telefono. Apri il gestore di account ed aggiungi un account Google. Aggiungi account - Configura le notifiche rumorose Configura le notifiche di chiamata Configura le notifiche silenziose Scegli il colore del LED, la vibrazione, il suono… - - Gestione chiavi crittografiche Invia il messaggio col tasto Invio Il tasto Invio della tastiera, invece di andare a capo invierà il messaggio - La modalità risparmio dati applica un filtro specifico che esclude gli aggiornamenti di presenza e le notifiche di scrittura. - Aggiorna la password La password non è valida Le password non corrispondono - Ripristino dei messaggi cifrati Gestisci il Backup delle chiavi - Silenzioso Inserisci un nome utente. Inserisci una password La password è troppo debole - Cancella la password se vuoi che Element generi un codice di recupero. Non c\'è alcuna sessione Matrix disponibile - Non perdere mai i messaggi cifrati I messaggi nelle stanze cifrate sono protetti con crittografia E2E. Solo tu e il/i destinatario/i avete le chiavi crittografiche per leggere questi messaggi. \n @@ -1248,7 +1041,6 @@ Inizia ad usare il Backup delle chiavi crittografiche (Avanzato) Esporta manualmente le chiavi crittografiche - Proteggi il tuo backup con una password. Una copia cifrata delle tue chiavi crittografiche sarà salvata sul tuo Home Server. Proteggi il tuo Backup con una password per tenerlo al sicuro. \n @@ -1270,7 +1062,6 @@ Il codice di recupero è stato salvato in \'%s\'. \n \nAttenzione: se l\'applicazione su cui è salvato venisse disinstallata, il codice di recupero verrà perso. - Si prega di farne una copia Condividi il codice di recupero con… Generazione del codice di recupero usando la password. Questo processo può durare alcuni secondi. @@ -1278,25 +1069,18 @@ Errore inatteso Backup avviato Il tuo Home Server sta facendo il Backup delle tue chiavi crittografiche in background. Il primo Backup può impiegare diversi minuti. - - Sei sicuro\? Se ti disconnetti o perdessi questo dispositivo potresti perdere l\'accesso ai tuoi messaggi. - Verifica versione backup… Usa la tua password di recupero per sbloccare i messaggi cifrati usa il tuo codice di recupero Se non conosci la tua password di recupero, puoi %s. - Usa il tuo codice di recupero per sbloccare la Timeline dei messaggi cifrati Inserisci codice di recupero - Ripristino messaggio - Hai perso il codice di recupero\? Nelle Impostazioni puoi crearne uno nuovo. Impossibile decifrare il backup con questa password: verifica che la password di recupero inserita sia corretta. Errore di rete: controlla la tua connessione e riprova. - Ripristino Backup: Codice di recupero in elaborazione… Download chiavi… @@ -1304,7 +1088,6 @@ Sblocca Timeline Inserisci un codice di recupero Impossibile decifrare il backup con questo codice di recupero: verifica di avere inserito il codice di recupero corretto. - Backup ripristinato %s ! Ripristinato un Backup con %d chiave. @@ -1314,18 +1097,13 @@ %d nuova chiave è stata aggiunta a questa sessione. %d nuove chiavi sono state aggiunte a questa sessione. - Rilevazione ultima versione codici di recupero fallita (%s). La cifratura della sessione non è stata attivata - - Ripristina da backup Elimina backup - Il backup delle chiavi è stato impostato correttamente per questa sessione. Il backup delle chiavi non è attivo su questa sessione. Questa sessione non sta facendo il backup delle chiavi. - Il backup ha una firma da una sessione sconosciuta con ID %s. Il backup ha una firma valida da questa sessione. Il backup ha una firma valida dalla sessione verificata %s. @@ -1333,14 +1111,11 @@ Il backup ha una firma non valida dalla sessione verificata %s Il backup ha una firma non valida dalla sessione non verificata %s Impossibile ottenere info di fiducia per il backup (%s). - Per usare il backup delle chiavi crittografiche su questa sessione, devi accedervi con la password o con il codice di recupero. Eliminazione backup… Eliminazione backup fallita (%s) - Elimina backup Eliminare il Backup delle chiavi crittografiche dall\'Home Server\? Non potrai più usare il codice di recupero per leggere i messaggi cifrati. - Nuovo Backup delle chiavi È stato rilevato un nuovo Backup delle chiavi crittografiche. \n @@ -1348,31 +1123,24 @@ Sono stato io Non perdere mai i messaggi cifrati Inizia ad usare il Backup delle chiavi - Non perdere mai i messaggi cifrati Usa il Backup delle chiavi - Nuove chiavi per messaggi sicuri Gestisci nel Backup delle chiavi - Backup chiavi in corso… - Il Backup delle chiavi è stato eseguito Backup di %d chiave… Backup di %d chiavi… - Versione Algoritmo Firma - Risposta Home Server non valida Opzioni autocompletamento server Element ha rilevato una configurazione server personalizzata per il tuo dominio userId \"%1$s\": \n%2$s Usa configurazione - Inizializzazione del servizio Multimedia Compressione predefinita @@ -1380,29 +1148,24 @@ Sorgente multimediale predefinita Scegli Riproduci il suono dell\'otturatore - Segna come letto L\'app non ha bisogno di connettersi in background all\'Home Server. Ciò dovrebbe ridurre il consumo della batteria - %1$s: 1 messaggio + %1$s: %2$d messaggio %1$s: %2$d messaggi %d notifica %d notifiche - Nuovo evento Stanza Nuovi messaggi Nuovo invito Io ** Invio fallito - per favore apri la stanza - Purtroppo i vecchi dispositivi (quelli con Android precedenti al 5.0) non supportano le conferenze con Jitsi - Verifica sessione - IP sconosciuto Una nuova sessione sta chiedendo le chiavi crittografiche. Nome sessione: %1$s \nUltimo accesso: %2$s @@ -1411,45 +1174,36 @@ \nNome sessione: %1$s \nUltimo accesso: %2$s \nSe non hai effettuato l\'accesso da un\'altra sessione, ignora questa richiesta. - Verifica Condividi Richiesta condivisione chiavi crittografiche Ignora - Verifica confrontando una breve stringa. Per la massima sicurezza, consigliamo di farlo di persona o di utilizzare un altro metodo di comunicazione fidato. Inizia la verifica Richiesta di verifica in arrivo Verifica questa sessione per segnarla come fidata. Verificare le sessioni dei partner ti dà una maggiore tranquillità quando usi messaggi cifrati end-to-end. Verificare questa sessione la segnerà come fidata e segnerà anche la tua sessione come fidata per il tuo partner. - Verifica questa sessione confermando che le seguenti emoji appaiono sullo schermo del partner Verifica questa sessione confermando che i seguenti numeri appaiono sullo schermo del partner - Hai ricevuto una richiesta di verifica. Vedi la richiesta In attesa che il partner confermi… - Verificato! Hai verificato correttamente questa sessione. I messaggi sicuri con questo utente sono cifrati end-to-end e impossibili da leggere da parte di terze parti. Capito - Non compare nulla\? Non tutti i client supportano già la verifica interattiva. Usa la verifica classica. Usa la verifica classica. - Verifica chiave Richiesta annullata L\'altra parte ha annullato la verifica. \n%s La verifica è annullata. \nMotivo: %s - Verifica sessione interattiva Richiesta di verifica %s vuole verificare la tua sessione - L\'utente ha annullato la verifica Il processo di verifica è scaduto La sessione non sa della transazione @@ -1461,23 +1215,18 @@ Le chiavi non corrispondono Utente non corrispondente Errore sconosciuto - Esiste già un backup sul tuo Home Server Sembra tu abbia già impostato il backup delle chiavi crittografiche da un\'altra sessione. Vuoi sostituirlo con il backup che stai creando\? Sostituisci Ferma - Controllo stato del backup Sei stato disconnesso a causa di credenziali non valide o scadute. - Modifica Rispondi - Riprova Entra in una stanza per iniziare a usare l\'app. Ti ha inviato un invito Invitato da %s - Non hai più messaggi non letti Benvenuti a casa! Recupera i messaggi non letti qui @@ -1485,19 +1234,15 @@ Le tue conversazioni dirette verranno mostrate qui Stanze Le tue stanze verranno mostrate qui - Reazioni D\'accordo Piace Aggiungi reazione Vedi le reazioni Reazioni - Evento eliminato dall\'utente Evento moderato da un admin della stanza Ultima modifica di %1$s il %2$s - - Evento malformato, impossibile visualizzarlo Crea nuova stanza Nessuna rete. Controlla la tua connessione internet. @@ -1505,13 +1250,10 @@ Cambia rete Attendere prego… Tutte le comunità - Anteprima non disponibile per questa stanza L\'anteprima di stanze leggibili da tutti non è ancora supportata in Element - Stanze Messaggi diretti - Nuova stanza CREA Nome stanza @@ -1519,18 +1261,13 @@ Tutti potranno entrare in questa stanza Elenco stanze Pubblica questa stanza nell\'elenco stanze - Si è verificato un errore nell\'ottenere informazioni sulla fiducia Si è verificato un errore nell\'ottenere i dati dal Backup delle chiavi - Importa le chiavi di crittografia dal file \"%1$s\". - Versione SDK Matrix Altre note di terze parti Stai già visualizzando questa stanza! - Reazioni rapide - Generali Preferenze Sicurezza e privacy @@ -1538,104 +1275,77 @@ Regole di push Nessuna regola di push definita Nessun gateway di push registrato - id_app: chiave_push: nome_visualizzato_app: nome_sessione: Url: Formato: - Audio e Video Aiuto e informazioni - - Registra token - Suggerisci qualcosa Scrivi qua sotto il tuo suggerimento. Descrivi qui il tuo suggerimento Grazie, il suggerimento è stato inviato correttamente L\'invio del suggerimento è fallito (%s) - Mostra gli eventi nascosti nella Timeline - Non hai nulla di nuovo da vedere! Messaggi diretti - In attesa… Criptazione miniatura… Invio miniatura (%1$s / %2$s) Criptazione file… Invio file (%1$s / %2$s) - Scaricamento file %1$s… Il file %1$s è stato scaricato! - (modificato) - - Modifiche messaggio Nessuna modifica trovata - Filtra conversazioni… Non trovi quello che cerchi\? Crea un nuova stanza Invia un nuovo messaggio diretto Vedi l\'elenco delle stanze - Nome o ID stanza (#esempio:matrix.org) - Attiva swipe per rispondere nella timeline - Collegamento copiato negli appunti - Gestore dell\'integrazione - Non è stato configurato nessun Integration Manager. Aggiungi per ID utente Creare una stanza … Nessun risultato trovato. Usa \"Aggiungi per ID utente\" per cercare sul server. Inizia a digitare per ottenere risultati Cerca per nome o ID utente … - Sto entrando nella stanza … - Visualizza Modifica Timeline - Leggi Rifiuta - Per continuare devi accettare i termini di servizio. - Termini di servizio Leggi i termini di servizio Fatti trovare dagli altri utenti Usa bot, bridge, widget e pacchetti di sticker - Leggi su - Nessuno Revoca Disconnetti Non è stato configurato alcun Identity Server. - Chiamata fallita a causa di un\'errata configurazione del server Chiedi all\'amministratore del tuo Home Server (%1$s) di configurare un server TURN affinché le chiamate funzionino in modo affidabile. \n \nOppure puoi provare ad usare il server pubblico su %2$s, ma non sarà altrettanto affidabile e potrà vedere il tuo indirizzo IP. Puoi configurare tutto questo nelle Impostazioni. Prova ad usare %s Non chiedermelo più - Imposta un\'email per il ripristino dell\'account in caso di problemi e, se vuoi, anche per farti trovare da chi conosce quell\'indirizzo email. Aggiungi un numero di telefono se vuoi farti trovare da chi lo conosce. Imposta un\'email per il ripristino dell\'account in caso di problemi. Email e telefono potranno essere usati anche per farti trovare dagli altri utenti. Imposta un\'email per il ripristino dell\'account in caso di problemi. Email e numero di telefono potranno essere usati, se lo vorrai, anche per permettere a chi li conosce di trovarti. Non è stato trovato alcun Home Server seguendo questo URL. Per favore, controllalo Permetti chiamate dal Server di appoggio - "Se il tuo Home Server non ne ha un proprio Server d\'appoggio verrà usato %s (il Server d\'appoggio verrà a conoscenza del tuo indirizzo IP durante le chiamate)" + Se il tuo Home Server non ne ha un proprio Server d\'appoggio verrà usato %s (il Server d\'appoggio verrà a conoscenza del tuo indirizzo IP durante le chiamate) Per poterlo fare, aggiungi un Identity Server nelle Impostazioni. - Modalità sync in background (Sperimentale) + Modalità sync in background Ottimizzato per la batteria Element si sincronizzerà in background in modo da non consumare la poca batteria disponibile. \nA seconda del livello della batteria, il sistema operativo potrebbe ritardare la sincronizzazione. @@ -1645,8 +1355,6 @@ Nessuna sincronizzazione in background Quando l\'App è in background non ti verranno notificati i messaggi in arrivo. L\'aggiornamento delle impostazioni è fallito. - - Intervallo preferito tra le sincronizzazioni %s \nLa sincronizzazione può essere ritardata a seconda delle risorse (batteria) o dello stato del dispositivo (sospensione). @@ -1656,9 +1364,7 @@ Il nome pubblico di una sessione è visibile alle persone con cui comunichi Non stai usando alcun Identity Server Nessun Identity Server configurato. E\' necessario per poter ripristinare la tua password. - Pare che tu stia provando a connetterti ad un altro homeserver. Vuoi disconnetterti da qui\? - Identity Server Disconnetti Identity Server Configura Identity Server @@ -1672,28 +1378,20 @@ Numeri di telefono visibili pubblicamente Abbiamo inviato un\'email di conferma a %s, controlla l\'email e clicca sul link di conferma In attesa - Inserisci un URL di server di identità Impossibile connettersi all\'Identity Server Inserisci l\'URL dell\'Identity Server L\'Identity Server non ha reso noti i propri termini di servizio L\'Identity Server che hai scelto non ha freso noti i propri termini di servizio. Continua solo se ti fidi È stato inviato un messaggio a %s. Inserisci il codice di verifica che contiene. - In questo momento stai condividendo i tuoi indirizzi email o numeri di telefono sull\'Identity Server %1$s. Dovrai riconnetterti a %2$s per interromperne la condivisione. Accetta i termini di servizio dell\'Identity Server (%s) per permettere ad altri utenti di trovarti tramite la tua email o numero di telefono. Latn - Attiva i log dettagliati. I log dettagliati aiuteranno gli sviluppatori fornendo loro molte più informazioni nelle segnalazioni che invii scuotendo il dispositivo. Anche se attivi i log dettagliati, Element non registra mai i contenuti dei messaggi o altri dati personali. - - Riprova dopo avere accettato i termini di servizio del tuo Home Server. - Sembra che il server stia impiegando troppo tempo a rispondere. Ciò può essere causato da una cattiva connessione o da un errore del server. Riprova fra qualche minuto. - Invia allegato - Apri il pannello di navigazione Apri il menu \"Crea nuova stanza\" Chiudi il menu di \"Crea nuova stanza\"… @@ -1703,17 +1401,14 @@ Mostra password Nascondi password Salta in fondo - %1$s, %2$s e %3$s hanno letto %1$s e %2$s hanno letto %s ha letto - 1 utente ha letto + %d utente ha letto %d utenti hanno letto - Il file \'%1$s\' (%2$s) è troppo grande da inviare. Il limite è %3$s. - Si è verificato un errore ricevendo l\'allegato. File Contatto @@ -1722,7 +1417,6 @@ Galleria Adesivo Errore nella gestione dei dati condivisi - È spam È inappropriato Segnalazione personalizzata… @@ -1730,31 +1424,25 @@ Motivo della segnalazione SEGNALA IGNORA UTENTE - Contenuto segnalato - Questo contenuto è stato segnalato. -\n -\nSe non vuoi più vedere contenuti da questo utente, puoi bloccarlo per nascondere i suoi messaggi + Questo contenuto è stato segnalato. +\n +\nSe non vuoi più vedere contenuti da questo utente, puoi ignorarlo per nascondere i suoi messaggi. Segnalato come spam - Questo contenuto è stato segnalato come spam. -\n -\nSe non vuoi più vedere contenuti da questo utente, puoi bloccarlo per nascondere i suoi messaggi + Questo contenuto è stato segnalato come spam. +\n +\nSe non vuoi più vedere contenuti da questo utente, puoi ignorarlo per nascondere i suoi messaggi. Segnalato come inappropriato - Questo contenuto è stato segnalato come inappropriato. -\n -\nSe non vuoi più vedere contenuti da questo utente, puoi bloccarlo per nascondere i suoi messaggi - + Questo contenuto è stato segnalato come inappropriato. +\n +\nSe non vuoi più vedere contenuti da questo utente, puoi ignorarlo per nascondere i suoi messaggi. Element richiede l\'autorizzazione per salvare le tue chiavi crittografiche sul disco. \n \nPermetti l\'accesso nel prossimo pop-up per poter esportare le chiavi manualmente. - In questo momento non c\'è nessuna connessione di rete - Conferma la tua password Non puoi farlo da Element mobile E\'necessaria l\'autenticazione - - Integrazioni Usa un Integration Manager per gestire bot, bridge, widget e pacchetti di sticker. \nGli Integration Manager possono ricevere dati di configurazione, modificare widget, mandare inviti alle stanze e modificare permessi a tuo nome. @@ -1769,25 +1457,20 @@ Ricarica widget Apri nel browser Revoca l\'accesso per me - Il tuo nome visualizzato L\'URL del tuo avatar Il tuo ID utente Il tuo tema ID widget ID stanza - - Questo widget vuole usare le seguenti risorse: Permetti Blocca tutto Usare la fotocamera Usa il microfono Leggi media protetti da DRM - Questo non è un indirizzo di server Matrix valido Ignora utente - Tutti i messaggi (rumoroso) Tutti i messaggi Solo citazioni @@ -1798,22 +1481,16 @@ Invia il messaggio come spoiler Spoiler Digita parole chiave per trovare una reazione. - Non stai ignorando alcun utente - Tieni premuto su una stanza per altre opzioni - - %1$s ha reso pubblica la stanza a chiunque conosca il collegamento. %1$s ha reso la stanza solo ad invito. Messaggi non letti - È la tua conversazione. Tienitela. Chatta con persone direttamente o in gruppi Tieni private le conversazioni con la cifratura Estendi e personalizza la tua esperienza Inizia - Seleziona un server Proprio come le email, gli account hanno una sola origine, ma puoi parlare con chiunque Unisciti a milioni gratuitamente sul server pubblico più grande @@ -1821,7 +1498,6 @@ Maggiori info Altro Impostazioni personalizzate ed avanzate - Continua Connetti a %1$s Connetti a Element Matrix Services @@ -1830,13 +1506,11 @@ Registrati Accedi Continua con SSO - Indirizzo Element Matrix Services Indirizzo Hosting premium per organizzazioni Inserisci l\'indirizzo del Element Modular o del server che vuoi usare Inserisci l\'indirizzo di un server o di un Element a cui vuoi connetterti - Si è verificato un errore caricando la pagina: %1$s (%2$d) L\'applicazione non riesce ad accedere a questo homeserver. L\'homeserver supporta i seguenti tipi di accesso: %1$s. \n @@ -1845,58 +1519,46 @@ L\'applicazione non riesce a creare un account su questo homeserver. \n \nVuoi registrarti usando un client web\? - Questa email non è associata ad alcun account. - Reimposta password su %1$s Verrà inviata un\'email di verifica nella tua posta per confermare l\'impostazione della nuova password. Avanti Email Nuova password - Attenzione! Cambiare la password reimposterà qualunque chiave di cifratura end-to-end su tutte le tue sessioni, rendendo illeggibile la cronologia delle chat criptate. Imposta il Backup Chiavi o esporta le tue chiavi della stanza da un\'altra sessione prima di reimpostare la password. Continua - Questa email non è collegata ad alcun account - Controlla la tua posta Un\'email di verifica è stata inviata a %1$s. Tocca il collegamento per confermare la tua nuova password. Una volta seguito il collegamento contenuto, clicca sotto. Ho verificato il mio indirizzo email - Successo! La tua password è stata reimpostata. Sei stato disconnesso da tutte le sessioni e non riceverai più notifiche push. Per riattivare le notifiche, riaccedi su ogni dispositivo. Torna all\'accesso - Attenzione La tua password non è ancora cambiata. \n \nFermare il processo di cambio password\? - Imposta indirizzo email Imposta un\'email per recuperare il tuo account. Più tardi potrai permettere facoltativamente alle persone che conosci di trovarti tramite la tua email. Email Email (facoltativa) Avanti - Imposta numero di telefono Imposta un numero di telefono per permettere facoltativamente alle persone che conosci di trovarti. Si prega di usare il formato internazionale. Numero di telefono Numero di telefono (facoltativo) Avanti - Conferma numero di telefono Abbiamo inviato un codice a %1$s. Inseriscilo sotto per verificare che sei tu. Inserisci codice Invia di nuovo Avanti - I numeri di telefono internazionali devono iniziare con \'+\' Il numero di telefono non sembra valido. Ricontrollalo - Registrati su %1$s Nome utente o email Password @@ -1906,27 +1568,22 @@ Il tuo account non è ancora stato creato. \n \nFermare il processo di registrazione\? - Seleziona matrix.org Seleziona Element Matrix Services Seleziona un server personalizzato Completa la verifica Captcha Accetta le condizioni per continuare - Controlla la tua email Abbiamo inviato un\'email a %1$s. \nClicca il collegamento contenuto per continuare la creazione dell\'account. Il codice inserito non è corretto. Ricontrollalo. Homeserver obsoleto Questo homerserver è di una versione troppo vecchia per connettersi. Chiedi all\'amministratore dell\'homeserver di aggiornarlo. - Sono state inviate troppe richieste. Puoi riprovare in %1$d secondo… Sono state inviate troppe richieste. Puoi riprovare in %1$d secondi… - Visto da - Sei disconnesso Può essere dovuto a vari motivi: \n @@ -1936,7 +1593,6 @@ \n \n• L\'amministratore del server ha bloccato il tuo accesso per motivi di sicurezza. Accedi di nuovo - Sei disconnesso Accedi L\'amministratore dell\'homeserver (%1$s) ti ha disconnesso dall\'account %2$s (%3$s). @@ -1948,7 +1604,6 @@ \n \nEliminali se hai finito di usare questo dispositivo, o se vuoi accedere ad un altro account. Elimina tutti i dati - Elimina i dati Eliminare tutti i dati attualmente presenti in questo dispositivo\? \nRiaccedi per avere accesso ai dati dell\'account e ai messaggi. @@ -1956,12 +1611,9 @@ Elimina i dati La sessione attuale è per l\'utente %1$s e hai fornito le credenziali per l\'utente %2$s. Ciò non è supportato da Element. \nPrima elimina i dati, poi accedi di nuovo con un altro account. - Il tuo collegamento matrix.to non è corretto La descrizione è troppo breve - Sync iniziale… - Vedi tutte le mie sessioni Impostazioni avanzate Modalità sviluppatore @@ -1973,25 +1625,19 @@ Impostazioni Sessione attuale Altre sessioni - Mostrati solo i primi risultati, digita più lettere… - Crash facile Element potrebbe crashare più spesso quando si verifica un errore imprevisto Antepone ¯\\_(ツ)_/¯ ad un messaggio testuale - Attiva la cifratura Una volta attivata, la cifratura non può essere disattivata. - Il dominio della tua email non è autorizzato alla registrazione in questo server - Accesso non fidato Corrispondono Non corrispondono Verifica questo utente confermando che le seguenti emoji appaiono sul suo schermo, nello stesso ordine. Per la massima sicurezza, usate un\'altra via di comunicazione fidata o fatelo di persona. Cerca lo scudo verde per garantire la fiducia dell\'utente. Fidati di tutti gli utenti in una stanza per garantire che essa sia sicura. - Non sicuro Uno dei seguenti potrebbe essere compromesso: \n @@ -1999,12 +1645,10 @@ \n - L\'homeserver al quale è connesso l\'utente che stai verificando \n - La tua connessione internet o quella dell\'altro utente \n - Il tuo dispositivo o quello dell\'altro utente - Video. Immagine. Audio File - In attesa… %s ha annullato Hai annullato @@ -2012,25 +1656,17 @@ Hai accettato Verifica inviata Richiesta di verifica - - Verifica questa sessione Verifica manualmente - Tu - Scansiona il codice con il dispositivo dell\'altro utente per verificarvi a vicenda Scansiona il suo codice Impossibile scansionare Se non siete di persona, confrontate invece le emoji - Verifica confrontando le emoji - Verifica via emoji Se non potete scansionare il codice sopra, verificate confrontando una breve selezione univoca di emoji. - Immagine codice QR - Verifica %s %s verificato In attesa di %s… @@ -2053,54 +1689,38 @@ File caricati Lascia stanza Uscita dalla stanza… - Amministratori Moderatori Personalizzato Inviti Utenti - Amministratore in %1$s Moderatore in %1$s Personalizzato (%1$d) in %2$s - Vai alla ricevuta di lettura - Element non gestisce eventi del tipo \'%1$s\' Element non gestisce messaggi del tipo \'%1$s\' Element ha riscontrato un errore con il rendering del contenuto dell\'evento con id \'%1$s\' - Non ignorare - Questa sessione non riesce a condividere questa verifica con le tue altre sessioni. \nLa verifica sarà salvata in locale e condivisa in una versione futura dell\'app. - Stanze recenti Altre stanze - Invia il messaggio in questione colorato ad arcobaleno Invia l\'emoticon in questione colorata ad arcobaleno - Cronologia - Editor messaggi - Attiva cifratura end-to-end Una volta attivata, la cifratura non può essere disattivata. - Attivare la cifratura\? Una volta attivata, la cifratura di una stanza non può essere disattivata. I messaggi inviati in una stanza cifrata non possono essere visti dal server, solo dai partecipanti della stanza. L\'attivazione della cifratura può impedire il funzionamento di molti bot e bridge. Attiva cifratura - Per sicurezza, verifica %s controllando un codice univoco. Per sicurezza, fatelo di persona o usate un\'altra via di comunicazione. - Confronta le emoji, assicurandoti che appaiono nello stesso ordine. Confronta il codice con quello mostrato nello schermo dell\'altro utente. I messaggi con questo utente sono cifrati end-to-end e non possono essere letti da terze parti. La tua nuova sessione ora è verificata. Ha accesso ai messaggi cifrati e gli altri utenti la vedranno come fidata. - - Firma incrociata La firma incrociata è attiva \nChiavi private nel dispositivo. @@ -2110,53 +1730,37 @@ La firma incrociata è attiva. \nLe chiavi non sono fidate La firma incrociata non è attiva - - Sessioni attive Mostra tutte le sessioni Gestisci sessioni Disconnetti questa sessione - Nessuna informazione crittografica disponibile - Questa sessione è fidata per i messaggi sicuri perché l\'hai verificata: Verifica questa sessione per segnarla come fidata e darle l\'accesso ai messaggi cifrati. Se non hai fatto l\'accesso a questa sessione il tuo account potrebbe essere compromesso: - %d sessione attiva %d sessioni attive - Verifica questo accesso Gli altri utenti potrebbero non fidarsi Completa la sicurezza - Usa una sessione esistente per verificare questa, dandole l\'accesso ai messaggi cifrati. - - Verifica Verificato Attenzione - Rilevazione sessioni fallita Sessioni Fidato Non fidato - Questa sessione è fidata per i messaggi sicuri perché %1$s (%2$s) l\'ha verificata: %1$s (%2$s) ha fatto l\'accesso con una nuova sessione: Finché questo utente non si fida di questa sessione, i messaggi inviati da e verso di essa sono etichettati con avvisi. In alternativa, puoi verificarlo manualmente. - - Inizializza la firma incrociata Reimposta chiavi - Codice QR - Quasi fatto! %s sta mostrando lo stesso scudo\? No - La connessione al server è stata persa Nome utente Strumenti Svil @@ -2173,49 +1777,36 @@ Crea un semplice sondaggio Usa una password o chiave di recupero Se non puoi accedere a una sessione esistente - Nuovo accesso - Impossibile trovare segreti nell\'archivio Inserisci la password dell\'archivio segreto Attenzione: Dovresti accedere all\'archivio segreto solo da un dispositivo fidato - Rimuovi… Vuoi inviare questo allegato a %1$s\? Invia immagine nella dimensione originale Invia immagini nella dimensione originale - Conferma rimozione Sei sicuro di volere rimuovere (eliminare) questo evento\? Nota che se elimini il nome della stanza o cambi l\'argomento, ciò potrebbe annullare la modifica. Includi un motivo Motivo della revisione - Evento eliminato da un utente, motivo: %1$s Evento moderato da un admin della stanza, motivo: %1$s - Le chiavi sono già aggiornate! - Element Android - Richieste di chiavi - Sblocca la cronologia dei messaggi cifrati - Ricarica - Nuovo accesso. Eri tu\? Tocca per controllare e verificare Usa questa sessione per verificare quella nuova, dandole l\'accesso ai messaggi cifrati. Non ero io Il tuo account potrebbe essere compromesso - Se annulli, non potrai leggere i messaggi cifrati su questo dispositivo e altri utenti non si fideranno di esso Se annulli, non potrai leggere i messaggi cifrati sul tuo nuovo dispositivo e altri utenti non si fideranno di esso Non verificherai %1$s (%2$s) se annulli adesso. Ricomincia nel suo profilo utente. - Uno dei seguenti potrebbe essere compromesso: \n \n- La tua password @@ -2224,35 +1815,25 @@ \n- La connessione internet usata da uno dei dispositivi \n \nTi consigliamo di cambiare immediatamente la password e le chiavi di recupero nelle impostazioni. - Verifica i tuoi dispositivi dalle impostazioni. Verifica annullata - Password di ripristino chiave dei messaggi password dell\'account - Imposta una %s Genera una chiave dei messaggi - Conferma la %s - Inserisci la tua %s per continuare. - Proteggi e sblocca i messaggi cifrati e fidati con una %s. Inserisci la tua %s di nuovo per confermarla. Non riutilizzare la tua password dell\'account. - - Potrebbe impiegarci qualche secondo, porta pazienza. Inizializzazione del ripristino. La tua chiave di recupero Hai finito! Tienila al sicuro Fine - Usa questa %1$s come una rete di salvataggio in caso ti dimentichi la tua %2$s. - Pubblicazione delle chiavi di identità create Generazione della chiave sicura dalla password Definizione della chiave predefinita SSSS @@ -2260,37 +1841,26 @@ Sincronizzazione della chiave utente Sincronizzazione della chiave di auto-firma Inizializzazione del backup chiavi - - La tua %2$s e la %1$s sono ora impostate. \n \nTienile al sicuro! Ti serviranno per sbloccare i messaggi cifrati e informazioni sicure se perdi tutte le tue sessioni attive. - Stampala e conservala in un posto sicuro Salvala in una penna USB o disco di backup Copiala nella tua archiviazione online - Non puoi farlo da mobile - Impostare una password dei messaggi ti consente di proteggere e sbloccare i messaggi cifrati e di fidarti. \n \nSe non vuoi impostare una password dei messaggi, genera una chiave dei messaggi. Impostare una password di ripristino ti consente di proteggere e sbloccare i messaggi cifrati e di fidarti. - - Cifratura attiva I messaggi in questa stanza sono cifrati end-to-end. Maggiori info e verifica degli utenti nel loro profilo. Cifratura non attiva La cifratura usata da questa stanza non è supportata - %s ha creato e configurato la stanza. - Quasi fatto! L\'altro dispositivo sta mostrando lo stesso scudo\? Quasi fatto! In attesa della conferma… In attesa di %s… - Importazione chiavi fallita - Configurazione delle notifiche Messaggi contenenti @room Messaggi cifrati in conversazioni private @@ -2298,24 +1868,17 @@ Quando le stanze vengono aggiornate Risoluzione problemi Imposta l\'importanza della notifica per evento - Invia un messaggio come testo semplice, senza interpretarlo come markdown - Nome utente e/o password errati. La password inserita inizia o termina con spazi, controllala. - Messaggio… - Aggiornamento cifratura disponibile Verifica te stesso e gli altri per tenere al sicuro le chat - Inserisci la tua %s per continuare Usa file - Inserisci la %s Password di ripristino Non è una chiave di ripristino valida Inserisci una chiave di ripristino - Controllo della chiave di backup Controllo della chiave di backup (%s) Rilevazione chiave di curva ellittica @@ -2324,19 +1887,15 @@ Generazione chiave SSSS dalla chiave di ripristino Memorizzazione segreto della chiave in SSSS %1$s (%2$s) - Inserisci la password del backup chiavi per continuare. usare la chiave di ripristino del backup chiavi Non conosci la password del backup chiavi, puoi %s. Chiave di ripristino del backup chiavi - Impedisci la cattura di schermate dell\'app Attivandolo verrà aggiunto FLAG_SECURE a tutte le Activity. Riavvia l\'applicazione per applicare le modifiche. - File multimediale aggiunto alla galleria Impossibile aggiungere il file multimediale alla galleria Imposta una nuova password dell\'account… - Usa l\'ultima versione di Element sui tuoi altri dispositivi, Element Web, Element Desktop, Element iOS, Element per Android o un altro client Matrix che supporti la firma incrociata Element Web \nElement Desktop @@ -2351,30 +1910,25 @@ Seleziona la tua chiave di recupero, oppure inseriscila a mano digitandola o incollando dagli appunti Impossibile decifrare il backup con questa chiave di recupero: verifica di avere inserito la chiave giusta. Accesso all\'archivio sicuro fallito - Non cifrato Cifrato da un dispositivo non verificato Controlla dove hai fatto l\'accesso Verifica tutte le tue sessioni per assicurarti che il tuo account e i messaggi siano protetti Verifica il nuovo accesso entrando nel tuo account: %1$s - Verifica manualmente con testo Verifica accesso Verifica interattivamente con emoji Conferma la tua identità verificando questo accesso da una delle tua altre sessioni, dandole l\'accesso ai messaggi cifrati. Segna come fidato - Scegli un nome utente. Scegli una password. Verifica questo collegamento Il collegamento %1$s ti sta portando ad un altro sito: %2$s. \n \nSei sicuro di volere continuare\? - Impossibile creare il messaggio diretto. Controlla gli utenti che vuoi invitare e riprova. %1$s: %2$s %1$s: %2$s %3$s - Aggiungi membri INVITA Invito utenti… @@ -2386,19 +1940,16 @@ Inviti spediti a %1$s e ad altri %2$d Impossibile invitare gli utenti. Controlla gli utenti che vuoi invitare e riprova. - Messaggio eliminato Mostra messaggi rimossi Mostra un segnaposto per i messaggi rimossi Ti abbiamo inviato un\'email di conferma a %s, prima controlla la tua posta e clicca sul link di conferma Il codice di verifica non è corretto. - MEDIA Non ci sono file multimediali in questa stanza FILE %1$s alle %2$s Non ci sono file in questa stanza - In alternativa, se hai già un account e conosci il tuo identificativo e password di Matrix, puoi usare questo metodo: Accedi con il mio identificativo di Matrix Accedi @@ -2406,13 +1957,10 @@ Identificativo utente Non è un identificativo valido. Formato previsto: \'@utente:homeserver.org\' Impossibile trovare un homeserver valido. Controlla il tuo identificativo - Modalità aereo attiva - Lingua attuale Altre lingue disponibili Caricamento lingue disponibili… - Apri condizioni di %s Disconnettere dal server di identitià %s\? Questo server di identità è obsoleto. Element supporta solo API V2. @@ -2422,26 +1970,20 @@ Per la tua privacy, Element supporta solo l\'invio di email utente e numeri di telefono in formato hash. L\'associamento è fallito. Non c\'è alcun associamento attuale con questo identificativo. - Il tuo homeserver (%1$s) propone di usare %2$s come tuo server di identità Usa %1$s In alternativa, puoi inserire un qualsiasi altro URL di server di identità Inserisci l\'URL di un server di identità Invia - Riproduci Pausa Annulla - - Copia Completato - Notifiche Telefonata di Element fallita Connessione in tempo reale fallita. \nChiedi all\'amministratore del tuo homeserver di configurare un server TURN per fare funzionare le chiamate. - Seleziona dispositivo audio Telefono Altoparlante @@ -2452,18 +1994,14 @@ Posteriore Disattiva l\'HD Attiva l\'HD - Errore SSL: l\'identità del peer non è stata verificata. Errore SSL. Telefonata attiva (%s) Torna alla telefonata - Annulla invito Vuoi declassarti\? Non potrai annullare questa modifica dato che ti stai declassando, se sei l\'ultimo utente privilegiato nella stanza sarà impossibile ottenere di nuovo i privilegi. Declassa - - Ignora utente Ignorando questo utente verranno rimossi i suoi messaggi dalle stanze che condividete. \n @@ -2481,7 +2019,6 @@ Motivo del ban Togli il ban dell\'utente Togliere il ban dell\'utente gli permetterà di rientrare nella stanza. - Backup Sicuro Gestisci Imposta il Backup Sicuro @@ -2490,54 +2027,37 @@ Proteggiti contro la perdita dell\'accesso ai messaggi e dati cifrati facendo un backup delle chiavi crittografiche sul tuo server. Genera una nuova chiave di sicurezza o imposta una nuova frase di sicurezza per il backup esistente. Ciò sostituirà la tua attuale chiave o frase. - Le integrazioni sono disattivate Attiva \'Permetti integrazioni\' nelle impostazioni per continuare. - %d utente bandito %d utenti banditi - Chiavi esportate correttamente - VEDI Widget attivi - - La chiave di recupero è stata salvata. - Backup Sicuro Proteggiti dalla perdita dei messaggi e dati crittografati - Imposta il Backup Sicuro - - Decifrazione del messaggio nella timeline fallita Aggiungi una scheda dedicata per le notifiche non lette nella schermata principale. - Aggiungi ai preferiti Rimuovi dai preferiti Non hai fatto modifiche Hai reso pubblica la stanza a chiunque conosca il link. Hai reso la stanza solo su invito. Inserisci l\'indirizzo del server che vuoi usare - Se non sai la tua password, torna indietro per reimpostarla. Adesivo - Azioni amministratore Predefinito in %1$s L\'amministratore del server ha disattivato la crittografia end-to-end in modo predefinito nelle stanze private e nei messaggi diretti. Inserisci una frase di sicurezza che conosci solo tu, usata per proteggere i segreti nel tuo server. - Se annulli ora, potresti perdere i messaggi e dati cifrati se perdi l\'accesso ai tuoi login. \n \nPuoi anche configurare il backup sicuro e gestire le tue chiavi nelle impostazioni. - Hai creato e configurato la stanza. - Questo account è stato disattivato. - Attiva la firma incrociata Impossibile salvare il file multimediale Imposta ruolo @@ -2547,9 +2067,7 @@ Accendi il microfono Ferma la fotocamera Avvia la fotocamera - Imposta il Backup Sicuro - Backup sicuro Proteggiti contro la perdita dell\'accesso ai messaggi e dati cifrati facendo un backup delle chiavi crittografiche sul tuo server. Configura @@ -2557,22 +2075,17 @@ Genera una chiave di sicurezza da conservare in qualche posto sicuro, come un gestore di password o una cassaforte. Usa una frase di sicurezza Inserisci una frase segreta che conosci solo tu e genera una chiave per backup. - Salva la chiave di sicurezza Conserva la chiave di sicurezza in qualche posto sicuro, come un gestore di password o una cassaforte. - Imposta una frase di sicurezza Inserisci una frase di sicurezza che conosci solo tu, usata per proteggere i segreti nel tuo server. Frase di sicurezza Inserisci la frase di sicurezza di nuovo per confermarla. - Salva la chiave di sicurezza Conserva la chiave di sicurezza in qualche posto sicuro, come un gestore di password o una cassaforte. - Nome stanza Argomento Hai cambiato le impostazioni della stanza correttamente - Non puoi accedere a questo messaggio In attesa del messaggio, potrebbe volerci un po\' Impossibile decifrare @@ -2581,17 +2094,12 @@ Non puoi accedere a questo messaggio perché la tua sessione non è fidata dal mittente Non puoi accedere a questo messaggio perché il mittente non ha inviato le chiavi di proposito In attesa della cronologia di crittografia - Riot ora si chiama Element! Siamo entusiasti di annunciare che abbiamo cambiato nome! La tua app è aggiornata e hai fatto l\'accesso al tuo account. CAPITO MAGGIORI INFO - element - - Salva chiave di ripristino in - Aggiungi dalla mia rubrica La tua rubrica è vuota Rubrica @@ -2599,13 +2107,10 @@ Rilevazione dei tuoi contatti… Il tuo elenco di contatti è vuoto Elenco contatti - Revoca invito Revocare l\'invito a %1$s\? - Bandito da %1$s Rimozione ban fallita - Le notifiche push sono disattivate Verifica le tue impostazioni per attivare le notifiche push Scegli un PIN di sicurezza @@ -2634,13 +2139,11 @@ %1$d/%2$d chiave importata correttamente. %1$d/%2$d chiavi importate correttamente. - Gestisci integrazioni Nessun widget attivo La stanza è stata creata, ma alcuni inviti non sono stati spediti per il seguente motivo: \n \n%s - %1$s, %2$s e %3$d altro hanno letto %1$s, %2$s e altri %3$d hanno letto @@ -2658,15 +2161,80 @@ Numeri di telefono Rimuovere %s\? Assicurati di avere cliccato il link nell\'email che ti abbiamo inviato. - Email e numeri di telefono Gestisci le email e i numeri di telefono collegati al tuo account Matrix - Codice Si prega di usare il formato internazionale (il numero deve iniziare con \'+\') Conferma la tua identità verificando questo accesso, dandogli l\'accesso ai messaggi cifrati. Spiacente, questa operazione non è ancora possibile per gli account connessi tramite Single Sign-On. - Impossibile aprire una stanza dove sei stato bandito. Impossibile trovare questa stanza. Assicurati che esista. - + Non hai il permesso di avviare una chiamata in questa stanza + + %d secondo + %d secondi + + Mostra eventi di stato dei membri della stanza + Includi eventi di invito/entrata/uscita/kick/ban e modifiche di avatar/nome. + Sondaggio + Pulsanti bot + Reagito con: %s + Conclusione della verifica + Eliminare i dati dell\'account di tipo %1$s\? +\n +\nUsalo con attenzione, può avere comportamenti imprevisti. + Il collegamento non è corretto + Mostra solo il numero di messaggi non letti in una semplice notifica. + Mostra dettagli come il nome delle stanze e il contenuto dei messaggi. + Mostra il contenuto nelle notifiche + Il codice PIN è l\'unico modo per sbloccare Element. + Attiva la biometria specifica del dispositivo, come impronte e riconoscimento facciale. + Attiva la biometria + Configura protezione + Proteggi l\'accesso usando un PIN e la biometria. + Proteggi accesso + + Mostra il dispositivo con cui puoi verificare ora + Mostra %d dispositivi con cui puoi verificare ora + + Ricomincerai senza cronologia, messaggi, dispositivi o utenti fidati + Se reimposti tutto + Fallo solo se non hai altri dispositivi con cui verificare questo. + Reimposta tutto + Dimenticato o perso tutte le opzioni di recupero\? Reimposta tutto + Sei entrato. + I messaggi in questa stanza sono cifrati end-to-end. + Esci + Impostazioni + I messaggi qui sono cifrati end-to-end. +\n +\nI tuoi messaggi sono protetti con lucchetti e solo tu e il destinatario avete le chiavi univoche per sbloccarli. + I messaggi qui non sono cifrati end-to-end. + L\'homeserver gira su una versione vecchia. Chiedi al tuo amministratore di aggiornare. Puoi continuare, ma alcune funzionalità potrebbero non funzionare correttamente. + L\'hai resa solo su invito. + %1$s l\'ha resa solo su invito. + Mostra la cronologia completa nelle stanze cifrate + %1$s e %2$s + %1$s in %2$s e %3$s + + %d invito + %d inviti + + La notifica è stata cliccata! + Clicca la notifica. Se non la vedi, controlla le impostazioni di sistema. + Visualizzazione notifica + Stai vedendo la notifica! Cliccami! + Ricezione push fallita. La soluzione può essere di reinstallare l\'applicazione. + L\'applicazione sta ricevendo il PUSH + Prova push + La ricerca in stanze cifrate non è ancora supportata. + Filtra utenti banditi + Non hai il permesso di avviare una chiamata + Non hai il permesso di avviare una chiamata di gruppo + Reimposta + Il codice PIN è richiesto ogni volta che apri Element. + È necessario il codice PIN dopo 2 minuti di inattività con Element. + Richiedi il PIN dopo 2 minuti + %s è entrato. + L\'applicazione è in attesa del PUSH + \ No newline at end of file diff --git a/vector/src/main/res/values-ja/strings.xml b/vector/src/main/res/values-ja/strings.xml index 66c6eb53d3..165ff49f7a 100644 --- a/vector/src/main/res/values-ja/strings.xml +++ b/vector/src/main/res/values-ja/strings.xml @@ -1227,4 +1227,7 @@ Matrixでのメッセージの可視性は電子メールと同様です。メ 自分自身には通話できません + マークダウン書式 + メッセージ送信前にマークダウン書式を適用します。これにより、アスタリスクを使用して斜体のテキストを表示するなどの高度な書式設定が利用できます。 + 音声とビデオ diff --git a/vector/src/main/res/values-kab/strings.xml b/vector/src/main/res/values-kab/strings.xml index 9dabcdd77a..27e8259efd 100644 --- a/vector/src/main/res/values-kab/strings.xml +++ b/vector/src/main/res/values-kab/strings.xml @@ -729,8 +729,8 @@ %dwussan - %1$S tura - Seg %1$S %2$S + %1$s tura + Seg %1$s %2$s IFECKA N UNEDBAL SIWEL @@ -2527,7 +2527,6 @@ Tuzna n tenfult (%1$s / %2$s) Rmed afraḍ i tririt deg tesnakudt - Asdukkel ur yeddi ara i tukksa n uwgelhen n yiznan deg tesnakudt Aqeddac n timagit i tferneḍ ulac ɣer-s akk tiwtilin n yimeẓla. Ur ttkemmil ara ala ma yella tettekleḍ ɣef umeẓlu Iɣmisen ɣezzifen ad ɛawnen ineflayen s umuddu n wugar n yiɣemisen mi ara tazneḍ aneqqis RageShake, ula ma yili yermed asnas ur yettazen ara agbur n yiznan neɣ isefka-nniḍen usligen. @@ -2549,4 +2548,6 @@ Taggrayt n usenqed Aseɣwen ur yemsil ara akken iwata + Ur tesɛiḍ ara tasiregt ad tebduḍ asiwel deg texxamt-a + Issedmer s: %s diff --git a/vector/src/main/res/values-pl/strings.xml b/vector/src/main/res/values-pl/strings.xml index 690849ff6a..6501b49aee 100644 --- a/vector/src/main/res/values-pl/strings.xml +++ b/vector/src/main/res/values-pl/strings.xml @@ -1,11 +1,10 @@ - + Wiadomości Pokój Ustawienia Informacje o użytkowniku Archiwum - OK Anuluj Zapisz @@ -30,15 +29,14 @@ Wideo Nie można rozpocząć połączenia, spróbuj ponownie później Ze względu na brak pewnych uprawnień, niektóre funkcje mogą nie działać… - Musisz być uprawniony, aby rozpocząć połączenie grupowe + Musisz posiadać uprawninenia, aby rozpocząć połączenie grupowe Nie można rozpocząć połączenia Informacje o sesji Połączenia grupowe nie są obsługiwane w szyfrowanych pokojach - Wyślij mimo wszystko + Wyślij mimo to i Zaproś Offline - Wyloguj się Połączenie głosowe Połączenie wideo @@ -50,23 +48,18 @@ Zamknij Skopiowano do schowka Wyłącz - Potwierdzenie Ostrzeżenie - Home Ulubione Ludzie Pokoje - Filtruj nazwy pokojów Filtruj ulubione Filtruj ludzi Filtruj nazwy pokojów - Zaproszenia Niski priorytet - Rozmowy Lokalna książka adresowa Katalog użytkowników @@ -74,7 +67,6 @@ Brak rozmów Nie udzieliłeś(-aś) uprawnienia na dostęp do listy kontaktów Brak wyników - Pokoje Katalog pokojów Brak pokojów @@ -84,7 +76,6 @@ %d użytkowników %d użytkowników - Wyślij dzienniki Wyślij dzienniki awarii Wyślij zrzut ekranu @@ -94,14 +85,11 @@ W celu zdiagnozowania problemów, logi z tego klienta zostaną wysłane wraz z tym raportem o błędzie. Ten raport o błędzie, w tym dzienniki i zrzut ekranu, nie będzie publicznie widoczny. Jeśli wolisz wysłać tylko powyższy tekst, odznacz: Wygląda na to, że potrząsasz telefonem ze zdenerwowania. Czy chcesz otworzyć ekran zgłaszania błędu\? Przy wcześniejszym użyciu aplikacja napotkała błąd. Czy chcesz zgłosić raport o nieoczekiwanym zamknięciu\? - Pomyślnie zgłoszono błąd Nie udało się zgłosić błędu (%s) Postęp (%s%%) - Wyślij do Przeczytano - Dołącz do pokoju Nazwa użytkownika Stwórz konto @@ -109,18 +97,14 @@ Wyloguj Adres serwera Szukaj - Rozpocznij nową rozmowę Rozpocznij połączenie telefoniczne Rozpocznij połączenie wideo - pl PL - Adres serwera tożsamości Wyślij pliki Zrób zdjęcie lub film - Zaloguj się Stwórz konto Wyślij @@ -166,32 +150,25 @@ Nie udało się zarejestrować: Błąd sieci Nie udało się zarejestrować Proszę wprowadzić prawidłowy adres URL - Nieprawidłowa nazwa użytkownika/hasło Nie zawiera prawidłowego JSON Wysłano zbyt wiele żądań Ta nazwa użytkownika jest już używana Nie kliknięto odnośnika z wiadomości e-mail - Przeczytaj listę odbiorców - "Wyślij jako " Oryginalny Duży Średni Mały - Anulować pobieranie? Anulować wysyłanie? %d sek. %1$d min. %2$d sek. - Wczoraj Dzisiaj - Nazwa pokoju Temat pokoju - Połączono Łączenie… Zakończono połączenie @@ -200,13 +177,10 @@ Przychodzące połączenie wideo Przychodzące połączenie głosowe W trakcie połączenia… - Połączenie multimedialne nie powiodło się Nie udało się zainicjalizować aparatu - Zrób zdjęcie lub nagraj film Nie można nagrać filmu - Informacja Element wymaga uprawnienia, aby wysyłać i zapisywać pliki multimedialne. @@ -214,43 +188,34 @@ Przyznaj dostęp w następnym oknie. Element wymaga uprawnienia, aby wykonywać zdjęcia i nawiązywać połączenia wideo. Element wymaga uprawnienia, aby przeprowadzić połączenie audio. Nie można wykonać operacji, ze względu na brak wymaganych uprawnień - Zapisano Zapisać do pobranych? TAK NIE Kontynuuj - Usuń Dołącz Podgląd Odrzuć - Przejdź do pierwszej nieprzeczytanej wiadomości. - Zostałeś(-aś) zaproszony(-a) do tego pokoju przez %s Próbujesz uzyskać dostęp do %s. Czy chcesz dołączyć do pokoju, aby wziąć udział w dyskusji? pokój To jest podgląd pokoju. Interakcje zostały zablokowane. - Nowa rozmowa Dodaj członka 1 członek - Opuść pokój Czy na pewno chcesz opuścić pokój? Czy na pewno chcesz usunąć %s z rozmowy? Utwórz - Online Offline Bezczynny(-a) - NARZĘDZIA ADMINISTRACYJNE DZWOŃ WIADOMOŚCI BEZPOŚREDNIE SESJE - Zaproś Opuść pokój Usuń z pokoju @@ -265,16 +230,13 @@ Przyznaj dostęp w następnym oknie. Wspomnij Pokaż listę sesji Czy na pewno chcesz zaprosić %s do tej rozmowy? - Zaproś przez ID LOKALNE KONTAKTY (%d) KATALOG UŻYTKOWNIKÓW (%s) Tylko użytkownicy Matrixa - Zaproś użytkowników po ID Wprowadź jeden lub więcej adresów e-mail lub ID Matrix E-mail lub ID Matrix - Szukaj %s pisze… %1$s i %2$s piszą… @@ -288,28 +250,24 @@ Przyznaj dostęp w następnym oknie. Usuń niewysłane wiadomości Nie znaleziono pliku Nie masz uprawnień, aby pisać w tym pokoju - Ufaj Nie ufaj Wyloguj Ignoruj Odcisk palca (%s): Nie można zweryfikować tożsamości serwera. - Szczegóły pokoju Ludzie Pliki Ustawienia ZAPROSZENI DOŁĄCZENI - Powód zgłoszenia zawartości Czy chcesz ukrywać wszystkie wiadomości od tego użytkownika? Zauważ, że ta czynność spowoduje ponowne uruchomienie aplikacji i może to trochę potrwać. Anuluj wysyłanie Anuluj pobieranie - Szukaj Filtruj członków pokoju Brak wyników @@ -317,7 +275,6 @@ Zauważ, że ta czynność spowoduje ponowne uruchomienie aplikacji i może to t WIADOMOŚCI LUDZIE PLIKI - DOŁĄCZ KATALOG ULUBIONE @@ -329,23 +286,19 @@ Zauważ, że ta czynność spowoduje ponowne uruchomienie aplikacji i może to t Dołącz do pokoju Dołącz do pokoju Wprowadź ID lub nazwę pokoju - Przeglądaj katalog Przeszukiwanie katalogu… - Dodaj do ulubionych Usuń z priorytetyzowanych Rozmawiaj bezpośrednio Opuść rozmowę Zapomnij - Wiadomości Ustawienia Wersja Warunki użytkowania Prawa autorskie Polityka prywatności - Obraz profilowy Wyświetlana nazwa E-mail @@ -353,21 +306,17 @@ Zauważ, że ta czynność spowoduje ponowne uruchomienie aplikacji i może to t Numer telefonu Dodaj numer telefonu Informacje o aplikacji - Włącz powiadomienia dla tego konta Włącz powiadomienia dla tej sesji Podświetl ekran na 3 sekundy - Wiadomości bezpośrednie Wiadomości w rozmowach grupowych Kiedy zostanę zaproszony(-a) do pokoju Zaproszenia do rozmów Wiadomości od botów - Rozpocznij przy uruchomieniu systemu Synchronizacja w tle Włącz synchronizację w tle - Wersja Zasady użytkowania Prawa autorskie @@ -387,7 +336,6 @@ Zauważ, że ta czynność spowoduje ponowne uruchomienie aplikacji i może to t Sesje Pokaż czas wysłania dla wszystkich wiadomości Tryb oszczędzania danych - Szczegóły o sesji ID Nazwa publiczna @@ -399,16 +347,13 @@ Wprowadź hasło, aby kontynuować. Uwierzytelnianie Hasło: Wyślij - Zalogowany jako Interfejs użytkownika Język Wybierz język - Oczekiwanie na weryfikację Ten e-mail jest już używany. Ten numer telefonu jest już używany. - Zmień hasło Bieżące hasło Nowe hasło @@ -418,9 +363,7 @@ Wprowadź hasło, aby kontynuować. Pokazywać wszystkie wiadomości od %s? Zauważ, że ta czynność spowoduje ponowne uruchomienie aplikacji i może to trochę potrwać. - Wybierz kraj - Kraj Wybierz kraj Numer telefonu @@ -428,17 +371,14 @@ Zauważ, że ta czynność spowoduje ponowne uruchomienie aplikacji i może to t Wprowadź kod aktywacyjny Wystąpił błąd podczas weryfikowania numeru telefonu Kod - 3 dni 1 tydzień 1 miesiąc Na zawsze - Obraz pokoju Nazwa pokoju Temat Tag pokoju - Ulubione Niski priorytet Nic @@ -448,11 +388,9 @@ Zauważ, że ta czynność spowoduje ponowne uruchomienie aplikacji i może to t Dostęp do historii pokoju Kto może czytać historię? Kto może uzyskać dostęp do pokoju? - Każdy Tylko dla zaproszonych Zablokowani użytkownicy - Zaawansowane Wewnętrzne ID tego pokoju Adresy @@ -461,18 +399,15 @@ Zauważ, że ta czynność spowoduje ponowne uruchomienie aplikacji i może to t Szyfrowanie End-to-End Szyfrowanie End-to-End jest aktywne Nowy adres (np. #foo:matrix.org) - Ustaw jako główny adres Kopiuj ID pokoju Kopiuj adres pokoju - Szyfrowanie jest włączone w tym pokoju. Szyfrowanie jest wyłączone w tym pokoju. Włącz szyfrowanie (ostrzeżenie: nie może zostać wyłączone!) Katalog Motyw - ID użytkownika Algorytm ID sesji @@ -481,31 +416,25 @@ Zauważ, że ta czynność spowoduje ponowne uruchomienie aplikacji i może to t ID Weryfikacja Odcisk palca Ed25519 - Eksportuj Wprowadź hasło Potwierdź hasło Nie zweryfikowano Zweryfikowano Na czarnej liście - nieznana sesja brak - Weryfikuj Dodaj na czarną listę Usuń z czarnej listy - Weryfikuj sesję Te klucze są ze sobą zgodne - Pokój zawiera nieznane sesje Wybierz katalog pokojów Serwer może być wyłączony lub przeciążony Adres serwera domowego Wszystkie pokoje na serwerze %s Szukaj w archiwum - Rozmiar czcionki Bardzo mały Mały @@ -517,10 +446,8 @@ Zauważ, że ta czynność spowoduje ponowne uruchomienie aplikacji i może to t Jasny motyw Ciemny motyw Czarny motyw - Zadzwoń Awatar - Powód: %1$s Zostałeś zbanowany z %1$s przez %2$s Zostałeś wyrzucony z %1$s przez %2$s @@ -529,10 +456,8 @@ Zauważ, że ta czynność spowoduje ponowne uruchomienie aplikacji i może to t Ludzie Przykład przykład - Wiadomość zaszyfrowana - Utwórz Off @@ -541,50 +466,37 @@ Zauważ, że ta czynność spowoduje ponowne uruchomienie aplikacji i może to t Nasłuchiwanie zdarzeń Głośne powiadomienia Ciche powiadomienia - Zgłoś błąd Informacje o społeczności - Wczytywanie… - Opuść Działania Społeczności - Filtruj nazwy społeczności - Zaproś Społeczności Brak grup - Potrząśnij wściekle, aby zgłosić błąd - Czy jesteś pewien, że chcesz rozpocząć nową rozmowę z %s? Czy jesteś pewien, że chcesz rozpocząć rozmowę głosową? Czy jesteś pewien, że chcesz rozpocząć wideorozmowę? - Zrób zdjęcie Nagraj film - Rejestracja jednocześnie za pomocą numeru telefonu i adresu e-mail nie jest obsługiwana dopóki nie pojawi się odpowiednie API. Tylko numer telefonu będzie brany pod uwagę. Możesz dodać adres e-mail do swojego profilu w ustawieniach. Serwer Domowy: Serwer Tożsamości: Twoje hasło zostało zresetowane. Zostałeś wylogowany ze wszystkich sesji i nie będziesz więcej otrzymywać powiadomień push. Aby ponownie włączyć powiadomienia, zaloguj się ponownie na każdym urządzeniu. - Wprowadzony token dostępu nie został rozpoznany Uszkodzony JSON - Lista grup - %d zmiana członkostwa %d zmiany członkostwa %d zmian członkostwa - + - Rozmówca nie połączył się. @@ -599,7 +511,6 @@ Przyznaj dostęp w następnym oknie. Element wymaga dostępu do kontaktów, aby znajdywać innych użytkowników Matrixa bazując na adresie e-mail i numerze telefonu. \n \nZezwolić Element na dostęp do kontaktów\? - Lista uczestników Otwarty nagłówek Synchronizacja… @@ -609,57 +520,50 @@ Przyznaj dostęp w następnym oknie. Jeden aktywny członek Kilku aktywnych członków %d aktywnych członków - + 1 członek kilku członków %d członków - + - Nie będziesz w stanie cofnąć tej zmiany, ponieważ przyznajesz użytkownikowi uprawnienia równe swoim. Jesteś pewien? - Czy chcesz zablokować tego użytkownika w tej rozmowie? - Nie udało się zarejestrować: błąd własności e-maila połączenie odebrane gdzie indziej - Wiadomość nie została wysłana. Czy %1$s lub %2$s teraz? Wiadomość niewysłana z powodu obecności nieznanych sesji. Czy %1$s lub %2$s teraz\? Nowa wiadomość Kilka nowych wiadomości %d nowych wiadomości - + - Może to oznaczać że ktoś zakłóca twoje połączenie, lub Twój telefon nie ufa certyfikatowi dostarczonemu przez zdalny serwer. Jeśli administrator serwera oświadczył, że jest to oczekiwane, upewnij się, że poniższy odcisk palca odpowiada odciskowi palca dostarczonemu przez niego. Certyfikat różni się od tego zaufanego w twoim telefonie. Serwer mógł odnowić certyfikat. Skontaktuj się z administratorem serwera w celu weryfikacji odcisku palca. Certyfikat zmienił stan z zaufanego na niezaufany. jest to NIEZWYKLE RZADKIE. Rekomendowane jest NIE AKCEPTOWANIE nowego certyfikatu. Akceptuj certyfikat tylko wtedy gdy administrator opublikował odcisk palca pasujący do tego powyżej. Uszkodzony ID. Powinien być adres e-mail lub Matrix ID podobny do \'@localpart:domain\' - 1 pokój %d pokoje %d pokoi - + %1$s pokój znaleziony dla %2$s Kilka pokoi znalezionych dla %2$s %1$s pokoi znalezionych dla %2$s - + Wszystkie wiadomości (hałaśliwy) Wszystkie wiadomości Tylko wspomnienia Wycisz Dodaj skrót do ekranu domowego - Informacje o trzecich stronach Pokaż informacje o aplikacji w ustawieniach systemu. Dźwięk powiadomień @@ -670,69 +574,52 @@ Jesteś pewien? Wersja olm Informacje o stronach trzecich Zatrzymaj media - Kraj książki adresowej Ekran domowy Podgląd zawartości URL Pokaż czas w formacie 12-godzinnym Wibruj gdy ktoś wspomni o Tobie - Analityka - Serwer domowy Serwer Tożsamości - Sprawdź pocztę e-mail i kliknij odnośnik, który zawiera. Gdy to zrobisz, kliknij przycisk Kontynuuj. Nie udało się zweryfikować adresu e-mail. Sprawdź swoją skrzynkę i kliknij w link. Gdy to zrobisz, kliknij kontynuuj. Adres e-mail nie został znaleziony. Czy na pewno chcesz usunąć %1$s %2$s? - Nieprawidłowy numer telefonu dla wybranego kraju Wysłaliśmy SMS z kodem aktywacyjnym. Podaj ten kod poniżej. Otagowany jako: - Powiadomienia Tylko członkowie (od momentu włączenia tej opcji) Tylko członkowie (od kiedy zostali zaproszeni) Tylko członkowie (od kiedy dołączyli) - Każdy kto zna link pokoju, poza gośćmi Każdy kto zna link pokoju, razem z gośćmi - Musisz się wylogować aby uruchomić szyfrowanie. Szyfruj wiadomości tylko do zaufanych sesji Nigdy nie wysyłaj zaszyfrowanych wiadomości do niezweryfikowanych sesjiw tym pokoju z tego urządzenia. - Ten pokój nie ma adresu lokalnego Nowe ID społęczności (np. +foo:matrix.org) Niepoprawne ID społeczności \'%s\' jest niepoprawnym ID społeczności - - Nieznany format aliasu \'%s\' jest nieznanym formatem dla aliasu Nie będziesz mieć podanego głównego adresu dla tego pokoju. Ostrzeżenia głównego adresu - NIe ustawiaj jako główny adres Błąd odszyfrowywania - Klucz sesji Wyeksportuj klucze pokoju Eksportuj klucze do pliku lokalnego Dołącz ponownie Zapomnij pokój - Pobierz Wyczyść Wyślij naklejkę Poproś ponownie o klucze szyfrujące z innych Twoich sesji. - Prośba o klucz wysłana. - Prośba wysłana Uruchom proszę Element na innym urządzeniu, które może odszyfrować wiadomość, aby wysłać klucze do tej sesji. - Prywatność powiadomień Standardowa Zmniejszona prywatność @@ -742,97 +629,77 @@ Jesteś pewien? • Treść powiadomienia jest bezpiecznie pobierana z domowego serwera Matrix • Powiadomienia zawierają meta-dane oraz treść wiadomości • Powiadomienia nie będą pokazywać treści wiadomości - Cele powiadomień Dezaktywuj konto Dezaktywuj moje konto - Prywatność powiadomień Element może działać w tle aby bezpiecznie i prywatnie zarządzać Twoimi powiadomieniami. To może mieć wpływ na zużycie baterii. Nadaj uprawnienie Wybierz inną opcję - Wysyłaj dane analityczne Element zbiera anonimowe informacje które pozwolą ulepszyć aplikację. Włącz proszę dane analityczne, aby pomóc nam ulepszyć Element. Tak, chcę pomóc! - Czy na pewno chcesz usunąć ten cel powiadomień? - Aby móc stworzyć link do pokoju musi on mieć swój adres. - %s próbował załadować konkretny punkt na osi czasu tego pokoju, ale nie był w stanie go znaleźć. - Informacje o szyfrowaniu end-to-end - Informacje zdarzenia Informacja o sesji nadawcy Eksportuj klucze E2E pokoju Klucze pokoju zostały zapisane w \'%s\' Uwaga: ten plik może zostać usunięty, jeśli aplikacja zostanie odinstalowana. - Importuj klucze E2E pokoju Importuj klucze pokoju Importuj klucze z lokalnego pliku Importuj Szyfruj wiadomości tylko do zaufanych sesji Nigdy nie wysyłaj zaszyfrowanych wiadomości do niezweryfikowanych sesji w tym pokoju z tej sesji. - Usuń weryfikację Aby sprawdzić czy ta sesja jest zaufana, skontaktuj się z jej właścicielem używając innych form (np. osobiście lub telefonicznie) i zapytaj czy klucz, który widzą w ustawieniach użytkownika dla tego urządzenia pasuje do klucza poniżej: Jeśli klucz pasuje, potwierdź to przyciskiem poniżej. Jeśli nie, to ktoś inny najprawdopodobniej przejmuje lub podszywa się pod tą sesję i powinieneś dodać tę sesję do czarnej listy. W przyszłości proces weryfikacji będzie bardziej skomplikowany. - Ten pokój zawiera nieznane sesje, które nie zostały zweryfikowane. Oznacza to brak gwarancji, że sesje należą do użytkowników do których twierdzą, że należą. Przed kontynuowaniem, zalecamy wykonanie procesu weryfikacji każdego urządzenia, ale możesz ponownie wysłać wiadomość bez weryfikacji, jeśli wolisz. Nieznane sesje: - Wyślij naklejkę - Licencje osób trzecich - Niestety, nie znaleziono zewnętrznej aplikacji, która ukończy to działanie. - 1 pokój %d pokoje %d pokoi - + 1 aktywny widżet %d aktywne widżety %d aktywnych widżetów - + - Pokój %s nie jest widoczny. Użyj natywnej kamery Mów Wyślij głos - Nie masz obecnie włączonych żadnych pakietów naklejek. Czy chcesz dodać teraz kilka? - zadziałaj poprzez… Klucz tożsamości Curve25519 Zażądano klucza odcisku palca Ed25519 Wpisz serwer domowy, aby pobrać z niego listę publicznych pokoi Napisz tutaj… - 1 nieprzeczytana wiadomość powiadomienia %d nieprzeczytane wiadomości powiadomienia %d nieprzeczytanych wiadomości powiadomienia - + 1 nieprzeczytana wiadomość powiadomienia %d nieprzeczytane wiadomości powiadomienia %d nieprzeczytanych wiadomości powiadomienia - + %1$s w %2$s - Potrzebujesz uprawnień do zarządzania widżetami w tym pokoju Utworzenie widżetu nie powiodło się Utwórz połączenia konferencyjne za pomocą jitsi @@ -843,7 +710,6 @@ Czy chcesz dodać teraz kilka? Nie jesteś w tym pokoju. Nie masz uprawnień, aby zrobić to w tym pokoju. Wyróżnik społeczności - Ten pokój nie wyświetla wyróżników dla żadnych społeczności Brakujące room_id w żądaniu. Brakujące user_id w żądaniu. @@ -851,45 +717,33 @@ Czy chcesz dodać teraz kilka? Parametr jest niepoprawny. Dodaj aplikacje Matrix Wyślij wiadomości głosowe - Dodałeś(-aś) nową sesję \'%s\', która żąda kluczy szyfrujących. Twoje niezweryfikowana sesja \'%s\' żąda kluczy szyfrujących. Rozpocznij weryfikację Współdziel bez weryfikacji Zignoruj żądanie - Połączenia konferencyjne są w stadium rozwoju i mogą nie być niezawodne. - Błąd polecenia Nierozpoznane polecenie: %s - Głośne - Utwórz społeczność Nazwa społeczności Identyfikator społeczności Strona startowa Brak użytkowników - Pokoje Zaproszeni Filtrowanie członków grupy Filtrowanie pokoi grupy - Administrator społeczności nie podał długiego opisu tej społeczności. - Dezaktywuj konto Wszystkie lokalne pokoje %s - Aby kontynuować korzystanie z serwera domowego %1$s, musisz przejrzeć i zaakceptować warunki korzystania z usługi. Przejrzyj teraz - Proszę zapomnieć o wszystkich wiadomościach, które wysłałem, gdy moje konto zostało dezaktywowane (Ostrzeżenie: spowoduje to, że przyszli użytkownicy zobaczą niepełny obraz rozmów) Aby kontynuować, wpisz swoje hasło: Dezaktywuj konto - Prosimy wpisać swoje hasło. - To sprawi, że Twoje konto stanie się na stałe niezdatne do użytku. Nie będziesz mógł się zalogować i nikt nie będzie mógł ponownie zarejestrować tego samego identyfikatora użytkownika. Spowoduje to, że Twoje konto opuści wszystkie pokoje, w których uczestniczy, i usunie dane Twojego konta z serwera tożsamości. Ta czynność jest nieodwracalna. Dezaktywacja konta domyślnie nie powoduje zapomnienia wysłanych przez Ciebie wiadomości. Jeśli chcesz, abyśmy zapomnieli o Twoich wiadomościach, zaznacz pole poniżej. @@ -898,10 +752,8 @@ Widoczność wiadomości w Matrix jest podobna do wiadomości e-mail. Nasze zapo Uzyskaj awatar Zamień awatar Błąd - %1$s teraz %1$s %2$s temu - "%1$s, " %1$s i %2$s Wyślij zaszyfrowaną odpowiedź… @@ -912,57 +764,49 @@ Widoczność wiadomości w Matrix jest podobna do wiadomości e-mail. Nasze zapo 1 członek %d członków - + - 1 pokój %d pokoje %d pokoi - + Kliknij tutaj, aby zobaczyć starsze wiadomości - Przepraszamy, wystąpił błąd - Motyw Status.im - Z powodu braku uprawnień to działanie nie jest możliwe. Alerty systemowe - Jeżeli to możliwe, proszę napisz opis w języku angielskim. 1 sekunda %d sek. - + 1 minuta %d min. - + 1 godzina %s godz. - + 1 dzień %d dni - + - 1 zaznaczone %d zaznaczone %d zaznaczonych - + Wersja %s Podgląd mediów przed wysłaniem - Nie jesteś obecnie członkiem żadnej społeczności. - Utwórz hasło, aby zaszyfrować wyeksportowane klucze. Będziesz musiał wpisać to samo hasło, aby móc zaimportować klucze. Utwórz hasło Hasło musi się zgadzać @@ -977,49 +821,36 @@ Widoczność wiadomości w Matrix jest podobna do wiadomości e-mail. Nasze zapo Wyrzuca użytkownika z podanym ID Zmienia twój wyświetlany nick Do naprawiania zarządzania aplikacjami Matrix - Ten pokój został zamieniony i nie jest już aktywny Konwersacja jest kontynuowana tutaj Ten pokój jest kontynuacją innej rozmowy Limit pakietów został przekroczony Skontaktuj się z administratorem - Skontaktuj się z administratorem usług - Ten serwer przekroczył jeden z limitów, więc niektórzy użytkownicy nie będą mogli się zalogować. Ten serwer przekroczył jeden z limitów zasobów. - Ten serwer osiągnął miesięczny limit aktywnego użytkownika, więc niektórzy użytkownicy nie będą mogli się zalogować. Ten serwer osiągnął miesięczny limit aktywnego użytkownika. - Proszę %s, aby zwiększył ten limit. Proszę %s, aby kontynuować z tej usługi. - Zwiększ wydajność, ładując tylko członków pokoju w pierwszym widoku. Twój serwer nie obsługuje jeszcze powolnego ładowania członków pokoju. Spróbuj później. - Rozwiń Zwiń - Powolne ładowanie członków pokojów %1$s %2$s - Dzwonek przychodzących połączeń Wybierz dzwonek dla połączeń: - Powód - Wyślij powiadomienia o pisaniu Hasło Markdown został włączony. Markdown został wyłączony. - Zawsze %1$s: %1$s: %2$s +%d %d+ - Użyj domyślnego dzwonka Element dla przychodzących połączeń Zadzwoń mimo to Połączenia @@ -1028,7 +859,6 @@ Widoczność wiadomości w Matrix jest podobna do wiadomości e-mail. Nasze zapo Pokaż zdarzenia dołączenia i wyjścia Dla komunikatów i błędów Tylko dla błędów - Wyświetl potwierdzenia odczytu Obejmuje zmiany awatara i nazwy wyświetlania. Pokaż zdarzenia konta @@ -1037,29 +867,22 @@ Widoczność wiadomości w Matrix jest podobna do wiadomości e-mail. Nasze zapo Komenda „%s” potrzebuje więcej parametrów lub niektóre parametry są niepoprawne. Pokaż obszar informacyjny Akceptuj - Przeczytaj i zaakceptuj zasady tego serwera domowego: - Ta opcja wymaga aplikacji osoby trzeciej do nagrywania wiadomości. - Uruchom Testy Ustawienia Systemowe. Powiadomienia są włączone w ustawieniach systemowych. Otwórz Ustawienia - Ustawienia Konta. Powiadomienia są właczone dla twojego konta. Włącz - Ustawienia Sesji. Powiadomienia są włączone dla tej sesji. Włącz - Token Firebase Rejestracja Tokena Rozpocznij przy uruchomieniu systemu Nadaj uprawnienie - Jeden lub więcej testów nie powiodło się, spróbuj sugerowaną poprawkę(-ki). Powiadomienia są wyłączone w ustawieniach systemowych. Sprawdź ustawienia systemowe. @@ -1067,65 +890,50 @@ Sprawdź ustawienia systemowe. Sprawdź ustawienia konta. Powiadomienia nie są włączone dla tej sesji. Proszę sprawdź ustawienia Element. Dodatkowe informacje: %s - Wystąpił błąd podczas weryfikowania numeru telefonu. Wystąpił błąd podczas weryfikowania adresu e-mail. - Połączenie w tle Ignoruj Optymalizację - Optymalizacja Baterii Jeden lub więcej testów nie powiodło się, prosimy o przesłanie raportu o błędzie, aby pomóc nam w zbadaniu problemu. - Uruchom Usługę - Usługa Powiadomień Usługa Powiadomień jest uruchomiona. Usługa Powiadomień nie jest uruchomiona. Spróbuj uruchomić ponownie aplikację. Połączenie wideo trwa… - Kopia Zapasowa Klucza Kopia zapasowa kluczy nie jest zakończona, proszę czekać… Pomiń Zaawansowane ustawienia powiadomień Sprawdź ustawienia - Algorytm Stracisz zaszyfrowane wiadomości, jeśli się teraz wylogujesz Jesteś pewien? Przerwij - Czy na pewno chcesz się wylogować? Trwa tworzenie kopii zapasowej klucza. Jeśli wylogujesz się teraz utracisz dostęp do zaszyfrowanych wiadomości. Nie chcę moich zaszyfrowanych wiadomości Utracisz dostęp do zaszyfrowanych wiadomości, chyba że wykonasz kopię zapasową kluczy przed wylogowaniem się. - Zostań Ignoruj - Domyślna kompresja Konfiguruj głośne powiadomienia Konfiguruj ciche powiadomienia Ciche Przywróć z kopii zapasowej Usuń kopię zapasową - Usuń kopię zapasową Wersja Zweryfikuj sesję - Oznacz jako przeczytane Zweryfikowano! Weryfikacja klucza Żądanie weryfikacji %s chce zweryfikować twoją sesję - Nieznany błąd - Użyj kopii zapasowej klucza Podpis - Nowa sesja żąda kluczy szyfrujących. \nNazwa sesji: %1$s \nOstatnio widziana: %2$s @@ -1134,40 +942,33 @@ Spróbuj uruchomić ponownie aplikację. \nNazwa sesji: %1$s \nOstatnio widziana: %2$s \nJeśli to nie Ty zalogowałeś(-aś) się na innej sesji, zignoruj to żądanie. - Weryfikuj Udostępnij Żądanie udostępnienia klucza Ignoruj - Udostępnij Przywrócono kopię zapasową z %d kluczem. Przywrócono kopię zapasową z %d kluczami. - + Użyj kopii zapasowej klucza Uruchamianie… (%1$d z %2$d) Ustawienia niestandardowe. Zarządzaj kopią zapasową klucza - Udostępnij klucz odzyskiwania z… Usunąć klucze szyfrowania z serwera\? Nie będziesz mógł/mogła używać klucza odzyskiwania do odczytywania historii zaszyfrowanych wiadomości. - Wszystkie klucze mają kopię zapasową Zaktualizuj hasło Zrobione Zrobione Edytuj Odpowiedz - Pokoje Dodaj reakcję Utwórz nowy pokój Wszystkie społeczności - Wiadomości Bezpośrednie - Nowy pokój STWÓRZ Nazwa pokoju @@ -1179,7 +980,6 @@ Spróbuj uruchomić ponownie aplikację. Zmień sieć Pokoje Opublikuj pokój do spisu pokojów - Aby nie utracić dostępu do Twoich zaszyfrowanych wiadomości, powinienieś aktywować kopię zapasową klucza na wszystkich aktywnych sesjach. Tworzenie kopii zapasowej kluczy… Utwórz kopię zapasową @@ -1188,13 +988,11 @@ Spróbuj uruchomić ponownie aplikację. Inicjalizacja usługi Zaloguj się za pomocą logowania jednorazowego Ważność powiadomień ze względu na wydarzenie - Diagnostyka powiadomień Rozwiązywanie problemów Diagnostyka podstawowa nie wykazała problemów. Jeżeli wciąż nie otrzymujesz powiadomień, prosimy o przesłanie raportu o błędach, w celu ich rozwiązania. Aplikacja nie potrzebuje łączyć się z serwerem domowym w tle, powinno to zredukować użycie baterii Jeżeli nie pamiętasz swoich danych odzystkiwania, możesz %s. - Zgubiłeś (-łaś) swój klucz odzyskiwania\? Możesz ustawić nowy w ustawieniach. Kopia zapasowa posiada poprawną sygnaturę z niezweryfikowanej sesji %s Wykryto nową, bezpieczną kopię kluczy wiadomości. @@ -1205,24 +1003,18 @@ Spróbuj uruchomić ponownie aplikację. Unieważnij Rozłącz Nie pytaj ponownie - To nie jest prawidłowy adres serwera Matrix Potwierdź swoje hasło Napraw Usługi Play - Dodaj Konto - Zarządzanie Kluczami Kryptograficznymi Zezwól na integracje Menadżer Integracji - Hasło jest nieprawidłowe Hasła nie pasują do siebie - Wybierz Wybierz Odtwarzaj dźwięk migawki - Nazwa urządzenia (widoczna dla osób, z którymi się komunikujesz) Publiczna nazwa urządzenia jest widoczna dla ludzi, z którymi się komunikujesz nieznany adres ip @@ -1232,8 +1024,6 @@ Spróbuj uruchomić ponownie aplikację. Otwórz w przeglądarce Twój motyw Identyfikator Pokoju - - Zezwól Wprowadź nazwę użytkownika. Wprowadź hasło @@ -1243,17 +1033,13 @@ Spróbuj uruchomić ponownie aplikację. Twoje klucze są archiwizowane. Zastąp Zatrzymaj - Klucz Odzyskiwania Nieoczekiwany błąd Czy na pewno\? Pobieranie wersji kopii zapasowej… Wprowadź Klucz Odzyskiwania - Odzyskiwanie Wiadomości - Błąd sieci: sprawdź połączenie i spróbuj ponownie. - Obliczanie klucza odzyskiwania… Pobieranie kluczy… Importowanie kluczy… @@ -1265,20 +1051,15 @@ Spróbuj uruchomić ponownie aplikację. dodano %d nowe klucze do tej sesji. dodano %d nowych kluczy do tej sesji. - Niepowodzenie przy pobieraniu wersji kluczy (%s). Usuwanie kopii zapasowej… Sprawdzanie stanu kopii zapasowej Oczekiwanie na potwierdzenie partnera… - Sesja została z powodzeniem zweryfikowana. Druga strona odrzuciła weryfikację. \n%s Reakcje - Ostatnio zmieniony %2$s przez %1$s - - Brak sieci. Sprawdź swoje połączenie z Internetem. Proszę czekać… Tego pokoju nie można podejrzeć @@ -1289,21 +1070,16 @@ Spróbuj uruchomić ponownie aplikację. Wysyłanie miniatury (%1$s / %2$s) Szyfrowanie pliku… Wysyłanie pliku (%1$s / %2$s) - Pobieranie pliku %1$s… Plik %1$s został pobrany! - Tworzę pokój… Warunki Usługi Wyśli załącznik - Utwórz nowy pokój Pokaż hasło Ukryj hasło Przewiń do dołu - Plik \'%1$s\' (%2$s) jest zbyt duży do przesłania. Limit wynosi %3$s. - Plik Kontakt Galeria @@ -1315,40 +1091,31 @@ Spróbuj uruchomić ponownie aplikację. Powód zgłoszenia treści ZGŁOŚ ZABLOKUJ UŻYTKOWNIKA - Treść zgłoszona Zgłoszone jako spam Weyfikacja zostaje anulowana. \nPowód: %s - Proszę napisz swoją sugestię poniżej. Opisz swoją sugestię tutaj Utwórz nową rozmowę bezpośrednią Połączenie z serwerem zostało utracone - Proszę wykonać kopię Preferencje Głos i wideo Wiadomości Bezpośrednie - Filtruj rozmowy… Wyślij nową wiadomość bezpośrednią Dodaj przez matrix ID Filtruj wg nazwy użytkownika lub ID… - Wersja Matrix SDK Pomoc i o aplikacji - - Wszystkie wiadomości (hałaśliwy) Wszystkie wiadomości Tylko wspomnienia Wycisz Ustawienia Nie ignorujesz żadnych użytkowników - Widziany przez - Zaawansowane ustawienia Tryb programisty Ustawienia @@ -1360,7 +1127,6 @@ Spróbuj uruchomić ponownie aplikację. Włącz szyfrowanie end-to-end Brak Brak skonfigurowanego serwera tożsamości. - Poproś administratora serwera (%1$s) o skonfigurowanie usługi TURN, aby połączenia mogły działać prawidłowo. \n \nMożesz także użyć publicznego serwera %2$s, choć nie będzie on tak niezawodny i pozna twój adres IP. Wybór możesz zmienić w Ustawieniach. @@ -1370,8 +1136,6 @@ Spróbuj uruchomić ponownie aplikację. Nie udało się połączyć z serwerem o podanym adresie URL, upewnij się, że wpisano go poprawnie Dodaj serwer tożsamości w ustawieniach, aby móc wykonać tę akcję. Wymagane jest uwierzytelnienie - - Niektóre rodzaje wiadomości będą ciche (wygenerują powiadomienie bez dźwięku). Nie udało się wczytać niestandardowych reguł, spróbuj ponowić. Weryfikacja Usług Google @@ -1386,10 +1150,8 @@ Spróbuj uruchomić ponownie aplikację. Token FCM z powodzeniem zarejestrowany na serwerze domowym. Niepowodzenie przy rejestracji tokena FCM na serwerze domowym: \n%1$s - Usługa została zatrzymana i automatycznie uruchomiona ponownie. Usługa nie uruchomiła się ponownie - Usługa zostanie uruchomiona przy starcie urządzenia. Usługa nie zostanie uruchomiona przy starcie urządzenia, nie otrzymasz żadnych powiadomień, dopóki Element nie zostanie uruchomiony. Dla zwiększenia bezpieczeństwa, zalecamy aby wykonać ten krok osobiście lub przez inne zaufane środki komunikacji. @@ -1401,14 +1163,10 @@ Spróbuj uruchomić ponownie aplikację. \nJeśli nie chcesz więcej widzieć treści tego użytkownika,możesz go zablokować by ukryć jego wiadomości Sprawdź ograniczenia pracy w tle Wyłącz ograniczenia - Konfiguruj powiadomienia połączeń Wybierz kolor diody LED, wibrację, dźwięk… - - Integracje Latn - Wprowadź adres e-mail, aby możliwe było odzyskiwanie konta. Opcjonalnie użyj adresu e-mail lub numeru telefonu aby móc zostać odkrytym przez znajomych. Wprowadź adres e-mail, aby możliwe było odzyskiwanie konta. Opcjonalnie użyj adresu e-mail lub numeru telefonu aby móc zostać odkrytym przez znajomych. Pozwól na awaryjny serwer wspomagania połączeń @@ -1418,7 +1176,6 @@ Spróbuj uruchomić ponownie aplikację. [%1$s] \nBłąd jest poza kontrolą Element. Może on występować z wielu powodów. Przypuszczalnie aplikacja powróci do normalnego stanu po spróbowaniu ponownie, chociaż można sprawdzić także w ustawieniach systemu uprawnienia Usług Google Play dotyczące dostępu do sieci, sprawdzić prawidłowość zegaru urządzenia lub też, może być to błąd niestandardowego oprogramowania systemowego. Aktywuj uruchamianie przy starcie systemu - Restrykcje dotyczące działania aplikacji w tle są wyłączone dla Element. Test powinen zostać uruchomiony używając danych komórkowych (bez WIFI). \n%1$s "Restrykcje dotyczące działania aplikacji w tle są włączone dla Element. @@ -1436,8 +1193,6 @@ Spróbuj uruchomić ponownie aplikację. Brak synchronizacji w tle Nie będziesz otrzymywać powiadomień o przychodzących wiadomościach gdy aplikacja będzie działać w tle. Niepowodzenie przy aktualizacji ustawień. - - Preferowany interwał synchronizacji %s \nSynchronizacja może zostać opóźniona w zależności od zasobów (bateria) lub stanu urządzenia (hibernacja). @@ -1448,32 +1203,28 @@ Spróbuj uruchomić ponownie aplikację. Nie wpływa to na zaproszenia, wyrzucenia oraz bany. Wysyłaj wiadomości za pomocą klawisza enter Przycisk enter na klawiaturze programowej wyśle wiadomość zamiast wprowadzania łamanania linii - Znajdź Zarządzaj ustawieniami wyszukiwania. Element potrzebuje utrzymać mało wpływowe połączenie w tle, w celu otrzymywania wiarygodnych powiadomień. \nNa następnym ekranie zostanie się poproszonym o pozwolenie działania w tle dla Element, proszę zaakceptować. Tryb oszczędzania danych użyje filtra szczegółowego, w związku z czym aktualizacje o obecności i powiadomienia o pisaniu zostaną przefiltrowane. - Media Domyślne źródło mediów Odzyskiwanie zaszyforwanych wiadomości %1$s: 1 wiadomość %1$s: %2$d wiadomości - + %d powiadomienie %d powiadomień - + - Nowe wydarzenie Nowe wiadomości Ja ** Nie udało się wysłać - proszę otworzyć pokój - Widżet Załaduj widżet Używając tego pliki cookies mogą zostać ustawione i dane wspóldzielone z %s: @@ -1482,7 +1233,6 @@ Spróbuj uruchomić ponownie aplikację. \n%s Przeładuj widżet Cofnij dostęp dla mnie - Wyświetlana nazwa Adres URL awatara Twój ID użytkownika @@ -1493,25 +1243,19 @@ Spróbuj uruchomić ponownie aplikację. Użyj aparatu Użyj mikrofonu Odczytaj media zabezpieczone DRM - Nie skonfigurowano menedżera integracji. Uruchom systemową kamerę zamiast niestandardowego ekranu kamery w aplikacji. Aby kontynuować, musisz zaakceptować Warunki użytkowania dla tej usługi. - Nie znaleziono prawidłowej aplikacji Usługi Google Play. Powiadomienia mogą nie działać prawidłowo. - Hasło jest zbyt słabe - Proszę usunąć hasło, jeżeli chcesz aby Element wygenerował klucz odzyskiwania. Brak dostępnych sesji Matrix - Nie utrać zaszyfrowanych wiadomości Wiadomości w pokojach zaszyfrowanych są bezpieczne dzięki szyfrowaniu end-to-end. Jedynie Ty i Twój odbiorca posiadają klucze dla tych wiadomości. \n \nBezpiecznie utwórz kopię zapasową, aby ich nie utracić. Zacznij używać Kopii Zapasowej Kluczy Wyeksportuj klucze ręcznie - Zabezpiecz swoją kopię zapasową używając hasła. Będziemy przechowywać zaszyfrowaną kopię Twoich kluczy na serwerze domowym. Chroń swoją kopię zapasową hasłem, aby pozostała bezpieczna. \n @@ -1528,72 +1272,54 @@ Spróbuj uruchomić ponownie aplikację. "Klucz odzyskiwania został zapisany do \'%s\'. \n \nUwaga: plik może zostać usunięty, jeżeli aplikacja jest odinstalowana." - Kopia zapasowa już istnieje na Twoim serwerze domowym Wygląda na to, iż kopia zapasowa kluczy została skonfigurowana za pomocą innej sesji. Czy chcesz zastąpić ją tą, którą tworzysz\? Generowanie Klucza Odzyskiwania używając hasła, proces może zająć kilka sekund. Kopia zapasowa uruchomiona Twoje klucze szyfrujące będą kopiowane do kopii zapasowej w tle przez Twój serwer domowy. Wstępna kopia zapasowa może zająć kilka minut. - - Utracisz dostęp do swoich wiadomości jeżeli wylogujesz się lub utracisz to urządzenie. - Użyj kopii zapasowej klucza aby odblokować historię zaszyfrowanych wiadomości użyj klucza odzyskiwania Użyj Klucza Odzyskiwania aby odblokować historię zaszyfrowanych wiadomości Kopia zapasowa nie może zostać zdeszyfrowana za pomocą tego hasła: proszę upewnij się, czy wprowadzone hasło jest poprawne. Przywracanie kopii zapasowej: Kopia zapasowa nie może zostać zdeszyfrowana za pomocą tego klucza odzyskiwania: proszę upewnij się, czy wprowadzony klucz odzyskiwania jest poprawny. - Szyfrowanie sesji nie jest aktywowane - - Kopia zapasowa klucza nie jest aktywna dla tej sesji. Twoje klucze nie są będą zapisywane w kopii zapasowej od tej sesji. - Kopia zapasowa posiada sygnaturę od nieznanej sesji z ID %s. Kopia zapasowa posiada prawidłową sygnaturę dla tej sesji. Kopia zapasowa posiada prawidłową sygnaturę z zweryfikowanej sesji %s. Kopia zapasowa posiada nieprawidłową sygnaturę ze zweryfikowej sesji %s Kopia zapasowa posiada niezweryfikową sygnaturę z nieznanej sesji %s Nie udało się uzyskać zaufanych informacji dla kopii zapasowej (%s). - Aby użyć Kopii Zapasowej Kluczy dla tej sesji, przywróć ją za pomocą hasła lub klucza odzyskiwania. Usuwanie kopii zapasowej nie powiodło się (%s) - Nowa kopia zapasowa kluczy To byłem(-łam) ja Nigdy nie utrać zaszyfrowanych wiadomości Zacznij korzystać z Kopii Zapasowej Kluczy - Nigdy nie utrać zaszyfrowanych wiadomości Użyj Kopii Zapasowej Kluczy - Nowe klucze szyfrowanych wiadomości Zarządzaj w Kopii Zapasowej Kluczy - Tworzenei kopii zapasowej kluczy… - Kopiowanie %d klucza… Kopiowanie %d kluczy… - + - Nieprawidłowa odpowiedź funkcji autoodkrywania serwera domowego Opcje automatycznego uzupełniania serwerów Element wykryło niestandardową konfigurację serwera dla Twojej domeny userID \"%1$s\": \n%2$s Użyj Konfiguracji - Zostałeś(-łaś) wylogowana ze względu na nieprawidłowe lub wygasłe dane logowania. - Zweryfikuj porównując krótki ciąg tekstowy. Rozpocznij weryfikację Przychodzące żądanie weryfikacji Zweryfikuj tą sesję poprzez oznaczenie jej jako zaufanej. Zaufanie sesji innych osób przynosi spokój na umyśle gdy są to szyfrowane wiadomości end-to-end. Werfikacja tej sesji oznaczy ją jako zaufaną, a także oznaczy Twoją sesję jako zaufaną dla rozmówcy. - Wyświetl żądanie Bezpieczne wiadomości od tego użytkownika są zabezpeiczone za pomocą szyfrowania end-to-end i są nie do odczytania przez osoby trzecie. Połączenie nie powiodło się z powodu niewłaściwie skonfigurowanego serwera @@ -1603,13 +1329,10 @@ Spróbuj uruchomić ponownie aplikację. Automatycznie uruchom ponownie usługę powiadomień Potwierdź, że następujące emoji pojawiły się na ekranie partnera Potwierdź, że następujące liczby pojawiły się na ekranie partnera - Otrzymano przychodzące żądanie weryfikacji. Rozumiem - Nic się nie pojawiło\? Nie wszystkie aplikacje obsługują już interaktywną weryfikację. Spróbuj weryfikacji starszego typu. Użyj weryfikacji starszego typu. - Weryfikacja Anulowana Interaktywna Weryfikacja Sesji Użytkownik anulował weryfikację @@ -1620,60 +1343,40 @@ Spróbuj uruchomić ponownie aplikację. SAS nie zgadza się Sesja otrzymała niespodziewaną wiadomość Otrzymano nieprawidłową wiadomość - Dołącz do pokoju, aby rozpocząć korzystanie z aplikacji. Zapoznaj się z nieprzeczytanymi wiadomościami tutaj Twoje rozmowy bezpośrednie będą wyświetlane tutaj Twoje pokoje będą wyświetlane tutaj - Nieprawidłowe zdarzenie, nie można wyświetlić Podgląd globalnego, publicznego pokoju nie jest wciąż wspierany w Element - Wystąpił błąd podczas otrzymywania zaufanych informacji Wystąpił błąd podczas uzyskiwania danych kluczy kopii zapasowej - Importowanie kluczy E2E z pliku \"%1$s\". - Informacje o stronach trzecich Już wyświetlasz ten pokój! - id_aplikacji: klucz_push: wyświetlana_nazwa_aplikacji: nazwa_sesji: Url: Format: - Zarejestruj token - Dziękujemy, sugestia została szczęśliwie wysłana Wysłanie sugestii nie powiodło się (%s) - Wyświetl ukryte wydarzenia na linii czasowej - (edytowano) - - Edycje wiadomości Nie znaleziono edycji - Aktywuj gest przesunięcia, aby odpowiedzieć na osi czasu - Link skopiowany do schowka - Nie znaleziono, użyj Dodaj poprzez ID Matrix aby wyszukać na serwerze. Zacznij pisać, aby uzyskać rezultaty Dołączanie do pokoju… - Wyświetl historię edycji - Sprawdź Warunki Bądź odkryty przez innych Używaj Botów, mostów, widżetów i paczek naklejek - Czytaj na - - Obecnie używasz %1$s aby odkrywać i być odkrytym przez kontakty, które znasz. Nie używasz serwera tożsamości. Aby odkrywać i być odkrywanym przez kontakty, które znasz, skonfiguruj jeden poniżej. Rozpoznawalny adres e-mail @@ -1683,25 +1386,18 @@ Spróbuj uruchomić ponownie aplikację. Rozpoznawalne numery telefonu Wysłaliśmy e-mail potwierdzający do %s, sprawdź swoją skrzynkę i naciśnij link potwierdzający Oczekiwanie - Wprowadź nowy serwer tożsamości Nie można połączyć z serwerem tożsamości Wprowadź adres serwera tożsamości Serwer tożsamości nie posiada warunków usługi Wybrany system tożsamości nie posiada jakichkolwiek warunków usługi. Kontynuuj jedynie, gdy ufasz właścicielowi usługi Wiadomość tekstowa wysłana do %s. Proszę wprowadzić kod weryfikacyjny w niej zawarty. - Udostępniasz adres e-mail lub numer telefonu serwerowi tożsamości %1$s. Musisz ponownie połączyć się z %2$s aby ich nie udostępniać. Akceptuj Warunki Usługi serwera tożsamości (%s) aby pozwolić na bycie odkrytym za pomocą adresu e-mail lub numeru telefonu. - Aktywuj szczegółowe dzienniki. Dzienniki szczegółowe pomogą twórcom poprzez udostępnianie większej ilości danych, które zostaną wysłane, gdy potrząśniesz wściekle. Nawet jeżeli zostaną aktywowane, aplikacja nie zapisuje szczegółów wiadomości oraz innych danych prywatnych. - - Spróbuj ponownie, gdy zaakceptujesz warunki usługi oraz warunki ogólne serwera domowego. - Wygląda na to, iż serwer potrzebuje wiele czasu aby odpowiedzieć i może być to spowodowane zarówno słabą łącznością jak i problemem z serwerem. Spróbuj ponownie za chwilę. - Otwórz panel nawigacji Otwórz menu tworzenia pokoju Zamknij menu tworzenia pokoju… @@ -1712,41 +1408,32 @@ Spróbuj uruchomić ponownie aplikację. 1 użytkownik odczytał %d użytkowników odczytało - + - Wystąpił błąd poczas otrzymywania załącznika. Audio Nie można obsłużyć otrzymanych danych - Zgłoszono jako nieodpowiedni Zawartość została zgłoszona jako niewłaścwa. \n \nJeżeli nie chcesz widzieć treści od tego użytkownika, możesz go zablokować aby ukryć jego wiadomości - Element potrzebuje uprawnień aby zapisywać klucze E2E na dysku. \n \nPozwól na dostęp w następnym oknie aby móc eksportować klucze ręcznie. - Opuść pokój %1$s nie dokona(-ła) zmian Wysyła wiadomość jako spoiler Spoiler Wprowadź słowa kluczowe aby znaleźć reakcję. - Naciśnij długo na pokój aby wyświetlić więcej opcji - - %1$s ustawił(-a) pokój dostępnym publicznie dla każdego, kto zna link. %1$s ustawił(-a) pokój tylko dla zaproszonych. Nieprzeczytane wiadomości - Wyzwól swoją komunikację Czatuj z osobami bezpośrednio lub w grupach Pozostaw konwersacje prywatnymi za pomocą szyfrowania Rozszerz i dopasuj swoje doświadczenie Rozpocznij - Wybierz serwer Tak jak adres e-mail, konta mają jeden dom, aczkolwiek możesz rozmawiać ze wszystkimi Dołącz do milionów, za darmo, na największym publicznym serwerze @@ -1754,7 +1441,6 @@ Spróbuj uruchomić ponownie aplikację. Dowiedz się więcej Inne Ustawienia niestandardowe i zaawansowane - Kontynuuj Połącz z %1$s Połącz z Element Matrix Services @@ -1763,13 +1449,11 @@ Spróbuj uruchomić ponownie aplikację. Zarejestruj Zaloguj się Kontynuuj za pomocą logowania jednostopniowego - Adres Element Matrix Services Adres Hosting premium dla organizacji Wprowadź adres Element Modular lub serwera którego chcesz użyć Wprowadź adres serwera lub Element z którym chcesz się połączyć - Wystąpił błąd podczas ładowania strony: %1$s (%2$d) Aplikacja nie jest w stanie zalogować się do tego serwera domowego. Serwer domowy obsluguje następujące metody logowania: %1$s. \n @@ -1778,58 +1462,46 @@ Spróbuj uruchomić ponownie aplikację. Aplikacja nie jest w stanie utworzyć konta na tym serwerze domowym. \n \nCzy chcesz zarejestrować się używając klienta sieciowego\? - E-mail nie jest powiązany z kontem. - Zresetuj hasło na %1$s Wiadomość weryfikacyjna zostanie wysłana na adres e-mail aby potwierdzić ustawienie nowego hasła. Dalej E-mail Nowe hasło - Uwaga! Zmiana hasła zresetuje wszystkie klucze szyfrowania end-to-end dla wszystkich twoich sesji, czyniąc zaszyfrowaną historię czasu nie do odczytania. Ustaw Kopię Zapasową Kluczy lub wyeksportuj klucze pokoju do innej sesji przed resetowaniem hasła. Kontynuuj - Adres e-mail nie został połączony z kontem - Sprawdź swoją skrzynkę E-mail weryfikacyjny został wysłany do %1$s. Naciśnij na link aby potwierdzić nowe hasło. Po naciśnięciu na link, który je zawiera, naciśnij poniżej. Zweryfikowałem(-łam) swój adres e-mail - Sukces! Hasło zostało zresetowane. Zostałeś(-łaś) wylogowany(-na) ze wszystkich sesji i nie będziesz otrzymywać powiadomień push. Aby re-aktywować powiadomienia, zaloguj się ponownie na każdym z urządzeń. Powróć do logowania - Ostrzeżenie Hasło wciąż nie zostało zmienione. \n \nZatrzymać proces zmiany hasła\? - Ustaw adres e-mail Ustaw e-mail aby odzyskać konto. Później, opcjonalnie, będziesz w stanie pozwolić na odkrycie Ciebie za pomocą Twojego adresu e-mail. E-mail E-mail (nieobowiązkowy) Dalej - Ustaw numer telefonu Ustaw numer telefonu, aby pozwolić innym na odkrycie Ciebie za pomocą numeru telefonu. Użyj formatu międzynarodowego. Numer telefonu Numer telefonu (nieobowiązkowy) Dalej - Potwierdź numer telefonu Wysłaliśmy kod do %1$s. Wprowadź go poniżej, aby potwierdzić, że to Ty. Wprowadź kod Wyślij ponownie Dalej - Międzynarodowe numery telefonów muszą zaczynać się od \'+\' Numer telefonu wydaje się być nieprawidłowy. Sprawdź - Zaloguj się do %1$s Nazwa użytkownika lub e-mail Nazwa użytkownika @@ -1840,26 +1512,22 @@ Spróbuj uruchomić ponownie aplikację. Twoje konto wciąż nie zostalo utworzone. \n \nZatrzymać proces rejestracji\? - Wybierz matrix.org Wybierz Element Matrix Services Wybierz niestandardowy serwer domowy Wypełnij zadanie Captcha Zaakceptuj warunki aby kontynuować - Sprawdź swój e-mail Wysłaliśmy e-mail do %1$s. \nProszę nacisnąć na link który zawiera aby kontynuować tworzenie konta. Wprowadzony kod jest nieprawidłowy. Sprawdź. Nieaktualny serwer domowy Ten serwer domowy pracuje pod kontrolą zbyt starej wersji, aby się z nim połączyć. Zapytaj administratora serwera domowego o aktualizację. - Zostało wysłane zbyt wiele żądań. Możesz spróbować ponownie za %1$d sekundę… Zostało wysłane zbyt wiele żądań. Możesz spróbować ponownie za %1$d sekundy… - + - Wylogowałeś(-łaś) się Mogło to się stać z wielu powodów: \n @@ -1869,7 +1537,6 @@ Spróbuj uruchomić ponownie aplikację. \n \n• Administrator Twojego serwera unieważnił dostęp ze względów bezpieczeństwa. Zaloguj ponownie - Wylogowałeś(-łaś) się Zaloguj się Administaror twojego serwera domowego (%1$s) wylogował cię z konta %2$s (%3$s). @@ -1881,7 +1548,6 @@ Spróbuj uruchomić ponownie aplikację. \n \nWyczyść je, jeżeli skończyłeś(-łaś) używać tego urządzenia, lub chcesz zalogować się na inne konto. Wyczyść wszystkie dane - Wyczyść dane Wyczyścić wszystkie dane przechowywane na tym urządzeniu\? \nZaloguj się ponownie aby uzyskać dostęp do danych konta i wiadomości. @@ -1889,12 +1555,9 @@ Spróbuj uruchomić ponownie aplikację. Wyczyść dane Aktualna sesja jest dla użytkownika %1$s, podajesz natomiast dane dla użytkownika %2$s. Nie jest to wspierane przez Element. \nNa początku usuń dane, następnie zaloguj ponownie na innym koncie. - Link matrix.to został zdeformowany Opis zbyt krótki - Synchronizacja wstępna… - Zobacz wszystkie sesje Wściekłe potrząśnięcie Próg detekcji @@ -1902,26 +1565,19 @@ Spróbuj uruchomić ponownie aplikację. Potrząśnięcie wykryte! Aktualna sesja Inne sesje - Wyświetlanie jedynie początkowych wyników, wprowadź więcej znaków… - Bezproblemowy Element może zawieszać się częściej gdy napotka na niespodziewany błąd - Preparuje ¯\\_(ツ)_/¯ dla zwykłej wiadomości tekstowej - Aktywuj szyfrowanie Odkąd zostanie włączone, szyfrowanie nie może zostać wyłączone. - Twoja domena adresu e-mail nie została dopuszczona do rejestracji na tym serwerze - Niezaufane logowanie Zgadzają się Nie zgadzają się Zweryfikuj użytkownika poprzez potwierdzenie unikalnego ciągu emoji, w tym samym porządku na jego ekranie. Dla najlepszego bezpieczeństwa, użyj innych zaufanych form komunikacji lub zrób to osobiście. Patrz na zieloną tarczę, aby upewnić się czy użytkownik jest zaufany. Zaufaj wszystkim użytkownikom w pokoju, aby upewnić się, że pokój jest bezpieczny. - Nie jest bezpieczny Jeden z poniższych mogł zostać narażony: \n @@ -1929,12 +1585,10 @@ Spróbuj uruchomić ponownie aplikację. \n- Serwer domowy użytkownika, z którym jest on połączony \n- Połączenie z Internetem Twoje lub innych użytkowników \n- Urządzenie Twoje lub innych użytkowników - Wideo. Zdjęcie. Dźwięk Plik - Oczekiwanie… %s anulowana Anulowałeś(-łaś) @@ -1942,25 +1596,17 @@ Spróbuj uruchomić ponownie aplikację. Zaakceptowałeś(-łaś) Żądanie weryfikacji wysłane Żądanie weryfikacji - - Zweryfikuj tą sesję Zweryfikuj ręcznie - Ty - Zeskanuj kod z urządzenia innego użytkownika aby bezpiecznie zweryfikować siebie nawzajem Zeskanuj ich kod Nie można zeskanować Jeżeli nie jesteś z tą osobą, zamiast tego porównaj emoji - Zweryfikuj porównując emoji - Zweryfikuj za pomocą Emoji Jeżeli nie możesz zeskanować kodu powyżej, zweryfikuj porównując krótki, unikalny ciąg emoji. - Obraz kodu QR - Zweryfikuj %s Zweryfikowano %s Oczekiwanie na %s… @@ -1974,57 +1620,41 @@ Spróbuj uruchomić ponownie aplikację. Jedna osoba %1$d osób - + Opuszczanie pokoju… - Administratorzy Moderatorzy Niestandardowy Zaproszenia Użytkownicy - Administrator w %1$s Moderator w %1$s Niestandardowy (%1$d) w %2$s - Przeskocz do znacznika odczytania - Element nie obsługuje wydarzeń typu \'%1$s\' Element nie obsługuje wiadomości typu \'%1$s\' Element napotkał problem przy wyświetlaniu zawartości wydarzenia z ID \'%1$s\' - Nie ignoruj - Sesja nie jest w stanie podzielić się weryfikacją z innymi sesjami. \nWeryfikacja zostanie zapisana lokalnie i udostępniona w przyszłych wersjach aplikacji. - Ostatnie pokoje Inne pokoje - Wysyła wiadomość w odcieniach tęczy Wysyła emoji w odcieniach tęczy - Oś czasu - Edycja wiadomości - Odkąd zostanie włączone, szyfrowanie nie może zostać wyłączone. - Aktywować szyfrowanie\? Odkąd zostanie włączone, szyfrowanie w pokoju nie może zostać wyłączone. Wiadomości wysłane w zaszyfrowanym pokoju nie są widzane przez serwer, a jedynie przez uczestników w pokoju. \nAktywowanie szyfrowania może uniemożliwić wielu botom i mostom prawidłowe działanie. Aktywuj szyfrowanie - Aby być bezpiecznym, zweryfikuj %s poprzez sprawdzenie jednorazowego kodu. Aby być bezpiecznym, zrób to osobiście lub użyj innej metody komunikacji. - Porównaj unikalny ciąg emoji, upewniając się, że pojawiają się w identycznym porządku. Porównaj kod wyświetlany na ekranie innego użytkownika. Wiadomości z użytkownikiem są szyfrowane end-to-end i nie mogą zostać odczytane przez osoby trzecie. Nowa sesja została zweryfikowana. Posiadasz dostęp do zaszyfrowanych wiadomości, a inni użytkownicy będą ją widzieć jako zaufaną. - - Podpis krzyżowy Podpis krzyżowy jest aktywowany. \nKlucze Prywatne znajdują się na urządzeniu. @@ -2034,108 +1664,81 @@ Spróbuj uruchomić ponownie aplikację. Podpis krzyżowy jest aktywowany. \nKlucze nie są zaufane Podpis krzyżowy nie jest aktywowany - - Aktywne Sesje Pokaż wszystkie Sesje Zarządzaj Sesjami Wyloguj z tej sesji - Brak dostępnej informacji o kryptografii - Ta sesja jest zaufana dla bezpiecznej wymiany wiadomości, ponieważ ją zweryfikowałeś(-łaś): Zweryfikuj tą sesję aby oznaczyć ją jako zaufaną i przyznać jej dostęp do zaszyfrowanych wiadomości. Jeżeli nie logowałeś(-łaś) się do tej sesji, twoje konto mogło zostać naruszone: - %d aktywna sesja %d aktywnych sesji - + - Zweryfikuj tą sesję Inni użytkownicy mogą jemu nie ufać Całkowite Bezpieczeństwo - Otwórz obecną sesję i użyj jej do zweryfikowania obecnej, przyznając jej dostęp do zaszyfrowanych wiadomości. - - Zweryfkuj Zweryfikowano Ostrzeżenie - Uzyskanie sesji nie powiodło się Sesje Zaufany Niezaufany - Sesja jest zaufana dla bezpiecznej wymiany wiadomości ponieważ %1$s (%2$s) zweryfikował(-a) ją: %1$s (%2$s) zalogował(-a) się używając nowej sesji: Dopóki użytkownik ufa tej sesji, wiadomości wysłane do oraz od niej będą oznaczone ostrzeżeniami. Ewentualnie, możesz zweryfikować je ręcznie. - - Inicjalizacja podpisu krzyżowego Zresetuj Klucze - Kod QR - Tak Nie - Narzędzia programistyczne Dane konta %d głos %d głosów - + %d głos - wyniki końcowe %d głosów - wyniki końcowe - + Wybrana Opcja Tworzy prostą ankietę Nie masz dostępu do instniejącej sesji\? Użyj klucza odzyskiwania lub hasła - Nowa rejestracja - Nie można odnaleźć tajemnej przestrzeni w pamięci Wprowadź hasło tajemnej przestrzeni Ostrzeżenie: Powinieneś(-nnaś) uzyskać dostęp do tajnej przestrzeni jedynie z zaufanego urządzenia - Usuń… Czy chcesz wysłać załącznik do %1$s\? Wyślij obraz w oryginalnym rozmiarze Wyślij obrazy w oryginalnym rozmiarze - + - Potwierdź Usunięcie Jesteś pewny(-na), że chcesz usunąć to wydarzenie\? Jeżeli usuniesz nazwę pokoju lub zmienisz temat, wciąż będzie możliwe cofnięcie zmiany. Podaj przyczynę Przyczyna reakcji - Wydarzenie usunięte przez użytkownika, przyczyna: %1$s Wydarzenie moderowane przez administratora pokoju, przyczyna: %1$s - Klucze są już aktualne! - Sprawdź Zrezygnuj - Niezgodność klucza Niezgodność użytkowników Nie używasz Serwera Tożsamości Nie skonfigurowano serwera toższamości, który jest wymagany do resetowania hasła. - Wygląda na to, że próbujesz podłączyć się do innego serwera domowego. Czy chcesz się wylogować\? - Przysyła zaproszenie Zaproszono przez %s - Nie masz więcej nieprzeczytanych wiadomości Witaj w domu! Reakcje @@ -2146,39 +1749,44 @@ Spróbuj uruchomić ponownie aplikację. Zdarzenie moderowane przez administratora pokoju Katalog Pokoi Szybkie Reakcje - Ogólne Zasady push Brak zarejestrowanych bramek push - Szyfrowanie miniatury… Nie możesz czegoś odnaleźć\? Utwórz nowy pokój Otwórz katalog pokoi - Nazwa lub ID (#przykład:matrix.org) - Serwer toższamości Odłącz od serwera tożsamości Skonfiguruj serwer tożsamości Zmień serwer tożsamości Aparat Obecnie nie ma połączenia z siecią - Zablokuj użytkownika - Złóż sugestię Tryb programisty aktywuje ukryte funkcje i może również spowodować, że aplikacja będzie mniej stabilna. Tylko dla programistów! Wiadomości w tym pokoju nie są szyfrowane end-to-end. Przesłane pliki Prośby o klucze - Odblokuj historię zaszyfrowanych wiadomości - Element Android - Odśwież - Użyj tej sesji do weryfikacji nowej, nadając jej dostęp do zaszyfrowanych wiadomości. To nie ja - + Zamknij + Wstrzymaj + Odtwórz + Nie posiadasz wymaganych uprawnień do rozpoczęcia połaczenia w tym pokoju + Odbierz + Usuwanie widżetu nie powiodło się + Dodawanie widżetu nie powiodło się + Nie można rozpocząć połączenia z samym sobą, poczekaj aż inni zaakceptują zaproszenie + Nie można rozpocząć połączenia z samym sobą + Rozpocznij połączenie głosowe + Rozpocznij połączenie wideo + Połączenie grupowe już trwa! + Nie posiadasz wymaganych uprawnień aby rozpocząć połączenie grupowe w tym pokoju + Nie posiadasz wymaganych uprawnień do rozpoczęcia połaczenia + Nie posiadasz wymaganych uprawnień aby rozpocząć połączenie grupowe + \ No newline at end of file diff --git a/vector/src/main/res/values-pt-rBR/strings.xml b/vector/src/main/res/values-pt-rBR/strings.xml index 399dff3c89..c50be8c20f 100644 --- a/vector/src/main/res/values-pt-rBR/strings.xml +++ b/vector/src/main/res/values-pt-rBR/strings.xml @@ -1,21 +1,18 @@ - + pt BR - Mensagens Sala Configurações Detalhes do participante Histórico - Aceitar Recusar Encerrar - OK Cancelar @@ -48,7 +45,6 @@ Enviar mesmo assim ou Convidar - Sair Chamada de voz @@ -61,27 +57,22 @@ Fechar Copiado para a área de transferência Desativar - Confirmação Atenção - Início Favoritos Pessoas Salas - Filtrar nomes de salas Filtrar favoritos Filtrar pessoas Filtrar nomes de salas - Convites Baixa prioridade - Conversas Agenda de endereços local @@ -89,7 +80,6 @@ Nenhuma conversa Você não permitiu que o Element acesse seus contatos locais Nenhum resultado - Salas Lista de salas @@ -99,7 +89,6 @@ %d usuário %d usuários - Enviar registros Enviar registros da falha Enviar recorte de tela @@ -112,10 +101,8 @@ Falha ao enviar o relatório de erro (%s) Andamento (%s%%) O aplicativo encerrou inesperadamente da última vez. Gostaria de abrir a tela de relatórios de erros\? - Enviar para Leitura: - Entrar na sala Nome de usuário Criar conta @@ -124,14 +111,11 @@ Endereço do servidor principal Endereço do servidor de identidade Pesquisar - Iniciar nova conversa Iniciar chamada de voz Iniciar chamada de vídeo - Enviar arquivos Tirar uma foto ou gravar vídeo - Entrar Criar conta @@ -180,7 +164,6 @@ Sua senha foi alterada. \n \nVocê foi desconectado de todas as sessões e não receberá mais notificações. Para reativar as notificações, faça login novamente em cada aparelho. - O endereço precisa começar com http[s]:// Não foi possível fazer login: erro de rede @@ -189,7 +172,6 @@ Não foi possível criar conta Não foi possível criar conta: falha na verificação de posse do e-mail Por favor, digite um endereço válido - Nome de usuário ou senha inválido O token de acesso especificado não foi reconhecido JSON mal formatado @@ -197,54 +179,43 @@ Muitas solicitações foram enviadas Este nome de usuário já está em uso O link no e-mail ainda não foi clicado - - Lista de confirmações de leitura - - Enviar como Original Grande Médio Pequeno - Cancelar o download? Cancelar o envio\? %d s %1$dm %2$ds - Ontem Hoje - Nome da sala Descrição da sala - Chamada aceita - Chamando… + Iniciando chamada… Chamada encerrada Chamando… Recebendo chamada Recebendo chamada de vídeo Recebendo chamada de voz Chamada em andamento… - A pessoa não atendeu a chamada. A conexão à mídia falhou Não foi possível iniciar a câmera chamada atendida em outro lugar - Tirar uma foto ou gravar vídeo Não é possível gravar vídeo - Informação Element precisa de permissão para acessar sua galeria de fotos e vídeos para enviar e salvar anexos. @@ -265,25 +236,20 @@ Element precisa de permissão para acessar os seus contatos para poder encontrar outros usuários a partir de seus e-mails e números de telefone. \n \nVocê concorda em usar a sua lista de contatos para esse propósito\? - Desculpe. A ação não foi realizada, por falta de permissão - Salvo Salvar nos downloads? SIM NÃO Continuar - Apagar Entrar Visualizar Recusar - Ir para a primeira mensagem não lida. - Você foi convidada(o) a entrar nesta sala por %s Este convite foi enviado a %s, que não está associado com esta conta. @@ -291,27 +257,22 @@ Você está tentando acessar %s. Quer entrar na sala para poder participar da conversa? uma sala Esta é uma pré-visualização desta sala. Interações com esta sala estão desativadas. - Nova conversa Adicionar uma pessoa 1 participante - Sair da sala Tem certeza de que deseja sair da sala\? Deseja remover %s desta conversa\? Criar - Online Offline Ocioso - FERRAMENTAS DE ADMINISTRAÇÃO CHAMADA Conversas SESSÕES - Convidar Sair da sala Remover desta sala @@ -327,18 +288,14 @@ Mostrar lista de sessões Você não poderá desfazer esta alteração, já que você está promovendo este usuário para ter o mesmo nível de permissões que você. \nTem certeza\? - "Você tem certeza que quer convidar %s para esta conversa?" - Convidar por ID CONTATOS LOCAIS (%d) Apenas usuários Matrix - Convidar pessoa por sua ID Por favor, digite um ou mais endereços de e-mail ou ID Matrix E-mail ou ID Matrix - Pesquisar %s está digitando… @@ -355,7 +312,6 @@ Apagar mensagens não enviadas Arquivo não encontrado Você não tem permissão para digitar nesta sala - Confiar Não confiar @@ -368,7 +324,6 @@ Você tinha um certificado confiável para o seu telefone, mas ele mudou. Isso é ALTAMENTE INCOMUM. É recomendável que você NÃO ACEITE este novo certificado. O certificado foi alterado de um anteriormente confiável para um que não é confiável. O servidor pode ter renovado seu certificado. Entre em contato com o administrador do servidor para obter a impressão digital esperada. Apenas aceite o certificado se o administrador do servidor publicou uma impressão digital que é idêntica a que está acima. - Detalhes da sala Pessoas @@ -377,7 +332,6 @@ ID mal formatado. Precisa ser um endereço de e-mail ou um ID Matrix, como \'@participantelocal:dominio\' CONVIDADOS ENTRARAM - Motivo de denunciar este conteúdo Deseja ocultar todas as mensagens deste usuário\? @@ -385,7 +339,6 @@ \nEsta ação irá reiniciar o aplicativo e poderá demorar um pouco. Cancelar envio Cancelar download - Pesquisar Pesquisar participantes da sala @@ -394,7 +347,6 @@ MENSAGENS PESSOAS ARQUIVOS - ENTRAR LISTA PÚBLICA @@ -407,18 +359,15 @@ Entrar na sala Entrar em uma sala Digite o ide ou o apelido de uma sala - Pesquisar na lista pública Buscando no diretório… - Favoritar Despriorizar Conversa direta Sair da conversa Esquecer - Mensagens Configurações @@ -427,9 +376,7 @@ Licenças de terceiros Direito autoral Política de privacidade - - Foto de perfil Nome e sobrenome E-mail @@ -438,22 +385,18 @@ Adicionar número de telefone Mostrar informações do aplicativo nas configurações do sistema. Informações sobre o aplicativo - Receba notificações de novas mensagens Ativar notificações nesta sessão Acender a tela por 3 segundos - Mensagens em conversas individuais Mensagens em salas Quando eu for convidada(o) a uma sala Recebendo chamada Mensagens enviadas por bots - Sincronização em segundo plano Ativar a sincronização em segundo plano Tempo expirado na solicitação de sincronização Demora entre cada solicitação - Versão Versão do olm Termos e condições @@ -462,8 +405,6 @@ Política de privacidade Limpar cache e recarregar - - Configurações de usuário Notificações Usuários bloqueados @@ -488,19 +429,15 @@ Autenticação Senha: Enviar - Conectado como Servidor Principal (Home Server) Servidor de identidade - Confirmação pendente Por favor, verifique o seu e-mail e clique no link que está lá. Feito isso, clique em continuar. Não não foi possível confirmar o seu endereço de e-mail. Por favor, verifique o seu e-mail e clique no link que ele contém. Feito isso, clique em continuar. - Este endereço de e-mail já está em uso. Este endereço de e-mail não foi encontrado. Este número de telefone já está em uso. - Alterar senha Senha atual Nova senha @@ -510,13 +447,9 @@ Mostrar todas as mensagens de %s\? \n \nNote que esta ação irá reiniciar o aplicativo e pode levar algum tempo. - Deseja deixar de notificar este aparelho\? - Deseja remover o %1$s %2$s\? - Escolha um país - País Por favor, escolha um país Número de telefone @@ -526,44 +459,36 @@ Digite o código de ativação Erro ao validar o seu número de telefone Código - - Foto da sala Nome da sala Descrição Etiqueta da sala Etiquetada como: - Favoritar Baixa prioridade Nenhuma - Acesso e visibilidade - Exibir esta sala no diretório público de salas + Exibir esta sala na lista pública de salas Acesso à sala Legibilidade do histórico da sala Quem pode ler o histórico de mensagens? Quem pode acessar esta sala? - Qualquer pessoa Apenas participantes (a partir do momento em que esta opção foi escolhida) Apenas participantes (desde que foram convidados) Apenas participantes (desde que entraram na sala) - Para fazer um link para uma sala, ela precisa ter um endereço. Apenas as pessoas que foram convidadas Qualquer pessoa que saiba o link da sala, exceto visitantes Qualquer pessoa que saiba o link da sala, incluindo visitantes - Usuários banidos - Avançado ID interno desta sala @@ -575,36 +500,27 @@ Você precisa desconectar para poder ativar a criptografia. Criptografar apenas para sessões confirmadas Nunca enviar mensagens criptografadas para sessões não confirmadas nesta sala, a partir desta sessão. - Esta sala não tem endereços locais Novo endereço (p.ex: #foo:matrix.org") - Formato inválido de alias \'%s\' não é um formato válido para um alias Você não terá endereço principal especificado para esta sala. Alertas do endereço principal - Definir como Endereço Principal Retirar este endereço como principal Copiar o ID desta sala Copiar o endereço desta sala - A criptografia está ativada nesta sala. A criptografia está desativada nesta sala. Ativar criptografia· \n(atenção: não é possível desativar depois!) - Diretório - %s tentou carregar um trecho específico da conversa desta sala, mas não conseguiu. - - Informação sobre a criptografia de ponta a ponta - Informação do evento ID de usuária/o Chave de identidade curve25519 @@ -612,7 +528,6 @@ Algoritmo ID de Sessão Erro de descriptografia - Informação sobre a sessão do remetente Nome público do aparelho Nome @@ -620,7 +535,6 @@ Chave da sessão Confirmação Impressão digital ed25519 - Exportar chaves de sala E2E Exportar chaves de sala Exportar as chaves para um arquivo local @@ -630,31 +544,25 @@ As chaves de sala E2E foram salvas em \'%s\' Atenção: este arquivo poderá ser apagado se o aplicativo for desinstalado. - Importar chaves de sala E2E Importar chaves de sala Importar as chaves de um arquivo local Importar Criptografar apenas para sessões confirmadas Nunca enviar mensagens criptografadas para sessões não confirmadas, a partir desta sessão. - Não confirmado Confirmado Na lista negra - sessão desconhecida nenhuma - Confirmar Marcar como não confirmado Colocar na lista negra Retirar da lista negra - Confirmar sessão Compare as seguintes informações com aquelas na sessão do outro usuário e confirme: Se não corresponderem, a segurança da sua comunicação pode estar comprometida. Eu confirmo que as chaves são iguais - Esta sala contém sessões desconhecidas Esta sala contém sessões desconhecidas que não foram confirmadas. @@ -662,7 +570,6 @@ Atenção: este arquivo poderá ser apagado se o aplicativo for desinstalado. - Escolha uma lista pública de salas O servidor pode estar indisponível ou sobrecarregado @@ -670,31 +577,24 @@ Atenção: este arquivo poderá ser apagado se o aplicativo for desinstalado.Endereço do servidor principal Todas as salas com o servidor %s Todas as salas nativas em %s - Pesquisar no histórico Desconectado - Lista de usuários LISTA DE USUÁRIAS(OS) (%s) Iniciar com o sistema Esvaziar o cache de mídia Manter mídia - Mostrar a hora para todas as mensagens Modo de economia de dados - Aparência Idioma Escolha o idioma - 3 dias 1 semana 1 mês Para sempre - Tema - Tamanho da fonte Minúsculo Pequeno @@ -706,67 +606,49 @@ Atenção: este arquivo poderá ser apagado se o aplicativo for desinstalado.Tema claro Tema escuro Tema preto - Sincronizando… Escutando eventos Notificações com som Notificações silenciosas - Relatar um erro Detalhes da comunidade - Carregando… - Sair Comunidades - Filtrar nomes de comunidades - Convidar Comunidades Nenhuma comunidade - Tem certeza de que deseja iniciar uma nova conversa com %s\? Tem certeza de que deseja iniciar uma chamada de voz\? Tem certeza de que deseja iniciar uma chamada de vídeo\? - Tirar foto Gravar vídeo - Lista de comunidades - Chamada de voz Banir o usuário resultará em sua remoção desta sala, impedindo-o de entrar nela novamente. - Todas as mensagens novas (com som) Todas as mensagens novas Apenas @menções Silenciar - Adicionar atalho na tela inicial - + Adicionar o Element na tela inicial Som de notificação Mensagens que contenham o meu nome e sobrenome Mensagens que contenham o meu nome de usuário Visualização prévia do endereço Mostrar a hora no formato de 12 horas Vibrar ao mencionar um usuário - Estatísticas de uso - Ícone - Notificações Esta sala não está mostrando ícones de nenhuma comunidade Nova ID da comunidade (p.ex: +foo:matrix.org) ID de comunidade inválido \'%s\' não é um ID de comunidade válido - - Você necessita ter permissões para poder gerenciar os widgets desta sala A criação do widget falhou Crie chamadas em grupo com jitsi Você tem certeza que quer apagar este widget desta sala? - Impossível criar o widget. O envio do pedido falhou. @@ -778,28 +660,22 @@ Atenção: este arquivo poderá ser apagado se o aplicativo for desinstalado.A sala %s não está visível. Adicionar aplicativos Matrix Usar a câmera nativa - Você adicionou uma nova sessão \'%s\', que está solicitando as chaves de criptografia. Sua sessão não confirmada \'%s\' está solicitando as chaves de criptografia. Iniciar a confirmação Compartilhar sem confirmar Ignorar a solicitação - Atenção! Chamadas em grupo estão em desenvolvimento, portanto podem não ser estáveis. - Erro de comando Comando desconhecido: %s - Desativado Ativado com som - Mensagem criptografada - Criar Criar comunidade @@ -807,21 +683,17 @@ Atenção: este arquivo poderá ser apagado se o aplicativo for desinstalado.Exemplo ID da comunidade exemplo - Início Pessoas Salas Sem ninguém - Salas Entrou em que foi convidada/o Pesquisar participantes da comunidade Filtrar salas da comunidade - O(A) administrador(a) desta comunidade não definiu uma descrição longa da mesma. - Você foi removido da sala %1$s por %2$s Você foi banido da sala %1$s devido à %2$s Motivo: %1$s @@ -842,7 +714,6 @@ Atenção: este arquivo poderá ser apagado se o aplicativo for desinstalado.%d nova mensagem %d novas mensagens - %d sala %d salas @@ -852,92 +723,75 @@ Atenção: este arquivo poderá ser apagado se o aplicativo for desinstalado.%1$s salas encontradas para %2$s - 1 alteração na filiação + %d alteração na filiação %d alterações na filiação - Lista de participantes - %d mensagem notificada não lida - %d mensagens notificadas não lidas + %d mensagem não lida + %d mensagens não lidas - %d mensagem notificada não lida - %d mensagens notificadas não lidas + %d mensagem não lida + %d mensagens não lidas %d sala %d salas %1$s em %2$s - %d widget ativo %d widgets ativos - Foto de perfil para a confirmação de leitura Foto de perfil para avisos Foto de perfil - Agite rapidamente para relatar um erro - Normal Privacidade reduzida O app necessita de permissão para rodar em segundo plano Enviar uma figurinha - Licenças de terceiros - Baixar Falar Limpar Devido à falta de permissões, essa ação não é possível. Alertas do sistema - Se possível, escreva a descrição em inglês, por favor. Enviar áudio - Enviar uma figurinha No momento, você não tem nenhum pacote de figurinhas ativado. \n \nQuer adicionar alguns agora\? - continuar com… Desculpe, nenhum aplicativo externo foi encontrado para concluir esta ação. - Solicitar novamente as chaves de criptografia das suas outras sessões.Pedir novamente as chaves de criptografia de seus outros dispositivos. - Requisição de chave enviada. - Requisição enviada Por favor, inicie o Element em outro aparelho que possa descriptografar a mensagem, de modo que ele possa enviar as chaves para esta sessão. - - 1s + %ds %ds - 1m + %dm %dm - 1h + %dh %dh %d dia %d dias - %1$s agora há %1$s %2$s - "%1$s, " %1$s e %2$s %1$s %2$s - Digite sua resposta criptografada… Digite sua resposta (não criptografada)… @@ -950,31 +804,23 @@ Atenção: este arquivo poderá ser apagado se o aplicativo for desinstalado.• O conteúdo das mensagens da notificação é armazenado de forma segura diretamente no servidor Matrix • As notificações contém metadados e os dados da mensagem • As notificações não exibirão o conteúdo da mensagem - Pré-visualizar a mídia antes de enviá-la - Desativar minha conta Desativar minha conta - Privacidade das notificações Element pode funcionar em segundo plano para gerenciar as suas notificações de forma segura e confidencial. Isso poderá impactar o uso da bateria. Conceder a permissão Escolha outra opção - Enviar dados de uso Element coleta dados de uso anônimos para nos ajudar a melhorar o aplicativo. Por favor, ative o envio de dados de uso para nos ajudar a melhorar o Element. Sim, eu quero ajudar! - Você não faz parte de alguma comunidade, no momento. - Escreva aqui… - Um parâmetro obrigatório está faltando. Um parâmetro não é válido. Use a tecla Enter do teclado para enviar a mensagem Enviar mensagens de voz - Mostra a ação Bane o usuário com a ID fornecida Remove o banimento do usuário com a ID fornecida @@ -988,19 +834,16 @@ Atenção: este arquivo poderá ser apagado se o aplicativo for desinstalado.Altera o seu nome e sobrenome Ativar/Desativar a formatação de texto Reparar a gestão de aplicativos Matrix - %d participante %d participantes - %d sala %d salas Para continuar usando o Servidor de Base %1$s, você precisa revisar e aceitar os termos e condições de uso. Revisar agora - Desativar minha conta Isso tornará sua conta permanentemente inutilizável. Você não conseguirá efetuar login e ninguém poderá registrar novamente o mesmo ID de usuário. Isso fará com que sua conta saia de todas as salas das quais está participando e removerá os detalhes de sua conta do servidor de identidade. Esta ação é irreversível. \n @@ -1010,43 +853,30 @@ Atenção: este arquivo poderá ser apagado se o aplicativo for desinstalado.Quando minha conta for desativada, apague todas as mensagens que eu enviei (Atenção: isso fará com que futuros usuários tenham uma visão incompleta das conversas) Para continuar, entre com sua senha: Desativar minha conta - Por favor, digite sua senha. Esta sala foi substituída e não está mais ativa A conversa continua aqui Esta sala é a continuação de outra conversa Clique aqui para ver as mensagens anteriores - Limite de recursos excedido Entre em contato com o(a) administrador(a) - entre em contato com o administrador do seu serviço - Este servidor local excedeu um dos seus limites de recursos, portanto alguns usuários não conseguirão fazer login. Este servidor local excedeu um de seus limites de recursos. - Este homeserver atingiu o seu limite mensal de usuários ativos, portanto alguns usuários não conseguirão fazer login. Este homeserver atingiu o seu limite mensal de usuários ativos. - Por favor, %s para que este limite seja aumentado. Por favor, %s para seguir usando este serviço. - Crie uma frase secreta para criptografar as chaves exportadas. Você precisará inserir essa frase secreta para importar chaves criptografadas. Tema Status.im - Aceitar - Erro - Por favor, revise e aceite as políticas deste servidor local: - Chamadas Use o toque padrão do Element para chamadas recebidas Toque de chamadas recebidas Selecione o toque de chamadas: - Motivo - Versão %s Resolver problemas nas notificações Diagnóstico de resolução de problemas @@ -1055,65 +885,53 @@ Atenção: este arquivo poderá ser apagado se o aplicativo for desinstalado.O diagnóstico básico está ok. Se você ainda não recebe notificações, por favor relate um erro para nos ajudar a investigar. Um ou mais testes falharam, tente as correções sugeridas. Um ou mais testes falharam, por favor relate um erro para nos ajudar a investigar. - Configurações do sistema Notificações estão ativadas nas configurações do sistema. Notificações estão desativadas nas configurações do sistema. Por favor, revise as configurações do sistema. Abra as Configurações - Configurações da conta Notificações estão ativadas para sua conta. Notificações estão desativadas para sua conta. \nPor favor, revise as configurações da conta. Ativar - Configurações da sessão Notificações estão ativadas nesta sessão. Remover da sala Notificações não estão ativadas nesta sessão. \nPor favor, revise as configurações do Element. Ativar - Versão do Google Play Services Google Play Services APK está disponível e atualizado. Element usa Google Play Services para entregar mensagens push, mas isto não parece está configurado corretamente: %1$s Consertar Play Services - Token do Firebase Token FCM recuperado com sucesso: %1$s Ligar mesmo assim Falha ao recuperar o token do FCM: %1$s - Registro do token do Firebase Token FCM registrado com sucesso no HomeServer. Falha ao registrar o token FCM no HomeServer: %1$s - Serviço de Notificações Serviço de Notificações está em execução. Serviço de Notificações não está em execução. Tente reiniciar a aplicação. Iniciar Serviço - O serviço foi encerrado e reiniciado automaticamente. Falha ao reiniciar Serviço - Começar na inicialização O serviço iniciará quando o aparelho for reiniciado. O serviço não iniciará enquanto o aparelho não for reiniciado. Você não receberá notificações até que o Element for aberto ao menos uma vez. Iniciar com o sistema - Revisar restrições de segundo plano Otimização de bateria Element não é afetado pela Otimização de Bateria. Reinicialização Automática do Serviço de Notificações Desativar restrições - Ignorar a otimização - Prévia de links dentro do chat quando seu homeserver suporta este recurso. Permitir que saibam quando eu estiver digitando Deixe outros usuários saberem quando você estiver digitando. @@ -1134,81 +952,62 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Formatação de texto Formatar o texto das mensagens a serem enviadas. Por exemplo: inserir asteriscos antes e depois do texto, mostrará o texto em itálico. Conceder permissão - Ocorreu um erro ao confirmar seu endereço de e-mail. - Senha Ocorreu um erro ao confirmar seu número de telefone. Informação adicional: %s - Inicie a câmera do sistema em vez da câmera personalizada. Esta opção requer um aplicativo de terceiros para gravar mensagens de voz. - O comando \"%s\" precisa de mais parâmetros ou alguns parâmetros estão incorretos. A formatação de texto foi ativada. A formatação de texto foi desativada. - Aumente o desempenho apenas carregando os participantes da sala na primeira exibição. Seu servidor principal ainda não suporta o carregamento Lazy de participantes da sala. Tente depois. - Desculpe, ocorreu um erro - expandir colapsar - Mostrar a área de informações Sempre Para mensagens e erros Somente para erros - %1$s: %1$s: %2$s +%d %d+ Nenhum APK do Google Play Services válido foi encontrado. Notificações podem não funcionar corretamente. - Se um usuário deixar um aparelho desconectado por um período de tempo, com a tela desligada, o aparelho entrará no modo Soneca. Isso impede que os aplicativos acessem a rede, adiando seus trabalhos, sincronizações e alarmes padrão. Criar frase secreta A frase secreta está errada Carregamento Lazy dos participantes das salas - Chamada de vídeo em andamento… - Backup da chave Usar Backup da chave - Backup de chaves não está concluído. Por favor, aguarde… Você perderá suas mensagens criptografadas se sair agora Backup de chave em andamento. Se você sair agora, perderá o acesso às suas mensagens criptografadas. - O backup de chave segura deve estar ativado em todas as suas sessões, para evitar perder o acesso às suas mensagens criptografadas. + O backup de chave deve estar ativado em todas as suas sessões, para evitar perder o acesso às suas mensagens criptografadas. Não quero minhas mensagens criptografadas Fazendo backup das chaves… Use o backup de chave Tem certeza\? Fazer o backup Você perderá o acesso às suas mensagens criptografadas, a menos que faça backup das suas chaves antes de sair. - Assinatura - Ficar Pular Fechar Abortar - Deseja mesmo sair\? Configurar notificações Configurar notificações por evento - Configurações personalizadas Observe que alguns tipos de mensagens estão configurados para serem silenciosos (produzirão uma notificação sem som). Algumas notificações estão desativadas nas suas configurações personalizadas. Falha ao carregar regras personalizadas, tente novamente. Verifique as configurações - [%1$s] \nEste erro está fora do controle do Element e, de acordo com o Google, esse erro indica que o aparelho tem muitos aplicativos registrados com FCM. O erro só ocorre nos casos em que há números extremos de aplicativos, portanto, isso não deve afetar o usuário comum. Bloquear - Entre com o login único Este endereço não está acessível. Por favor, verifique-o Seu aparelho está usando um protocolo de segurança TLS desatualizado, vulnerável a ataques. Para sua segurança, você não poderá se conectar @@ -1218,48 +1017,36 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr [%1$s] \nEste erro está fora de controle do Element. Não há conta do Google no celular. Por favor, abra o gerenciador de contas e adicione uma conta do Google. Adicionar Conta - Configurar notificações com som Configurar notificações de chamada Configurar notificações silenciosas Escolha a cor do LED, vibração, som… - - Gerenciamento de Chaves de Criptografia Enviar mensagem com a tecla enter Latn - Confirmar sessão - Nenhuma Revogar Desconectar Revisar Recusar - Marcar como lida Nenhum servidor de identidade está configurado. - A chamada falhou por conta de má configuração no servidor Reproduzir Pausar Descartar - - Copiar Pronto - Notificações Peça ao administrador do seu servidor principal (%1$s) que configure um servidor TURN para que as chamadas funcionem de maneira confiável. \n \nAlternativamente, você pode tentar usar o servidor público em %2$s. No entanto, ele não é tão confiável e compartilhará o seu IP com esse servidor. Você também pode configurar isso nas Configurações. Tente usar %s Não pergunte novamente - A chamada falhou Falha ao estabelecer conexão em tempo real. \nPor favor, peça ao administrador do seu servidor para configurar um servidor TURN, de modo que as chamadas funcionem de maneira estável. - Selecione a caixa de som Celular Alto-falante @@ -1270,7 +1057,6 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Traseira Desativar vídeo de alta definição Ativar vídeo de alta definição - Defina um e-mail para recuperação da conta. Posteriormente, você pode permitir que as pessoas encontrem você através dele. Defina um número de telefone. Posteriormente, você pode permitir que as pessoas encontrem você através dele. Defina um e-mail para a recuperação da conta. Posteriormente, um endereço de e-mail ou número de telefone pode ser usado para ser encontrado por outras pessoas. @@ -1283,14 +1069,11 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Permitir a assistência do servidor de chamadas reserva %s quando seu servidor não oferecer este serviço (seu endereço IP será transmitido quando você ligar) Chamada em andamento (%s) Retornar à chamada - Adicione um servidor de identidade nas suas configurações para executar esta ação. Cancelar convite Reduzir privilégios\? Você não poderá desfazer essa alteração, já que está reduzido seus privilégios. Se você for a última pessoa nesta sala, será impossível recuperar a permissão atual. Reduzir privilégio - - Bloquear usuário Ao bloquear este usuário, as mensagens dele serão ocultadas de você em todas as salas. \n @@ -1308,14 +1091,11 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Motivo do banimento Remover banimento do usuário Remover o banimento do usuário permitirá que ele entre novamente na sala. - Confirme sua senha Não pode fazer isto pelo Element app Autenticação exigida - - O aplicativo não precisa de se conectar ao servidor em segundo plano, isto deve reduzir a utilização da bateria - Sincronização em segundo plano (Experimental) + Sincronização em segundo plano Optimizado para bateria Element sincronizará em segundo plano para preservar os recursos limitados do aparelho (bateria). \nDependendo do estado dos recursos do seu aparelho, a sincronização pode ser adiada pelo sistema operacional. @@ -1325,68 +1105,55 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Sem sincronização em segundo plano Você não será notificado sobre mensagens recebidas quando o Element estiver em segundo plano. Não foi possível actualizar a configuração. - - Intervalo de sincronização preferido %s \nA sincronização pode ser adiada dependendo dos recursos (bateria) ou do estado do aparelho (modo de suspensão). Integrações - Use o Gerenciador de Integrações para gerenciar bots, pontes, widgets e pacotes de figurinhas. + Use o Gerenciador de Integrações para gerenciar bots, integrações, widgets e pacotes de figurinhas. \nO Gerenciador de Integrações recebe dados de configuração e pode modificar widgets, enviar convites para salas e definir níveis de permissão em seu nome. Botão enter do teclado enviará a mensagem em vez de adicionar uma quebra de linha - - Backup Seguro + Backup online Gerenciar - Configurar Backup Seguro - Restaurar Backup Seguro + Configurar backup online + Restaurar backup online Configurar neste aparelho Prevenir contra a perda de acesso a mensagens e dados encriptados, guardando as chaves de encriptação no seu servidor. Gerar uma nova Chave de Segurança ou definir uma nova Frase de Segurança para o seu backup existente. Isto irá substituir a sua Chave ou Frase actual. - Encontrar Gerencie suas configurações de descoberta. O modo de baixo uso de dados desativa a atualização de presença e a animação de que alguém está digitando. - Permitir integrações Gerenciador de Integrações - As integrações estão desativadas Ative \'Permitir integrações\' nas Configurações para fazer isso. - Atualizar Senha A senha não é válida As senhas não são iguais - Média Compressão predefinida Escolher Fonte de mídia por padrão Escolher Tocar o som da câmera - %d usuário banido %d usuários banidos - O nome público (visível para as pessoas com quem você se comunica) O nome público de uma sessão é visível para as pessoas com quem você se comunica Chaves exportadas com sucesso - Recuperação de mensagens criptografadas - Gerenciar Backup das Chaves - + Configurar backup das chaves IP desconhecido - %1$s: 1 mensagem + %1$s: %2$d mensagem %1$s: %2$d mensagens %d notificação %d notificações - Novo Evento Sala Novas Mensagens @@ -1395,11 +1162,8 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr ** Não foi possível enviar - por favor abra a sala %1$s: %2$s %1$s: %2$s %3$s - VISTA Widgets Ativos - - Widget Carregar widget Widget adicionado por: @@ -1410,15 +1174,12 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Recarregar widget Abrir no navegador Remover para mim - O seu nome e sobrenome Link da sua foto de perfil Sua ID de usuário Seu tema ID do widget ID da sala - - Desculpe, as chamadas em grupo com o Jitsi não são suportadas em aparelhos antigos (com versões do Android anteriores a 5.0) Este wigdet deseja utilizar os seguintes recursos: Permitir @@ -1426,10 +1187,8 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Usar a câmera Usar o microfone Ler as mídias protegidos por DRM - O gerenciador de integrações não está configurado. Para continuar, você precisa aceitar os termos de serviço. - Uma nova sessão está solicitando chaves de criptografia. Nome da sessão: %1$s \nVisto por último às: %2$s \nSe você não fez login em outra sessão, ignore essa solicitação. @@ -1437,38 +1196,33 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr \nNome da sessão: %1$s \nVisto por último às: %2$s \nSe você não fez login em outra sessão, ignore essa solicitação. - Confirmar Compartilhar Pedido de compartilhamento das Chaves Ignorar - Ativado Digite o nome de usuário. Digite a sua frase secreta A frase secreta é muito fraca - Por favor, apague a frase secreta se desejar que o Element gere uma chave de recuperação. Nenhuma sessão Matrix disponível - Nunca perca mensagens criptografadas - As mensagens em salas criptografadas são protegidas com a criptografia de ponta a ponta. Somente você e o(s) destinatário(s) têm as chaves para ler essas mensagens. -\n -\nFaça backup de suas chaves de segurança para evitar perdê-las. - Comece a usar o Backup de Chave + As mensagens em salas criptografadas são protegidas com a criptografia de ponta a ponta. Somente você e o(s) destinatário(s) têm as chaves para ler essas mensagens. +\n +\nFaça backup de suas chaves para evitar perdê-las. + Comece a fazer o backup de chave (Avançado) Exportar as chaves manualmente - Proteja seu backup com uma frase secreta. Armazenaremos uma cópia criptografada de suas chaves em nosso servidor. Proteja seu backup com uma frase secreta para mantê-lo seguro. -\n -\nPara segurança máxima, isso deve ser diferente da senha da sua conta. +\n +\nPara segurança máxima, a frase secreta deve ser diferente da senha da sua conta. Criar frase secreta - Criar Backup + Criando backup Ou proteja seu backup com uma chave de recuperação, salvando-a em algum lugar seguro. (Avançado) Configurar a chave de recuperação Parabéns ! - Está sendo feito backup das suas chaves. + O backup das suas chaves está sendo feito. Sua chave de recuperação é uma rede de proteção - você pode usá-la para restaurar o acesso às suas mensagens criptografadas se você esquecer sua frase de recuperação. \nMantenha a sua chave de recuperação em algum lugar muito seguro, como um gerenciador de senhas (ou um cofre) Mantenha sua chave de recuperação em algum lugar muito seguro, como um gerenciador de senhas (ou um cofre) @@ -1481,38 +1235,29 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr \n \nAtenção: este arquivo pode ser removido se o aplicativo for desinstalado. A chave de recuperação foi guardada. - - Já existe uma cópia de segurança no servidor - Parece que você já tem configuração backup da chave de outra sessão. Queres substituí-lo pela nova que estás criando \? + Já existe um backup no seu servidor + Parece que você já configurou um backup de chaves em outra sessão. Deseja substituí-lo por um novo backup\? Substituir Parar - Faça uma cópia Compartilhar chave de recuperação com… Gerando Chave de Recuperação usando a frase secreta, este processo pode levar alguns segundos. Chave de recuperação Erro inesperado - A Cópia de Segurança foi iniciada - As suas chaves de encriptação estão sendo copiadas em segundo plano para o seu servidor. O backup inicial pode demorar vários minutos. - - + Começando o backup + O backup de suas chaves está sendo feito. O primeiro backup pode demorar vários minutos. Você tem certeza\? Você pode perder o acesso às suas mensagens se você sair do Element ou perder este aparelho. - - Obtendo versão do backup… + Obtendo a versão do backup… Use sua frase secreta de recuperação para desbloquear seu histórico de mensagens seguras use sua chave de recuperação Não sabe sua frase secreta de recuperação, você pode %s. - Use a sua Chave de Recuperação para desbloquear o seu histórico de mensagens criptografadas Digite a chave de recuperação - Recuperação de Mensagem - Perdeu sua chave de recuperação\? Você pode configurar uma nova nas configurações. - O backup não pôde ser descriptografado com essa frase secreta: verifique se você digitou a frase secreta de recuperação correta. + O backup não pôde ser descriptografado com essa frase secreta: verifique se você digitou corretamente a frase secreta de recuperação. Erro de rede: verifique a sua conexão e tente de novo, por favor. - Restaurando o backup: Processando a chave de recuperação… Baixando as chaves… @@ -1520,73 +1265,55 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Desbloquear Histórico Digite uma chave de recuperação O backup não pôde ser descriptografado com essa chave de recuperação: verifique se você digitou a chave de recuperação correta. - Backup restaurado %s ! - Restaurou um backup com chave %d. - Restaurou um backup com chaves %d. + O backup restaurou %d chave. + O backup restaurou %d chaves. %d nova chave foi adicionada a esta sessão. %d novas chaves foram adicionadas a esta sessão. - Não foi possível obter a versão mais recente das chaves de recuperação (%s). A sessão criptografada não está activa - - Restaurar do backup - Excluir Backup - - A chave do backup foi configurada correctamente para esta sessão. - A chave do backup não está activa nesta sessão. - Não está sendo feito backup das suas chaves nesta sessão. - - O backup tem uma assinatura da sessão desconhecida com o ID %s. + Remover backup + O backup da chave foi configurado corretamente para esta sessão. + O backup da chave não está configurado nesta sessão. + O backup das suas chaves não está sendo feito nesta sessão. + O backup tem uma assinatura de uma sessão desconhecida com ID %s. O backup tem uma assinatura válida desta sessão. O backup tem uma assinatura válida da sessão confirmada %s. O backup tem uma assinatura válida da sessão não confirmada %s O backup tem uma assinatura inválida da sessão confirmada %s O backup tem uma assinatura inválida da sessão não confirmada %s - Não foi possível obter a informação de confiança para o backup (%s). - + Não foi possível saber se o backup é confiável (%s). Para usar o Backup de Chaves nesta sessão, restaure com a sua frase secreta ou chave de recuperação agora. - Excluindo o backup… - Não foi possível excluir o backup (%s) - + Removendo o backup… + Não foi possível remover o backup (%s) Verificando a situação do backup - Excluir Backup + Remover backup Excluir as chaves de criptografia do servidor\? Você não será mais capaz de usar a sua chave de recuperação para ler o histórico de mensagens criptografadas. - - Novo Backup da Chave - Foi detectado um novo backup da chave de mensagem. -\n + Novo backup de chave + Foi detectado um novo backup de chave. +\n \nSe você não definiu o novo método de recuperação, um invasor pode estar tentando acessar sua conta. Altere a senha da sua conta e defina um novo método de recuperação imediatamente nas Configurações. Foi eu - Nunca perca mensagens criptografadas - Comece a usar o Backup da Chave - - Backup Seguro + Comece a fazer o backup da chave + Backup online Proteja-se contra a perda de acesso a mensagens e dados criptografados - Nunca perca mensagens criptografadas - Use o backup da chave - + Fazer o backup da chave Novas chaves de mensagens seguras - Gerenciar Backup das Chaves - - Fazendo backup das chaves. Isto pode demorar vários minutos.… - - - Configurar Backup Seguro - + Configurar o backup das chaves + Fazendo backup das chaves. Isso pode demorar vários minutos… + Configurar o backup online O backup de todas as chaves foi concluído - Fazendo o backup da chave %d … - Fazendo o backup das chaves %d … + Fazendo o backup de %d chave… + Fazendo o backup de %d chaves… - Versão Algoritmo Resposta de descoberta inválida no servidor local @@ -1594,42 +1321,33 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Element detectou uma configuração personalizada do servidor para a sua ID de usuário \"%1$s\": \n%2$s Usar a Configuração - Você foi desligado devido as credenciais inválidas ou caducadas. - Confirmar comparando um texto curto. Para máxima segurança, recomendamos que você faça isso pessoalmente ou use outro meio de comunicação confiável. Iniciar a confirmação Recebendo solicitação de confirmação Confirme se esta sessão é sua. Confirmar sessões fornece segurança adicional, ao usar mensagens criptografadas de ponta a ponta. Ao confirmar se esta sessão é sua, você também a tornará confirmada para o seu contato. - Confirme esta sessão, comparando os emojis a seguir que serão exibidos para você e seu contato Confirme esta sessão, comparando os números a seguir que serão exibidos para você e seu contato - Você recebeu uma solicitação de confirmação. Ver pedido Aguardando seu contato confirmar… - Confirmado! Você confirmou essa sessão com êxito. As mensagens com este usuário estão criptografadas de ponta a ponta e não podem ser lidas por terceiros. Ok, entendi - Não aparece nada\? Nem todos os clientes suportam a confirmação interativa. Use a confirmação tradicional. Use a confirmação tradicional. - Verificação da chave Pedido Cancelado Seu contato cancelou a confirmação. \n%s A confirmação foi cancelada. \nMotivo: %s - Confirmação interativa da sessão Solicitação de confirmação %s quer confirmar a sua sessão - O contato cancelou a confirmação O tempo de confirmação expirou A sessão não sabe sobre essa transação @@ -1641,44 +1359,35 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Chave incorreta Contato não corresponde Erro Desconhecido - Você não está usando algum servidor de identidade Nenhum servidor de identidade está configurado. É necessário usar um para redefinir sua senha. - Parece que você está tentando se conectar com outro servidor local. Deseja sair\? - Editar Responder - Tentar novamente Conecta-se a uma sala para começar a usar o aplicativo. Enviou um convite Convidado por %s - - Vocês estão todos tão envolvidos em tudo aquilo! + Tudo em dia! Você não tem mais mensagens não lidas - Bem-vindo a casa! - Encarregar-se de mensagens não lidas + Boas-vindas! + Leia mensagens não lidas aqui Conversas Suas conversas serão exibidas aqui Salas Seus salas serão exibidas aqui - Reações Concordo Curtir Adicionar reação Veja as reações Reações - Mensagem apagada Mostrar mensagens apagadas Mostrar um espaço reservado para mensagens apagadas Evento apagado pelo usuário Evento moderado pelo administrador da sala Última edição por %1$s em %2$s - - Evento malformado, não pode ser exibido Criar Nova Sala Sem rede. Por favor, verifique sua conexão de internet. @@ -1686,13 +1395,10 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Alterar a rede Por favor, aguarde… Todas as comunidades - Esta sala não pode ser visualizada A visualização da sala global ainda não é suportada por Element - Salas Conversas - Nova Sala CRIAR Nome da sala @@ -1700,18 +1406,13 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Qualquer pessoa poderá entrar nesta sala Lista de Salas Publicar esta sala na lista das salas - Ocorreu um erro ao receber informações de confiança Ocorreu um erro ao obter dados de backup de chaves - Importar as chaves de arquivo \"%1$s\". - Versão do SDK da Matrix Outras licenças de terceiros Você já está vendo esta sala! - Reações rápidas - Geral Preferências Segurança e Privacidade @@ -1719,76 +1420,53 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Regras do Push Nenhuma regra Push definida Nenhuma entrada Push registrada - app_id: chave_push: nome_da_ tela_ do_app: nome_da_sessão: Link: Formato: - Voz e vídeo Ajuda e Sobre - - Registrar token - Faça uma sugestão Por favor, escreva sua sugestão abaixo. Descreva aqui sua sugestão Obrigado, a sugestão foi enviada com êxito A sugestão não foi enviada (%s) - Mostrar eventos ocultos nas conversas - Conversas - Aguardando… Criptografando imagem miniatura… Enviando imagem miniatura (%1$s / %2$s) Criptografando arquivo… Enviando arquivo (%1$s / %2$s) - Baixando arquivo %1$s… O arquivo %1$s foi baixado! - (editado) - Edições na mensagem Nenhuma edição encontrada - Filtrar conversas… Não consegue encontrar o que você está procurando\? Criar uma sala nova Enviar nova mensagem Veja lista das salas - Nome ou ID (#example:matrix.org) - Ativar o recurso de deslizar para responder nas conversas - Alertar apenas uma vez em uma conversa de que não é possível descriptografar as mensagens, em vez de alertar para cada mensagem Adicione uma aba dedicada para notificações não lidas na tela principal. - Link copiado para a memória - Adicionar por ID do Matrix Criando a sala… Nenhum resultado encontrado, use adicionar por ID do Matrix para pesquisar no servidor. Comece a digitar para obter resultados Filtrar por nome do usuário ou ID… - Entrando na sala… - Veja Histórico das Edições - Termos de serviço Revisar termos Seja descoberto por outros - Use bots, pontes, widgets e pacotes de figurinhas - + Use bots, integrações, widgets e pacotes de figurinhas Leia em - - Servidor de identidade Desconectar servidor de identidade Configurar servidor de identidade @@ -1803,7 +1481,6 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Enviamos um e-mail de confirmação para %s, verifique seu e-mail e clique no link de confirmação Enviamos um e-mail de confirmação para %s. Por favor, verifique seu e-mail e clique no link de confirmação Aguardando - Digite o endereço de um servidor de identidade Não foi possível conectar-se ao servidor de identidade Digite o endereço do servidor de identidade @@ -1811,20 +1488,13 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr O servidor de identidade que você escolheu não possui termos de serviço. Continue apenas se você confiar no proprietário do servidor Uma mensagem de texto foi enviada para %s. Por favor, digite o código de confirmação que ela contém. O código de confirmação não está correto. - Atualmente, você está compartilhando endereços de e-mail ou números de telefone no servidor de identidade %1$s. Você precisará reconectar-se a %2$s para parar de compartilhá-los. Concorde com os Termos de Serviço do servidor de identidade (%s), para que você possa ser descoberto por endereço de e-mail ou por número de telefone. - Ativar registros detalhados Os registros detalhados ajudarão os desenvolvedores, ao fornecerem mais registros quando você usar sacudir o aparelho. Mesmo quando ativado, o aplicativo não registra o conteúdo da mensagem ou qualquer outro dado privado. - - Por favor, tente novamente quando tiver aceitado os termos e condições de seu servidor. - Parece que o servidor está demorando muito para responder, isto pode ser causado ou por má conectividade ou por um erro com o servidor. Por favor, tente novamente daqui a pouco. - Enviar anexo - Abra a gaveta de navegação Abra o menu de criar a sala Fecha o menu de criar a sala… @@ -1834,17 +1504,14 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Mostrar senha Esconder senha Saltar para parte inferior - %1$s, %2$s e %3$s leu %1$s e %2$s lê %s lê - 1 usuário leu + %d usuário leu %d usuários leram - O arquivo \'%1$s\' (%2$s) é muito grande para ser carregado. O limite é de %3$s. - Ocorreu um erro durante a recuperação do anexo. Arquivo Contato @@ -1853,13 +1520,11 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Galeria Figurinha Não conseguia lidar com dados compartilhados - MÍDIA Nenhuma mídia nesta sala ARQUIVOS %1$s em %2$s Nenhum arquivo nesta sala - É spam É inapropriado Relatório personalizado… @@ -1867,28 +1532,23 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Motivo para denúnciar este conteúdo RELATÓRIO BLOQUEAR USUÁRIO - Conteúdo relatado - Este conteúdo foi relatado. + Este conteúdo foi reportado. \n -\nSe você não quiser ver mais conteúdo deste usuário, você pode bloqueá-lo para esconder suas mensagens +\nSe você não quiser ver mais mensagens deste usuário, você pode bloqueá-lo para esconder suas mensagens. Denunciado como spam Este conteúdo foi reportado como spam. \n -\nSe você não quiser ver mais conteúdo deste usuário, você pode bloqueá-lo para esconder suas mensagens +\nSe você não quiser ver mais mensagens deste usuário, você pode bloqueá-lo para esconder suas mensagens. Denunciado como inadequado Este conteúdo foi relatado como inadequado. \n -\nSe você não quiser ver mais conteúdo deste usuário, você pode bloqueá-lo para esconder suas mensagens - +\nSe você não quiser ver mais mensagens deste usuário, você pode bloqueá-lo para esconder suas mensagens. Element precisa de permissão para salvar suas chaves E2E no aparelho. \n \nPermita o acesso na próxima janela para poder exportar suas chaves manualmente. - Não há conexão de rede no momento - Bloquear usuário - Todas as mensagens novas (com som) Todas as mensagens novas Apenas @menções @@ -1902,24 +1562,18 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Envia esta mensagem como um spoiler Estraga-Surpresa Digite palavras-chave para encontrar uma reação. - Nenhum usuário bloqueado - Clique longo sobre uma sala para ver mais opções - - %1$s tornou a sala pública para quem conhece o link. Você tornou a sala pública para quem conhece o link. %1$s tornou a sala acessível somente com convite. Você tornou a sala acessível somente com convite. Mensagens não lidas - É a sua conversa. Vira um admin. Converse com as pessoas diretamente ou em comunidades Manter conversas privadas com criptografia Amplie e personalize sua experiência Comece agora - Selecione um servidor Assim como o e-mail, as contas têm uma casa, embora você possa falar com qualquer pessoa Junte-se gratuitamente aos milhões no maior servidor público @@ -1927,7 +1581,6 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Saiba mais Outros Configurações avançadas e preferências - Continuar Conecte-se a %1$s Conecte-se aos serviços de Element no Matrix @@ -1936,14 +1589,12 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Cadastre-se Entrar Continuar com ID único - Endereço de serviço Element no Matrix Endereço Hospedagem Premium para organizações Digite o endereço de Element Modular ou Servidor que você deseja usar Digite o endereço do Servidor ou o servidor do Element que você quer entrar Digite o endereço do servidor que você deseja usar - Ocorreu um erro ao carregar a página: %1$s (%2$d) O aplicativo não pode entrar neste servidor. O servidor suporta os seguintes tipos de login: %1$s. \n @@ -1952,58 +1603,46 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr O aplicativo não pode criar uma conta neste servidor. \n \nDeseja criar uma conta no servidor usando o Element Web\? - Este e-mail não está associado a nenhuma conta. - Redefinir senha em %1$s Um e-mail de confirmação será enviado para sua caixa de entrada, para você confirmar a definição de sua nova senha. Próximo E-mail Nova senha - Atenção! - Alterar a sua senha redefinirá todas as chaves de criptografia de ponta a ponta existentes em todas as suas sessões, tornando o histórico de mensagens criptografadas ilegível. Faça uma cópia (backup) das suas chaves, ou exporte as chaves de outra sessão antes de alterar a sua senha. + Alterar a sua senha redefinirá todas as chaves de criptografia de ponta a ponta existentes em todas as suas sessões, tornando o histórico de mensagens criptografadas ilegível. Faça um backup das suas chaves, ou exporte as chaves de outra sessão antes de alterar a sua senha. Continuar - Este e-mail não está associado a nenhuma conta - Verifique sua caixa de entrada no e-mail Um e-mail de confirmação foi enviado para %1$s. Toque no link para confirmar sua nova senha. Uma vez que você tenha clicado o link que ele contém, clique abaixo. Verifiquei meu endereço de e-mail - Parabéns! Sua senha foi alterada. Você foi desconectado de todas as sessões e não receberá mais notificações pop-up. Para reativar as notificações, faça o login novamente em cada aparelho. Voltar para Entrar - Atenção Sua senha ainda não foi alterada. \n \nInterromper a alteração de senha\? - Defina um endereço de e-mail Defina um e-mail para recuperar sua conta. Mais tarde, você pode, opcionalmente, permitir que as pessoas que você conhece o descubram por seu e-mail. E-mail E-mail (opcional) Próximo - Defina número de telefone Defina um número de telefone para, opcionalmente, permitir que as pessoas que você conhece o encontram. Por favor, use o formato internacional. Número de telefone Número de telefone (opcional) Próximo - Confirme o número de telefone Acabamos de enviar um código para %1$s. Digite-o abaixo para confirmar se é você. Digite o código Enviar novamente Próximo - Os números de telefone internacionais devem começar com \'+\' O número de telefone parece inválido. Favor verificá-lo - Registar em %1$s Nome de usuário ou e-mail Nome de usuário @@ -2015,25 +1654,21 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr \n \n \nParar o processo de registro\? - Selecione matrix.org Selecione Element nos Serviços de Matrix Selecione um servidor personalizado Por favor, realize o desafio de Captcha Aceitar termos para continuar - Por favor, verifique seu e-mail Acabamos de enviar um e-mail para %1$s. \nPor favor, clique no link que ele contém para continuar a criação da conta. O código digitado não está correto. Por favor, verifique-o. Servidor desatualizado Este servidor tem uma versão muito antiga que não é compatível com nosso servidor. Peça ao administrador para fazer atualização. - Recebemos demasiado pedidos. Você pode tentar novamente em %1$d segundo… Recebemos demasiado pedidos. Você pode tentar novamente em %1$d segundos… - Alternativamente, se você já tem uma conta e conhece a sua ID de usuário na Matrix e a senha, você pode usar este método: Entre com ID do Matrix Entre com ID do Matrix @@ -2042,9 +1677,7 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Se você não souber sua senha, volte para redefinir-a. Este não é um identificador de usuário válido. Formato esperado: \'@user:homeserver.org\' Incapaz de encontrar um servidor válido. Por favor, verifique seu identificador - Lida por - Você está desconectada/o Isso pode ser devido à vários motivos: \n @@ -2054,7 +1687,6 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr \n \n• O administrador do seu servidor invalidou seu acesso por motivos de segurança. Entrar novamente - Você está desconectada/o Entrar Administrador do seu servidor (%1$s) invalidou seu acesso %2$s (%3$s). @@ -2066,7 +1698,6 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr \n \nApague-os quando não usar mais este aparelho, ou se quiser entrar em outra conta. Limpar todos os dados - Limpar dados Limpar todos os dados atualmente armazenados neste aparelho\? \nEntre novamente para acessar os dados e mensagens da sua conta. @@ -2075,12 +1706,9 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr A sessão atual é para o usuário %1$s e você fornece credenciais para o usuário %2$s. Isto não é suportado por Element. \n \nPor favor, primeiro limpe os dados, depois faça o login novamente em outra conta. - Seu link do matrix.to foi malformado A descrição é curta demais - Sincronia Inicial… - Veja todas as minhas sessões Configurações avançadas Modo desenvolvedor @@ -2092,26 +1720,19 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Configurações Sessão atual Outras sessões - Mostrando apenas os primeiros resultados, digite mais letras… - Relatar imediatamente as falhas Element poderá quebrar com mais frequência quando ocorrer um erro inesperado - - Adicionar ¯\\_(ツ)_/¯ para uma mensagem de texto simples - + Adiciona ¯\\_(ツ)_/¯ a uma mensagem de texto Ativar criptografia Uma vez ativada, a criptografia não poderá ser desativada. - Seu domínio de e-mail não está permitido a se registrar neste servidor - Entrada não confiável Correspondem Não correspondem Confirme este usuário, comparando os emojis a seguir que serão exibidos na tela dele, na mesma ordem. Para maior segurança, use outro meio de comunicação confiável ou faça isso pessoalmente. Procure o escudo verde para garantir que um usuário seja confiável. Confie em todos os usuários numa sala para garantir que a sala é segura. - Não seguro Um dos seguintes casos pode estar comprometido: \n @@ -2119,13 +1740,11 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr \n·- O servidor do usuário que você está verificando \n·- A sua, ou a conexão de outros usuários à internet \n·- O seu, ou o aparelho dos outros usuários - Vídeo. Imagem. Áudio Arquivo Figurinha - Aguardando… %s cancelado Você cancelou @@ -2135,21 +1754,15 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Solicitação de confirmação Confirmar esta sessão Confirmar manualmente - Você - Escaneie o código com a câmera do aparelho do outro usuário para confirmar a segurança entre vocês Escanea o código do seu contato Não pode escanear Se você não está presente, alternativamente compare emoji - Confirmar comparando emojis - Confirmar por emojis Se você não consegue escanear o código acima, confirme via comparação de emojis. - Imagem em código QR - Confirmar %s Confirmou %s Aguardando por %s… @@ -2173,54 +1786,39 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Mídia e arquivos Sair da sala Deixando a sala… - Administradores Moderadores Personalizado Convites Usuários - Administrador em %1$s Moderador em %1$s Padrão em %1$s Personalizado (%1$d) em %2$s - Ir para a confirmação de leitura - Element não lida com eventos do tipo \'%1$s\' Element não lida com mensagens do tipo \'%1$s\' Element encontrou um problema ao render o conteúdo de um evento com \'%1$s\' ID - Desbloquear - Esta sessão não pode compartilhar essa confirmação com suas outras sessões. \nA confirmação será salvada localmente e será compartilhada em uma versão futura do aplicativo. - Salas recentes Outras salas - Envia a mensagem colorida como arco-íris Envia o emoji colorido como um arco-íris - Conversas - Campo de texto - Ativar a criptografia de ponta a ponta Uma vez ativada, a criptografia não poderá ser desativada. - Ativar criptografia\? - Uma vez ativada, a criptografia de uma sala não pode ser desativada. As mensagens enviadas em uma sala criptografada não podem ser lidas pelo servidor, apenas pelos participantes desta sala. A ativação da criptografia pode impedir que muitos bots e pontes funcionem corretamente. + Uma vez ativada, a criptografia de uma sala não pode ser desativada. As mensagens enviadas em uma sala criptografada não podem ser lidas pelo servidor, apenas pelos participantes desta sala. A ativação da criptografia pode impedir que muitos bots e integrações funcionem corretamente. Ativar criptografia - Para ficar seguro, confirme %s comparando um código único. Para sua segurança, faça isso pessoalmente ou use outra forma confiável de comunicação. - Compare os emoji únicos, assegurando que eles apareçam na mesma ordem. Compare o código com o exibido na tela do outro usuário. As mensagens com este usuário estão criptografadas de ponta a ponta e não podem ser lidas por terceiros. Sua nova sessão agora está confirmada. Ela tem acesso às suas mensagens criptografadas, e outros usuários a verão como confirmada. - Autoverificação A autoverificação está ativada \nChaves privadas estão no aparelho. @@ -2230,56 +1828,40 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr A autoverificação está ativada. \nAs chaves não são confiáveis A autoverificação não está ativada - O administrador do servidor desativou a criptografia de ponta a ponta por padrão em salas privadas e em conversas. Sessões ativas Veja todas as sessões Gerenciar sessões Sair desta sessão - Não há informações criptográficas disponíveis - Esta sessão é confiável para o envio de mensagens seguras porque você a confirmou: Confirme esta sessão para conceder para ela o acesso a mensagens criptografadas. Se você não entrou nesta sessão, sua conta pode estar comprometida: - %d sessão ativa %d sessões ativas - Confirmar este acesso Outros usuários podem não confiar nela Segurança completa - Use uma sessão existente para confirmar a nova sessão, dando a ela acesso às mensagens criptografadas. - - Confirmar Confirmado Atenção - Falha em obter sessões Sessões Confiável Não confiável - Esta sessão está confirmada para trocar mensagens seguras porque %1$s (%2$s) a confirmou: %1$s (%2$s) entrou usando uma nova sessão: Até que este usuário confirme esta sessão, as mensagens enviadas de e para ela são etiquetadas com avisos. Alternativamente, é possível confirmar a sessão manualmente. - - Ativar a autoverificação Reiniciar Chaves - Código QR - Quase lá! Este escudo também aparece para %s\? Sim Não - A conexão com o servidor foi perdida O modo de Avião está ativo - Ferramentas do Desenvolvedor Dados da Conta @@ -2294,49 +1876,36 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Cria uma pesquisa simples Use a Chave ou a Frase Secreta de Recuperação Se você não pode entrar numa sessão existente - Entrar de Novo - Não é possível encontrar segredos no armazenamento Digite a frase secreta de armazenamento Atenção: Você só deveria entrar no armazenamento secreto a partir de um aparelho confiável - Apagar Deseja enviar este anexo para %1$s\? Enviar imagem no tamanho original Enviar imagens no tamanho original - Confirmar a exclusão Tem certeza de que deseja apagar este evento\? Observe que, se você apagar a alteração do nome ou descrição de uma sala, isso reverterá a alteração. Incluir o motivo Motivo da edição - Evento apagado pelo usuário, motivo: %1$s Evento moderado pelo administrador da sala, motivo: %1$s - As chaves já estão atualizadas! - Element para Android - A chave pede - Desbloquear histórico de mensagens criptografadas - Recarregar - Novo login. Foi você\? Toque para revisar e confirmar Use esta sessão para confirmar a sua nova sessão, dando a ela acesso às mensagens criptografadas. Não foi eu Sua conta pode estar comprometida - Se você cancelar, não será capaz de ler mensagens criptografadas neste aparelho, e outros usuários não confiarão nele Se você cancelar, você não poderá ler mensagens criptografadas em seu novo aparelho, e outros usuários não confiarão nele Você não confirmará %1$s (%2$s) se cancelar agora. Precisará começar novamente no perfil dele. - Um dos seguintes casos pode estar comprometido: \n \n- Sua senha @@ -2345,126 +1914,95 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr \n- A conexão à internet de qualquer um dos aparelhos que você está usando \n \nRecomendamos que você altere imediatamente a sua senha e a chave de recuperação em Configurações. - Confirme seus aparelhos em Configurações. Confirmação cancelada - Digite a frase secreta de recuperação Chave da Mensagem Senha da Conta - Definir o %s Gerar uma Chave de Mensagem - Confirme %s - Digite sua %s para continuar. - Proteja e desbloqueie mensagens criptografadas e confia com um %s. Digite novamente seu %s para confirmar. Não use a senha da sua conta. - Digite uma frase de segurança que só você conheça, usada para proteger os segredos em seu servidor. - Isto pode levar vários segundos, seja paciente. Criação de recuperação. Sua chave de recuperação Tudo pronto! Guarde num local seguro Encerrar - Use isto %1$s como uma rede de segurança caso você esqueça sua %2$s. - Publicando as chaves de identidade criadas Gerando uma chave segura a partir de uma frase secreta Definindo a chave padrão SSSS Sincronização da chave Geral Sincronização da chave do Usuário Sincronizando a chave de Auto-Assinatura - Criando o Backup da Chave - - + Criando o backup da chave Suas %2$s & %1$s estão agora definidas. \n \nSalva arquivos em local seguro! Você precisará deles para desbloquear mensagens criptografadas e proteger informações se perder todas as suas sessões ativas. - Imprime e armazena em local seguro - Salva em uma chave USB ou HDD de backup + Salve isto em uma chave USB ou unidade de backup Copia para sua nuvem pessoal - Você não pode fazer isso por celular - Criptografia ativada Criptografia desativada Aguardando por %s… - Configuração de notificações Solução de problemas Digite uma mensagem… - Use Arquivo - Digite %s Frase Secreta de Recuperação - Verificando a chave de backup + Verificando o backup de chave Criando uma Frase Secreta de Recuperação permite proteger e desbloquear mensagens criptografadas e estabelecer confiança. \n \nSe você não quiser definir uma Senha de Mensagem, crie antes uma Chave de Mensagem. Criar uma Frase Secreta de Recuperação permite proteger e desbloquear mensagens criptografadas e estabelecer confiança. - Se você cancelar agora, poderá perder mensagens e dados criptografados para sempre se esquecer seus credenciais. -\n -\nTambém pode configurar o Backup Seguro e gerenciar suas chaves em Configurações. - + Se você cancelar agora, poderá perder mensagens e dados criptografados para sempre se esquecer suas credenciais. +\n +\nVocê também pode configurar o Backup online e gerenciar suas chaves em Configurações. As mensagens nesta sala estão criptografadas de ponta a ponta. Confirme os usuários em seus perfis. A criptografia usada por esta sala não é suportada - %s criou e configurou a sala. Você criou e configurou a sala. - Quase lá! O outro aparelho está mostrando o mesmo escudo\? Quase lá! Aguardando confirmação… Falha na importação de chaves - Mensagens contendo @room Mensagens criptografadas em conversas individuais Mensagens criptografadas em salas Quando a versão da sala é atualizada Ajustar as notificações por evento - Envia uma mensagem como texto simples, sem formatar o texto - Nome de usuário e/ou senha incorretos. A senha digitada começa ou termina com espaços, favor verificá-la. Esta conta foi desativada. - Atualização de criptografia disponível Ativar a autoverificação Verifique-se e os outros para manter suas conversas seguras - Digite sua %s para continuar Não é uma chave de recuperação válida Digite uma chave de recuperação - - Verificando a chave de backup (%s) + Verificando o backup da chave (%s) Obtendo a chave da curva Gerando a chave SSSS a partir de uma frase secreta Gerando a chave SSSS a partir de uma frase secreta (%s) Gerando a chave SSSS a partir da chave de recuperação - Armazenando secreto de chave do backup em SSSS + Armazenando backup secreto de chave em SSSS %1$s (%2$s) - Digite sua Frase Secreta de Chave do Backup para continuar. - use sua chave de recuperação de Chave do Backup - Não conheça sua Frase Secreta de Chave do Backup, você pode %s. - Chave de recuperação de Chave do Backup - + use sua chave de recuperação da Chave do Backup + Se não se lembrar de sua Frase Secreta do backup de chave, você pode %s. + Chave de recuperação do backup de chave Impedir a captura de tela do aplicativo Ativando esta opção acrescenta FLAG_SECURE a todas as atividades. Reinicie o aplicativo para que a alteração tenha efeito. - Arquivo de mídia adicionado à Galeria Não foi possível adicionar o arquivo de mídia à Galeria Não foi possível salvar o arquivo de mídia Definir uma nova senha da conta… - Use o Element mais recente em seus outros aparelhos: Element Web, Element para Computador, Element para iOS, Element para Android, ou outro cliente Matrix capaz de fazer autoverificação Element Web \nElement para Computador @@ -2477,30 +2015,25 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Use seu %1$s ou %2$s para continuar. Usar a chave de recuperação Selecione sua Chave de Recuperação, ou insira manualmente digitando-a ou colando-a da sua área de transferência - O backup não pôde ser descriptografado com essa Chave de Recuperação: verifique se você digitou a Chave de Recuperação correta. + O backup não pôde ser descriptografado com essa Chave de Recuperação: verifique se você digitou corretamente a Chave de Recuperação. Falha ao acessar o armazenamento seguro - Não criptografado Criptografado por um aparelho não confirmado Revisar onde você está logado Verifique todas as suas sessões para garantir que sua conta e mensagens estão seguras Verifique o novo login acessando sua conta: %1$s - Confirmação manual por texto Confirmar o login Confirme interativamente por emojis Confirme sua identidade verificando este login a partir de uma de suas outras sessões, concedendo-lhe acesso às mensagens criptografadas. Marcar como Confiável - Escolha um nome de usuário. Escolha uma senha. Verificar este link O link %1$s redirecionará você para outro site: %2$s. \n \nDeseja continuar\? - Não foi possível criar sua DM. Por favor, verifique os usuários que você deseja convidar e tente novamente. - Adicionar participantes CONVITE Convidando os usuários… @@ -2512,11 +2045,9 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Convites enviados para %1$s e %2$s e mais Não foi possível convidar os usuários. Por favor, verifique os usuários que você deseja convidar e tente novamente. - Idioma atual Outros idiomas disponíveis Carregando os idiomas disponíveis… - Abra termos de %s Desconectar-se do servidor de identidade %s\? Este servidor de identidade está desatualizado. Element suporta apenas API V2. @@ -2526,7 +2057,6 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Pela sua privacidade, Element apenas suporta o envio dos e-mails e números de telefone de usuários em hash. A associação falhou. Não há nenhuma associação atual com este identificador. - Seu servidor doméstico (%1$s) propõe o uso de %2$s para seu servidor de identidade Use %1$s Como alternativa, você pode digitar o endereço de outro servidor de identidade @@ -2539,32 +2069,25 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Ativar o microfone Desligue a câmera Ativar a câmera - - Configure um Backup Seguro - - Backup Seguro - Previna-se contra perda de acesso a mensagens e dados criptografados, fazendo backup das chaves de encriptação no seu servidor. + Configure o backup online + Backup online + Previna-se contra perda de acesso a mensagens e dados criptografados, fazendo backup das chaves de criptografia no seu servidor. Configure Use uma Chave de Segurança Gere uma chave de segurança para armazenar num local seguro, como um gerenciador de senhas ou um cofre. Use uma Frase de Segurança Digite uma frase secreta que só você conhece, e gere uma chave para backup. - Salve sua Chave de Segurança Armazene sua Chave de Segurança num local seguro, como um gerenciador de senhas ou um cofre. - Defina uma Frase de Segurança Digite uma frase de segurança que só você conheça, usada para proteger os segredos no seu servidor. Frase de Segurança Digite sua Frase de Segurança novamente para confirmá-la. - Salve sua Chave de Segurança Armazene sua Chave de Segurança num local seguro, como um gerenciador de senhas ou um cofre. - Nome da sala Descrição Você alterou as configurações da sala com êxito - Você não pode acessar esta mensagem Aguardando esta mensagem. Pode demorar um pouco Não é possível decifrar @@ -2573,17 +2096,12 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Você não pode acessar esta mensagem porque a sessão não foi definida como confiável pelo remetente Você não pode acessar esta mensagem porque o remetente intencionalmente não enviou as chaves Aguardando o histórico de criptografia - Riot agora é Element! Estamos felizes em anunciar que mudamos de nome! Seu aplicativo está atualizado e você está conectado à sua conta. OK, ENTENDI APRENDA MAIS - Element - - Salvar chave de recuperação em - Adicionar da minha lista de contatos Sua lista de contatos está vazia Lista de Contatos @@ -2591,13 +2109,10 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Extraindo seus contatos… Sua lista de contatos está vazia Lista de contatos - Revogar o convite Revogar o convite para %1$s\? - Banido por %1$s Não foi possível remover o banimento do usuário - As notificações Push estão desativadas Revise suas configurações para permitir notificações Push Escolha um PIN de segurança @@ -2626,13 +2141,11 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr %1$d/%2$d chave importada com êxito. %1$d/%2$d chaves importadas com êxito. - Gerenciar integrações Nenhum widget ativo A sala foi criada, mas alguns convites não foram enviados pelo seguinte motivo: \n \n%s - %1$s, %2$s e %3$d outro leu %1$s, %2$s e %3$d outros leram @@ -2643,4 +2156,87 @@ Na próxima tela, você será solicitado a permitir que o Element funcione sempr Atenção! Última tentativa restante antes de você ser desconectada/o! Muitos erros, você foi desconectada/o - + Você não tem permissão para iniciar uma chamada nesta sala + Nenhum número de telefone foi adicionado à sua conta + Endereços de e-mail + Nenhum e-mail foi adicionado à sua conta + Números de telefone + Remover %s\? + Certifique-se de ter clicado no link do e-mail que enviamos para você. + + %d segundo + %d segundos + + Mostrar quando alguém for convidado/entrar/sair/banido e mostrar alterações de foto de perfil e de nome e sobrenome. + E-mails e números de telefone + Editar e-mails e números de telefone vinculados à sua conta Matrix + Código + Por favor, use o formato internacional (o número de telefone precisa começar com \'+\') + Confirme sua identidade verificando este login, concedendo a ele acesso a mensagens criptografadas. + Não é possível carregar uma sala da qual você foi banido. + Não foi possível encontrar esta sala. Certifique-se de que ela existe. + O link não está correto + Este número de telefone já foi adicionado. + Mostrar eventos de status dos integrantes da sala + Votação + Botões do bot + Reagiu com: %s + Confirmação concluída + Remover os dados de %1$s desta conta\? +\n +\nProssiga com cuidado, pois não há como reverter esta escolha. + Desculpe, esta funcionalidade ainda não está disponível para contas conectadas via acesso único. + O código PIN é solicitado todas as vezes que você abre o Element. + O código PIN é solicitado após 2 minutos sem usar o Element. + Solicitar o PIN depois de 2 minutos + Exibir apenas o número de mensagens não lidas em uma notificação. + Exibir o nome das salas e o conteúdo das mensagens. + Exibir o conteúdo das mensagens nas notificações + O código PIN é a única maneira de desbloquear o Element. + Ativa a biometria específica do dispositivo, como impressões digitais e reconhecimento de rosto. + Ativar a biometria + Configurar a proteção + Proteger o acesso usando PIN e biometria. + Acesso restrito + + Exibir o aparelho com o qual você pode se verificar + Exibir %d aparelhos com os quais você pode se verificar + + Você não terá mais o histórico de mensagens, aparelhos confiáveis e contatos confirmados + Se você redefinir tudo + Apenas faça isso se você não tiver outro aparelho para confirmar este aparelho. + Redefinir tudo + Esqueceu as senhas ou perdeu todas as opções de recuperação\? Redefina tudo + Você entrou. + As mensagens nesta sala estão criptografadas de ponta a ponta. + Sair + Configurações + As mensagens nesta conversa estão criptografadas de ponta a ponta. +\n +\nSuas mensagens estão protegidas, pois só você e seu contato têm as chaves únicas para desbloqueá-las. + As mensagens nesta sala não estão criptografadas de ponta a ponta. + Este servidor local está executando uma versão antiga do Element. Peça ao administrador do servidor para atualizá-lo. Você pode prosseguir, mas alguns recursos poderão não funcionar corretamente. + Exibir histórico completo em salas criptografadas + %1$s e %2$s + %1$s em %2$s e %3$s + Você clicou na notificação! + Clique na notificação, por favor. Se você não receber uma notificação, verifique as configurações do seu aparelho. + Exibição de notificações + Você está vendo a notificação! Clique aqui! + Houve uma falha ao receber a notificação. Reinstalar o aplicativo pode ser a solução. + O aplicativo está recebendo a notificação + O aplicativo está aguardando a notificação + Ainda não é possível buscar mensagens em salas criptografadas. + Você não tem permissão para iniciar uma chamada + Você não tem permissão para iniciar uma chamada em grupo + Redefinir + %1$s tornou a sala acessível somente com convite. + Você definiu que a sala só receberá integrantes com convite. + %s entrou. + Buscar usuários banidos + Testar o recebimento de notificações + + %d convite + %d convites + + \ No newline at end of file diff --git a/vector/src/main/res/values-ru/strings.xml b/vector/src/main/res/values-ru/strings.xml index 5d9c44518f..7375bc6293 100644 --- a/vector/src/main/res/values-ru/strings.xml +++ b/vector/src/main/res/values-ru/strings.xml @@ -1,21 +1,18 @@ - + ru RU - Сообщения Комната Настройки Информация о собеседнике Исторические - Принять Отклонить Завершить звонок - OK Отмена @@ -48,7 +45,6 @@ Все равно отправить или Приглашение - Выйти из учётной записи Голосовой вызов @@ -61,27 +57,22 @@ Закрыть Скопировано в буфер Отключить - Подтверждение Предупреждение - Начало Избранные Люди Комнаты - Фильтр названия комнаты Фильтр избраного Фильтр людей Фильтр названия комнаты - Приглашения Маловажные - Беседы Локальные контакты @@ -89,7 +80,6 @@ Нет диалогов Вы не дали доступ Element к внутренним контактам Нет результатов - Комнаты Список комнат @@ -99,9 +89,8 @@ %d пользователь %d пользователя %d пользователей - + - Отправить логи Отправить журналы ошибок Отправить скриншот @@ -114,10 +103,8 @@ Сбой отправки отчета об ошибке (%s) Прогресс (%s%%) В прошлый раз приложение некорректно завершило работу. Хотите отправить отчет о сбое? - Отправить в Прочитан - Войти в Комнату Имя пользователя Создать аккаунт @@ -126,14 +113,11 @@ URL сервера URL сервера авторизации Поиск - Начать новый чат Начать голосовой вызов Начать видеовызов - Отправить файлы Камера - Вход Создать аккаунт @@ -180,7 +164,6 @@ На адрес %s было отправлено письмо. После перехода по ссылке в письме, нажмите ниже. Не удалось проверить email: убедитесь, что вы перешли по присланной ссылке Ваш пароль сброшен. Осуществлен выход на всех сессиях - вы не будете получать push уведомления. Для включения push уведомлений заново войдите на каждом из ваших устройств. - URL должен начинаться с http[s]:// Сбой входа: сетевая ошибка @@ -189,7 +172,6 @@ Сбой регистрации Сбой регистрации: ошибка проверки email Пожалуйста, введите корректный URL - Неверное имя пользователя или пароль Указанный токен доступа не распознан Поврежденный JSON @@ -197,35 +179,27 @@ Отправлено слишком много запросов Логин уже используется Вы не перешли по высланной в email ссылке - - Чтение списка вступивших - - Откравить как Оригинал Крупный Средний Мелкий - "Отменить загрузку? Отменить загрузку? %d с %1$dм %2$dс - Вчера Сегодня - Название Комнаты Тема комнаты - Вызов соединён Устанавливается соединение… @@ -235,16 +209,13 @@ Входящий видеовызов Входящий голосовой вызов Идёт разговор… - Вызываемый абонент не отвечает. Медиавызов не удался Невозможно инициализировать камеру Звонок принят на другом устройстве - Снять фото или видео" Не удалось записать видео" - Element Информация Element нуждается в разрешении на доступ к вашей фото-и видеотеке для отправки и сохранения вложений. @@ -261,25 +232,20 @@ Element может проверить Вашу адресную книгу, чтобы найти других пользователей сети по email или телефонному номеру. \n \nСогласны ли вы поделиться своей адресной книгой для этой цели\? - Извините. Действие не выполнено из-за недостаточных разрешений - Сохранено Сохранить в загрузки? ДА НЕТ Продолжить - Удалить Присоединиться Просмотр Отклонить - Перейти к первому непрочитанному сообщению. - %s пригласил вас присоединиться к этой комнате Приглашение пришло на адрес %s, который не связан с этим аккаунтом. @@ -287,27 +253,22 @@ Вы пытаетесь получить доступ к %s. Хотите присоединиться к обсуждению? комната Это пред. просмотр комнаты. Вы в режиме только чтения. - Новый чат Добавить участника 1 пользователь - Покинуть комнату Вы уверены, что хотите выйти из комнаты\? Вы уверены, что хотите исключить %s из чата? Создать - В сети Недоступен Покой - АДМИНИСТРИРОВАНИЕ ВЫЗОВ ПРЯМЫЕ ЧАТЫ СЕССИИ - Пригласить Покинуть этот чат Исключить из этого чата @@ -322,18 +283,14 @@ Упомянуть Отобразить список сессий Вы не сможете отменить это действие, поскольку пользователь получит такой же уровень доступа, как и у вас. Вы уверены? - "Вы уверены что хотите пригласить %s в этот чат?" - Пригласить по ID Локальные Контакты (%d) Только зарегистрированные - Пригласить пользователя по ID Пожалуйста, введите один или несколько адресов email или Matrix ID Email или Matrix ID - Поиск %s печатает… @@ -350,7 +307,6 @@ Удалить неотправленные сообщения Файл не найден У вас нет прав писать сообщения в этом чате - Доверять Не доверять @@ -363,7 +319,6 @@ Сертификат сервера изменился и ваш телефон теперь ему не доверяет. Это ОЧЕНЬ ПОДОЗРИТЕЛЬНО. Рекомендуется, НЕ ДОВЕРЯТЬ этому новому сертификату. Сертификат изменился с ранее доверенного на недействительный. Возможно, сервер обновил свой сертификат. Свяжитесь с администратором сервера для получения ожидаемого отпечатка сертификата. Примите сертификат только если администратор сервера опубликовал отпечаток сертификата, который соответствует указанному выше. - Подробности комнаты Люди @@ -372,7 +327,6 @@ Некорректный ID. Используйте email или Matrix ID вида \'@localpart:domain\' ПРИГЛАШЕНЫ ПРИСОЕДИНИЛИСЬ - Причина отчета о контенте Вы хотите скрыть все сообщения этого пользователя? @@ -380,7 +334,6 @@ Учтите, что это действие перезапустит приложение и может занять некоторое время. Отменить загрузку Отменить загрузку - Поиск Фильтр списка пользователей @@ -389,7 +342,6 @@ СООБЩЕНИЯ ЛЮДИ ФАЙЛЫ - ПРИСОЕДИНИЛИСЬ КАТАЛОГ @@ -402,18 +354,15 @@ Войти в комнату Войти в комнату Введите ID комнаты или псевдоним - Просмотр каталога Поиск в каталоге… - Избранное Уменьшить приоритет Прямой чат Покинуть обсуждение Забыть - Сообщения Настройки @@ -422,9 +371,7 @@ Прочие уведомления Авторские права Политика конфиденциальности - - Аватар Отображаемое имя Email @@ -433,22 +380,18 @@ Добавить телефон Системные настройки приложения. Сведения о приложении - Включить уведомления для этой учетной записи Включить уведомления для этой сессии Включить экран на 3 секунды - В персональных чатах В групповых чатах Когда меня приглашают в комнату Вызовы Сообщения от бота - Синхронизация Включить фоновую синхронизацию Таймаут синхронизации Задержка между каждой синхронизацией - Версия Версия OLM Правила и условия @@ -457,8 +400,6 @@ Политика конфиденциальности Очистить весь кэш - - Параметры пользователя Уведомления Игнорируемые @@ -484,19 +425,15 @@ Аутентификация Пароль: Отправить - Авторизован как Сервер Сервер идентификации - Ожидается подтверждение Проверьте электронную почту и перейдите по высланной ссылке. Затем нажмите продолжить. Не удалось подтвердить адрес электронной почты. Проверьте электронную почту и нажмите на содержащуюся ссылку. После этого нажмите продолжить. - Этот адрес электронной почты уже занят. Такой адрес электронной почты не найден. Этот номер телефона уже используется. - Смена пароля Текущий пароль Новый пароль @@ -506,13 +443,9 @@ Отображать сообщения пользователя %s? Учтите, что это действие перезапустит приложение и может занять некоторое время. - Вы уверены, что хотите удалить устройство для получения уведомлений? - Вы уверены, что хотите удалить %1$s %2$s? - Выберите страну - Страна Пожалуйста выберите страну Телефон @@ -522,21 +455,17 @@ Введите код активации Ошибка проверки телефона Код - - Аватар комнаты Название комнаты Тема Метки комнаты Отмечено как: - Избранное Маловажные Нет - Доступность и видимость Отображать комнату в каталоге @@ -544,22 +473,18 @@ Доступ к истории комнаты Кто может читать историю? Кто имеет доступ к комнате? - Все Только члены (с момента выбора этой опции) Только члены (с момента приглашения) Только члены (с момента присоединения) - Для генерации ссылки команда должна иметь адрес. Только приглашенные Все у кого есть ссылка на комнату, кроме гостей Все у кого есть ссылка на комнату, включая гостей - Забаненые пользователи - Дополнительно Внутренний ID комнаты @@ -571,36 +496,27 @@ Вам необходимо выйти, чтобы включить шифрование. Шифровать сообщения только для проверенных сессий Никогда не отправлять шифрованное сообщение на непроверенные сессии в этой комнате с этой сессии. - У этой комнаты еще нет локального адреса Новый адрес (например #foo:matrix.org") - Неверный формат псевдонима \'%s\' неверный формат псевдонима Для этой комнаты не будет основного адреса. Предупреждения основного адреса - Установить как основной адрес Сбросить основной адрес Копировать ID комнаты Копировать адрес комнаты - В этой комнате включено шифрование. В этой комнате выключено шифрование. Включить шифрование (внимание: не может быть снова отключено!) - Каталог - %s пытается загрузить историю комнаты, но не может ее найти. - - Информация о сквозном шифровании - Информация о событии ID пользователя Curve25519 идентификационный ключ @@ -608,7 +524,6 @@ Алгоритм ID сесии Ошибка дешифровки - Информация о сессии отправителя Публичное имя Публичное имя @@ -616,7 +531,6 @@ Ключ сессии Проверка Ed25519 отпечаток - Экспорт E2E ключей комнаты Экспорт ключей комнаты Экспорт ключей в локальный файл @@ -626,31 +540,25 @@ E2E ключи комнаты сохранены в \'%s\'. Предупреждение: этот файл может быть удален после деинсталляции приложения. - Импорт E2E ключей комнаты Импорт ключей комнаты Импортировать ключи из локального файла Импорт Шифровать только для проверенных сессий Не отправлять зашифрованные сообщения непроверенным сессиям с этой сессии. - Не проверено Проверено В черном списке - неизвестная сессия ничего - Подтвердить Отменить подтверждение Блокировать Разрешить - Проверить сессию Чтобы убедиться, что этой сессии можно доверять, обратитесь к его владельцу, используя другие способы (например, лично или по телефону), и спросите, соответствует ли ключ, который он видит в настройках для этой сессии: "Если совпадает, то нажмите кнопку подтвердить ниже. Если не совпадает, возможно кто-то пытается перехватить сессию и вы захотите добавить его в черный список. В будущем данный процесс будет улучшен." Я проверил, что ключи совпадают - Комната содержит неизвестные сессии Эта комната содержит неизвестные сессии, которые не были подтверждены. @@ -658,7 +566,6 @@ \nПеред продолжением рекомендуем вам пройти процесс проверки для каждой сессии, но вы можете отправить сообщение повторно, не проверяя. \n \nНеизвестные сессии: - Выбор списка комнат Сервер возможно недоступен или перегружен @@ -666,30 +573,23 @@ URL домашнего сервера Все комнаты на сервере %s Все местные комнаты %s - Поиск в истории Пользовательский интерфейс Язык Выберите язык - Запускать при загрузке Показывать метки времени для всех сообщений - 3 дня 1 неделя 1 месяц Постоянно - Очистить медиа кэш Недоступен - Каталог пользователей КАТАЛОГ ПОЛЬЗОВАТЕЛЕЙ (%s) Режим экономии трафика - Тема - Размер шрифта Очень мелкий Малый @@ -699,19 +599,15 @@ Самый большой Огромный Сохранить медиа - Светлая тема Тёмная тема Чёрная тема - Звук уведомлений Показывать метки времени в 12-часовом формате - Вам нужно разрешение на управление виджетами в этой комнате Создание виджета не удалось Осуществлять конференц звонки через Jitsi Вы уверены, что хотите удалить виджет из этой комнаты? - Не удалось создать виджет. Не удалось отправить запрос. @@ -722,10 +618,8 @@ В запросе отсутствует user_id. Комната %s невидима. Добавить приложения Matrix - Синхронизация… Мониторинг событий - Вызов Сообщения, содержащие мое имя пользователя Вы добавили новою сессию \'%s\', запрашивающая ключи шифрования. @@ -734,97 +628,70 @@ Начать проверку Поделиться без проверки Игнорировать запрос - Звуковые уведомления Беззвучные уведомления - Сделать фото Снять видео - Аналитика - Использовать встроенную камеру - Сообщить об ошибке - Внимание! Конференц-связь находится в разработке и может быть ненадежной. - Ошибка команды Нераспознанная команда: %s - Выкл Громко - Зашифрованное сообщение - Сведения о сообществе - Загрузка… - "Закрыть приложение" Сообщества - Поиск сообществ - Пригласить Сообщества Нет групп - Вы действительно хотите начать новый чат с %s? Вы уверены, что хотите начать голосовой вызов? Вы уверены, что хотите начать видеовызов? - Список групп - Блокирующий пользователь выгонит их из этой комнаты и не позволит им снова присоединиться. - Все сообщения (громко) Все сообщения Только упоминания Без звука - Добавить ярлык на главный экран - + Добавить на главный экран Предпросмотр URL-адресов Вибрация при упоминании пользователя - Уведомления Новый ID сообщества (например +foo:matrix.org) Недопустимый ID сообщества \'%s\' недействительный ID сообщества - - Создать Создать сообщество Имя сообщества Пример ID сообщества пример - Начало Люди Комнаты Нет пользователей - Комнаты Присоединился Приглашен Фильтр участников группы Фильтр комнат группы - Администратор сообщества не предоставил подробного описания этого сообщества. - Вас выгнал %2$s из %1$s Вас забанил %2$s в %1$s Причина: %1$s Присоединиться снова Забыть комнату Встряхните устройство, чтобы сообщить об ошибке - Действия Список участников Открыть заголовок @@ -833,74 +700,68 @@ %d комната %d комнаты %d комнат - + %d комната %d комнаты %d комнат - + %1$s в %2$s - %d активный виджет %d активных виджета %d активных виджетов - + - Аватар - %d активный участник %d активных участника %d активных участников - + %d участник %d участника %d участников - + %d новое сообщение %d новых сообщения %d новых сообщений - + - %1$s комната найдена для %2$s %1$s комнаты найдено для %2$s %1$s комнат найдено для %2$s - + %d изменение членства %d изменения членства %d изменений членства - + - %d непрочитанное уведомление %d непрочитанных уведомления %d непрочитанных уведомлений - + %d непрочитанное уведомление %d непрочитанных уведомления %d непрочитанных уведомлений - + Получить аватар Заметка аватара Сообщества - Эта комната не показывает любые сообщества Конфиденциальность уведомлений Нормальный @@ -909,74 +770,55 @@ • Уведомления содержат только метаданные • Уведомления содержат метаданные и данные сообщения • Уведомления не будут показывать содержимое сообщения - Конфиденциальность уведомлений Element может работать в фоновом режиме для управления конфиденциальностью и безопасностью ваших уведомлений. Это может повлиять на время работы от батареи. Предоставить разрешение Выбрать другой вариант - Ограниченная конфиденциальность надежно от Matrix-Homeserver получено Отправить стикер - Отправить стикер У вас сейчас нет доступных стикеров. Добавить сейчас? - Деактивация аккаунта Деактивировать мой аккаунт - Отправка аналитических данных Element собирает анонимную аналитику для улучшения приложения. Пожалуйста, включите аналитику, чтобы помочь нам улучшить Element. Да, я хочу помочь! - Обязательный параметр отсутствует. Параметр недействителен. Для продолжения использования этого сервера %1$s вы должны ознакомиться и принять Условия использования. Ознакомиться сейчас - Деактивировать аккаунт Пожалуйста, удалите все сообщения, которые я отправил, после деактивации моего аккаунта (предупреждение: будущие участники увидят неполную историю разговоров) Чтобы продолжить, введите пароль: Деактивировать аккаунт - Это действие сделает вашу учетную запись непригодной для дальнейшего использования. Вы не сможете войти в систему и никто другой не сможет заново зарегистрировать учетную запись с вашим идентификатором. Также, это приведет к тому, что вы покинете все комнаты, в которых участвовали и данные о вашей учетной записи будут удалены с сервера идентификации. Это действие необратимо. По умолчанию, деактивация вашей учетной записи не удаляет отправленные вами сообщения. Если вы хотите, чтобы мы удалили все ваши сообщения - поставьте отметку в поле ниже. Видимость сообщений в Matrix похожа на электронную почту. Удаление ваших сообщений означает, что отправленные вами сообщения не будут показаны новым или не зарегистрированным пользователям, но те пользователи, которые уже получили эти сообщения - по прежнему будут их видеть. Лицензии сторонних производителей - Скачать Говорить Очистить Перезапросить ключи шифрования у других ваших сессий. - Отправлен запрос ключа. - Запрос отправлен Запустите Element на другом устройстве, которое может расшифровать сообщение, для отправки ключа на эту сессию. - Введите здесь… - Отправить голосовое сообщение - продолжить с… К сожалению, для выполнения этого действия не найдено внешнее приложение. - Отправлять голосовые сообщения - Пожалуйста, введите ваш пароль. - Если возможно, добавьте описание на английском языке. Отправить зашифрованный ответ… Отправить ответ (незашифрованный)… Предварительный просмотр медиа перед отправкой - В настоящее время вы не являетесь участником каких-либо сообществ. - Использовать клавишу Enter для отправки сообщения Отображает действие Банит пользователя с указанным ID @@ -993,20 +835,20 @@ Этот разговор продолжается здесь Эта комната является продолжением другого разговора Нажмите здесь для просмотра старых сообщений - Присоединиться к комнате с указанным псевдонимом Для исправления управления приложениями Matrix - Из-за отсутствия разрешений это действие невозможно. - %d секунда - %d секунды - %d секунд + %d сек. + %d сек. + %d сек. + %d сек. - %d минута - %d минуты - %d минут + %d мин. + %d мин. + %d мин. + %d мин. %d час @@ -1018,160 +860,124 @@ %d дни %d дней - Сейчас %1$s %1$s %2$s назад - - "%1$S " + "%1$s " %1$s и %2$s %1$s %2$s - %d выбран %d выбрано %d выбраны - + %d участник %d участника %d участников - + - %d комната %d комнаты %d комнат - + Системные оповещения - Лимит ресурсов исчерпан Связаться с администратором - Тема status.im - Ошибка - Версия %s Создать парольную фразу Парольные фразы не совпадают свяжитесь с вашим администратором - Превышен один из ресурсных лимитов сервера, по этому некоторые пользователи не смогут авторизоваться. Превышен один из ресурсных лимитов сервера. - Пожалуйста, придумайте парольную фразу для шифрования экспортируемых ключей. Вам нужно будет ввести ту же самую фразу для импорта ключей. Из-за ежемесячного ограничения активных пользователей сервера некоторые из пользователей не смогут авторизоваться. Сервер достиг ежемесячного ограничения активных пользователей. - Пожалуйста %s для увеличения этого лимита. Пожалуйста, %s, чтобы продолжить пользоваться этим сервисом. - Ленивая подгрузка участников Увеличить производительность за счёт загрузки собеседников только при первом просмотре. Ваш сервер не поддерживает ленивую подгрузку собеседников. Попробуйте позже. - Извините, произошла ошибка - развернуть свернуть - Всё равно позвонить Пароль Пожалуйста ознакомьтесь и подтвердите согласие с политикой этого сервера: - Вызовы Использовать стандартную мелодию Element для входящих звонков Мелодия звонка Выберите мелодию звонка: - Идёт видеозвонок … - Выгнать Причина - Поиск проблем с уведомлениями Оправлять уведомления о наборе текста Markdown форматирование Фоновое Соединение Предоставить разрешение - Произошла ошибка при подтверждении вашего адреса электронной почты. - Произошла ошибка при подтверждении вашего номера телефона. Дополнительная информация: %s - Эта опция требует стороннего приложения для записи сообщений. - Команде \"%s\" нужно больше параметров, или некоторые параметры неверны. Markdown включен. Markdown выключен. - Всегда Для сообщений и ошибок Только для ошибок - Принимаю - Результаты диагностики Запустить тесты Выполняется… (%1$d из %2$d) В основном, всё в порядке. Если вы по-прежнему не получаете уведомления, пожалуйста, отправьте отчет об ошибке, чтобы помочь нам разобраться. Один или несколько тестов не пройдены, попробуйте предлагаемые решения. Один или несколько тестов не пройдены, пожалуйста, отправьте отчет об ошибке, чтобы помочь нам исследовать проблему. - Настройки системы. Уведомления включены в настройках системы. Уведомления отключены в настройках системы. Пожалуйста, проверьте настройки системы. Открыть настройки - Настройки аккаунта. Уведомления включены для вашей учетной записи. Уведомления отключены для вашей учетной записи. Пожалуйста, проверьте настройки аккаунта. Включить - Настройки сессии. Уведомления включены для этой сессии. Уведомления не включено для этой сессии. \nПожалуйста, проверьте настройки Element. Включить - Проверка сервисов Play APK Google Play сервисов доступен и обновлён. Element использует сервисы Google Play для доставки push-сообщений, но не похоже что он настроен правильно: %1$s Исправить сервисы Play - Токен Firebase Токен FCM успешно получен: %1$s Не удалось получить токен FCM: %1$s - Регистрация токена Токен FCM успешно зарегистрирован на сервере. Не удалось зарегистрировать токен FCM на сервере: %1$s - Служба уведомлений Служба уведомлений запущена. Служба уведомлений не запущена. Попробуйте перезапустить приложение. Запустить службу - Автоматический перезапуск службы уведомлений Служба была убита и перезапущена автоматически. Не удалось перезапустить службу - Запуск при загрузке Служба будет запущена после перезапуска устройства. При перезагрузке устройства служба не будет запущена , вы не будете получать уведомления, пока Element не будет открыт один раз. Включить запуск при загрузке - Проверьте фоновые ограничения Фоновые ограничения отключены для Element. Этот тест должен быть запущен с использованием мобильных данных (без WIFI). %1$s @@ -1179,12 +985,10 @@ Работа приложения будет жестко ограничена, пока оно находится в фоновом режиме, и это может повлиять на уведомления. %1$s Отключить ограничения - Оптимизация батареи - Оптимизация батареи не влияет на Райот. + Оптимизация батареи не влияет на Element. Если пользователь оставляет устройство в отключенном от сети и в неподвижном состоянии в течение некоторого времени при выключенном экране, устройство переходит в режим Doze. Это предотвращает доступ приложений к сети и откладывает выполнение заданий, синхронизацию и передачу стандартных аварийных сигналов. Игнорировать оптимизацию - Предпросмотр ссылок в чате, когда ваш домашний сервер поддерживает эту функцию. Показывать другим пользователям, что вы печатаете. Форматировать сообщения, используя markdown. Например, позволяет использовать звездочки для выделения текста курсивом. @@ -1195,51 +999,38 @@ Включает изменения аватара и отображаемого имени. Необходимо минимизировать влияние на фоновое соединение для надёжности уведомлений. На следующем экране вам будет предложено разрешить Райоту всегда работать в фоновом режиме, пожалуйста, примите. - - Использовать системную камеру вместо камеры Райот. + Использовать системную камеру вместо камеры Element. Показать информацию %1$s: %1$s: %2$s +%d %d+ Не найден APK сервисов Google Play. Уведомления могут работать неправильно. - Не влияет на приглашения, исключения и запреты. - Резервное копирование ключей Используйте резервную копию ключа Резервное копирование ключей не завершено, пожалуйста, подождите… - Пропустить Готово - Расширенные настройки уведомлений Значение уведомления по событию - Пользовательские настройки. Обратите внимание, для некоторых типов сообщений выбран беззвучный режим (уведомление будет, но без звука). Некоторые уведомления отключены в настройках. Не удалось загрузить пользовательские правила, повторите попытку. Проверьте настройки - Добавить аккаунт - Настроить шумные уведомления Настроить уведомления о вызовах Настроить беззвучные уведомления - Выберать цвет светодиода, вибрацию, звук… - - + Выбрать цвет светодиода, вибрацию, звук… Управление криптографическими ключами Управление резервным копированием ключей - Беззвучный Пожалуйста, введите парольную фразу Парольная фраза слишком простая - Пожалуйста, удалите парольную фразу, если хотите, чтобы Element сгенерировал ключ восстановления. Matrix сессия недоступна - Никогда не теряйте зашифрованных сообщений Сообщения в зашифрованных комнатах защищены сквозным шифрованием. Ключи для прочтения этих сообщений есть только у вас и получателя(ей). \n @@ -1255,10 +1046,8 @@ Резервное копирование началось Уверены? Удалить резервную копию ключей шифрования с сервера? Вы больше не сможете использовать ключ восстановления для чтения истории зашифрованных сообщений. - Удалить резервную копию Не удалось удалить резервную копию (%s) - Удаление резервной копии… Чтобы использовать резервную копию ключа в этой сессии, восстановите его с помощью своей парольной фразы или ключа восстановления. Резервная копия имеет недействительную подпись из подтвержденной сессии %s @@ -1267,62 +1056,46 @@ Резервная копия имеет действительную подпись с этой сессии. Резервная копия подписана сессией с идентификатором %s. Резервные копии ключей этой сессии не сохраняются. - - Резервное копирование ключей не активировано в этой сессии. Резервное копирование ключей успешно настроено для этой сессии. Удалить резервную копию - Восстановить из резервной копии Сессионное шифрование не активировано - - Пожалуйста, введите ключ восстановления Разблокировать историю Восстановление резервной копии: Восстановление сообщений - Введите ключ восстановления - Используйте ключ восстановления для разблокировки истории зашифрованных сообщений Если вы не знаете вашу парольную фразу для восстановления, вы можете %s. - используйте ключ восстановления Вы можете потерять доступ к сообщениям, если выйдете из системы или потеряете это устройство. - Запрашивает версию резервной копии… Используйте парольную фразу для разблокировки истории зашифрованных сообщений Потеряли ключ восстановления? В настройках вы можете создать новый. Ошибка сети: проверьте соединение и повторите попытку. - Резервная копия восстановлена %s ! Ошибка получения информации о доверии для резервной копии (%s). - Резервная копия имеет недействительную подпись из неподтвержденной сессии %s Не удалось получить последнюю версию ключей восстановления (%s). %d новый ключ был добавлен к этому устройству. %d новых ключа были добавлены к этому устройству. %d новых ключей были добавлены к этому устройству. - + - Восстановлена резервная копия с %d ключом. Восстановлены резервные копии с %d ключами. Восстановлены резервные копии с %d ключами. Невозможно расшифровать резервную копию с помощью этого ключа восстановления: убедитесь, что вы ввели правильный ключ. - Невозможно расшифровать резервную копию с помощью данного пароля: убедитесь, что вы ввели верный пароль. Ключи шифрования копируются на сервер в фоновом режиме. Первое копирование может занять несколько минут. - - Генерация ключей восстановления с использованием парольной фразы может занять несколько секунд. Ключ восстановления был сохранен в \'%s\'. Предупреждение: этот файл может быть удален при удалении приложения. - [%1$s] \nЭта ошибка вне контроля Element. На телефоне нет учетной записи Google. Пожалуйста, добавьте аккаунт Google. [%1$s] @@ -1336,35 +1109,28 @@ Уверены? Создание резервной копии Сделайте резервную копию ваших ключей или потеряете доступ к вашим зашифрованным сообщениям. - Остаться Прервать - Уверены, что хотите выйти? Пожалуйста, введите имя пользователя. (Расширенный) Ручной экспорт ключей - Создание резервной копии Успех! Подпись - Алгоритм Версия Резервное копирование %d ключа… Резервное копирование %d ключей… Резервное копирование %d ключей… - + - Все ключи сохранены Резервное копирование ключей… - Никогда не теряйте зашифрованные сообщения Никогда не теряйте зашифрованные сообщения Начать использовать резервное копирование ключей - Это был я Поделиться Я сделал копию @@ -1374,9 +1140,7 @@ Начать использовать резервное копирование ключей Новая резервная копия ключа Использовать резервное копирование ключей - Управление резервным копированием ключей - Новые ключи зашифрованных сообщений Обнаружена новая резервная копия ключа безопасных сообщений. \n @@ -1384,7 +1148,6 @@ Ваши ключи копируются. (Дополнительно) Настройка с ключом восстановления В режиме экономии траффика применяется специальный фильтр, поэтому обновление присутствия и уведомления о наборе отфильтровываются. - Или защитите резервную копию с помощью ключа восстановления, сохранив его в безопасном месте. Безопасная резервная копия ключей должна быть активирована на всех ваших сессиях, чтобы не потерять доступ к зашифрованным сообщениям. Зашифрованная копия ключей будет храниться на вашем сервере. Для безопасности защитите её парольной фразой. @@ -1395,7 +1158,6 @@ Скачивание ключей… Вычисление ключа восстановления… Игнорировать - Инициализация сервиса Отметить как прочитанное Войти с помощью единого входа @@ -1404,43 +1166,36 @@ Обновить пароль Пароль не действителен Пароли не совпадают - Медиа Сжатия по умолчанию Выберите Источник медиа по умолчанию Выберите - %1$s: 1 сообщение + %1$s: %2$d сообщение %1$s: %2$d сообщения %1$s: %2$d сообщений + %1$s: %2$d сообщений %d оповещение %d оповещения %d оповещений - Новое событие Комната Новые сообщения Новое приглашение Мне Использовать настройку - Проверить сессию - Ваше устройство использует устаревший TLS протокол, уязвимый для атак, для вашей же безопасности вам отказано в подключении Приложениям не" нужно подключаться к HomeServer в фоновом режиме, это должно снизить расход заряда батареи" Клавиша Ввод отправит сообщение вместо переноса строки - Воспроизвести звук затвора - неизвестный IP ** Отправить не удалось - пожалуйста откройте комнату - К сожалению, конференц-звонки с Jitsi не поддерживаются на старых устройствах (ниже Android OS - 5.0) - Новая сессия запрашивает ключи шифрования. \nИмя сессии: %1$s \nПоследний раз в сети: %2$s @@ -1449,12 +1204,10 @@ \nИмя сессии: %1$s \nПоследний раз в сети: %2$s \nЕсли вы не открывали новую сессию - проигнорируйте этот запрос. - Подтвердить Поделиться Запрос поделится ключем Игнорировать - Ошибка отклика сервера Дополнить параметры сервера Element обнаружил пользовательскую конфигурацию сервера для вашего userID домена\"%1$s\": @@ -1465,33 +1218,26 @@ Входящий запрос о проверке Проверьте эту сессию, чтобы отметить её как доверенную. Доверие сессиям партнеров позволяет быть уверенным в надёжности сквозного шифрования сообщений. Потдверждение этой сессии пометит её доверенной для вас и вашего партнёра. - Подтвердите эту сессию, убедившись, что следующие смайлики появляются на экране партнера Подтвердите эту сессию, убедившись, что следующие цифры появляются на экране партнера - Вы получили входящий запрос на подтверждение. Посмотреть запрос В ожидании партнера, чтобы подтвердить … - Проверено! Вы успешно подтвердили эту сессию. Защищенные сообщения от этого пользователя шифруются end-to-end и не могут быть прочитаны третьими лицами. Понял - Ничего не появляется\? Не все клиенты пока поддерживают интерактивную проверку. Используйте устаревшую проверку. Используйте устаревшую проверку. - Проверка ключа Запрос отменен Другая сторона отменила проверку. \n%s Проверка отменена. \nПричина: %s - Интерактивное подтверждение сессии Запрос на подтверждение %s желает подтвердить ваше устройство - Пользователь отменил проверку Время проверки истекло Сессия не знает об этой транзакции @@ -1503,23 +1249,18 @@ Несоответствие ключей Несоответствие пользователя Неизвестная ошибка - Резервная копия существует на домашнем сервере "Похоже, у вас уже есть резервная копия ключа настройки из другой сессии. Хотите заменить его новым\?" Заменить Стоп - Проверка состояния резервного копирования Вы вышли из системы из-за недействительных или истекших учетных данных. - Редактировать Ответить - Повторить Присоединитесь к комнате, чтобы начать использовать приложение. Отправил вам приглашение Приглашен %s - Вы в курсе! У вас больше нет непрочитанных сообщений Добро пожаловать домой! @@ -1528,22 +1269,17 @@ Здесь будут отображаться ваши диалоги Комнаты Здесь будут отображаться ваши комнаты - Реакции Принять Нравиться Добавить реакцию Просмотреть реакции Менеджер интеграции - Менеджер интеграции не настроен. Реакции - Событие удалено пользователем Мероприятие, модерируемое администратором помещения Последнее изменение %1$s %2$s - - Некорректное событие, не может быть отображено Создать новую комнату Сети нет. Пожалуйста, проверьте подключение к Интернету. @@ -1551,11 +1287,9 @@ Изменить сеть Пожалуйста, подождите… Все сообщества - Эту комнату нельзя предварительно просмотреть Комнаты Диалоги - Новая комната СОЗДАТЬ Имя комнаты @@ -1563,18 +1297,13 @@ Любой сможет присоединиться к этой комнате Каталог номеров Опубликовать эту комнату в каталоге номеров - Произошла ошибка при получении информации о доверии Произошла ошибка при получении ключей резервного копирования данных - Импорт ключей e2e из файла \"%1$s\". - Версия Matrix SDK Другие уведомления третьих сторон Вы уже просмотрели эту комнату! - Быстрое реагирование - Общее Параметры Безопасность и конфиденциальность @@ -1586,89 +1315,62 @@ session_name: Url: Формат: - Голос и видео Помощь и информация - - Регистрационный токен - Оставить отзыв Пожалуйста, напишите ваше предложение ниже. Опишите ваше предложение здесь Спасибо, предложение было успешно отправлено Предложение не было отправлено (%s) - Показать скрытые события в ленте сообщений - Предварительный просмотр открытой комнаты в Element пока не поддерживается - Диалоги - Ждите… Шифрование миниатюры… Отправка миниатюр (%1$s / %2$s) Файл шифруется… Файл отправляется (%1$s / %2$s) - Файл %1$s загружается… Файл %1$s был загружен! - (изменено) - - Редактирование сообщений Изменения не найдены - Отфильтровывать разговоры… Не можете найти то, что ищете\? Создать новую комнату Отправить новое прямое сообщение Просмотр список комнат - Имя или ID (#example:matrix.org) - Включить жест смахивания для ответа в ленте сообщений - Ссылка скопирована в буфер обмена - Добавить по Matrix ID Создание комнаты… "Результат не найден, используйте добавить matrix ID для поиска на сервере." Начните печатать, чтобы получить результат Фильтр по имени пользователя или ID… - Присоединение к комнате… - История изменений - Обзор Отклонить - Для продолжения Вам необходимо принять Условия данного сервиса. - Правила push-уведомлений не определены Нет зарегистрированных push-шлюзов - Условия предоставления услуг Условия просмотра Быть доступным для других Используйте ботов, мосты, виджеты и стикеры - Читать в - Никто Отмена Отключить Сервер идентификации не настроен. - Звонок не состоялся из-за неправильно настроенного сервера Попросите администратора вашего домашнего сервера (%1$s) настроить TURN сервер, чтобы звонки работали надежно. \n \nКроме того, вы можете попробовать использовать публичный сервер по %2$s, но это будет не так надежно, и он предоставит ваш IP-адрес этому серверу. Вы также можете управлять этим в настройках. Попробуйте использовать %s Больше не спрашивать - Установите адрес электронной почты для восстановления учетной записи, и позже она может будет найдена участниками, которые вас знают. Установите телефон, и позже его могут опционально обнаруживать люди, которые вас знают. Установите адрес электронной почты для восстановления аккаунта. Позже используйте электронную почту или телефон, чтобы их могли найти люди, которые вас знают. @@ -1679,8 +1381,6 @@ Оптимизирован для работы в реальном времени Без фоновой синхронизации Не удалось обновить настройки. - - Предпочтительный интервал синхронизации Обнаружение Будет использовать%s в качестве помощника, если ваш домашний сервер не предлагает его (ваш IP-адрес будет доступен во время разговора) @@ -1698,9 +1398,7 @@ Публичное имя сессии видны людям, с которыми вы общаетесь Вы не используете какой-либо сервер идентификации Идентификационный сервер не настроен, требуется сброс пароля. - Похоже, вы пытаетесь подключиться к другому домашнему серверу. Вы хотите выйти\? - Сервер идентификации Отключить идентификационный сервер Настроить идентификационный сервер @@ -1710,7 +1408,6 @@ Видимые адреса электронной почты Доступные номера телефонов В ожидании - Введите адрес сервера идентификации Не удалось подключиться к серверу идентификации Пожалуйста, введите URL сервера идентификации @@ -1721,19 +1418,15 @@ Мы отправили вам электронное письмо с подтверждением на %s, проверьте вашу электронную почту и нажмите на ссылку для подтверждения Выбранный сервер идентификации не имеет условий обслуживания. Продолжить, только если вы доверяете владельцу службы Текстовое сообщение отправлено %s. Введите код проверки, который он содержит. - В настоящее время вы делитесь адресами электронной почты или телефонными номерами на сервере идентификации %1$s. Вам нужно повторно подключиться к %2$s, чтобы прекратить делиться ими. Примите Условия обслуживания сервера идентификации (%s), чтобы разрешить обнаружение по адресу электронной почты или номеру телефона. Включить подробные логи. Отправить вложение - Откройте навигационный ящик Открыть меню создания комнаты Неверный адрес сервера Matrix Подтвердите пароль Требуется аутентификация - - Интеграции Разрешить интеграции Виджет @@ -1746,10 +1439,7 @@ Этот виджет был добавлен: Ваши тема ID комнаты - - Cyrl - Используйте менеджер интеграций чтобы управлять ботами, мостами, виджетами и наборами стикеров. \nМенеджеры интеграций получают данные о конфигурации, могут изменять виджеты, отправлять приглашения в комнаты и устанавливать права от вашего имени. "Использование может оставить cookie на вашем устройстве и отправить данные в %s:" @@ -1757,7 +1447,6 @@ Не удалось загрузить виджет. \n%s Отозвать доступ для меня - Ваше отображаемое имя URL вашего аватара Ваш ID @@ -1766,7 +1455,6 @@ Использовать камеру Использовать микрофон Получать доступ к медиа, защищённым DRM - Создать новую комнату Показать пароль Скрыть пароль @@ -1776,7 +1464,6 @@ Галерея Это спам Игнорировать пользователя - Все сообщения Только при упоминаниях Настройки @@ -1784,39 +1471,29 @@ %1$s сделал(а) комнату доступной для всех, у кого есть ссылка. %1$s сделал(а) комнату доступной только по приглашению. Подробные логи помогут разработчикам, предоставив больше информации, когда вы отправляете RageShake. Даже когда они разрешены, приложение не логирует ваши сообщения и другие приватные данные. - - Отменить создание комнаты… Вниз - Контакт Стикер Причина жалобы на контент Пожаловаться ИГНОРИРОВАТЬ ПОЛЬЗОВАТЕЛЯ - Все сообщения (громко) Без звука Отправить данное сообщение под спойлером Спойлер Введите ключевые слова, чтобы найти реакцию. - Долгий клик по комнате покажет дополнительные опции - - Непрочитанные сообщения - Это ваш разговор. Владейте им. Общайтесь с людьми напрямую или в группах Начать - Выберите сервер Как и у электронной почты, у учётных записей один дом, хотя вы можете общаться с кем угодно Премиум-хостинг для организаций Узнать больше Другой Пользовательские и расширенные настройки - Продолжить Подключиться к %1$s Подключиться к Element Matrix Services @@ -1824,7 +1501,6 @@ Зарегистрироваться Войти в систему Продолжить с SSO - Модульный адрес Адрес Премиум-хостинг для организаций @@ -1836,20 +1512,15 @@ Приложение не может создать учётную запись на этом домашнем сервере. \n \nХотите зарегистрироваться через веб-клиент\? - Этот адрес электронной почты не связан ни с одной учетной записью. - Сбросить пароль на %1$s Далее Email Новый пароль - Внимание! Смена пароля приведёт к сбросу всех сквозных ключей шифрования во всех ваших сессиях, что сделает зашифрованную историю разговоров нечитаемой. Настройте резервное копирование ключей или экспортируйте ключи от комнаты из другой сессии, прежде чем сбрасывать пароль. Продолжить - Данный email не связан ни с одним аккаунтом - Проверьте свою почту Письмо с подтверждением было отправлено на %1$s. Нажмите на ссылку, чтобы подтвердить свой новый пароль. Как только вы перейдете по ссылке, которую он содержит, нажмите ниже. @@ -1857,33 +1528,27 @@ Ваш пароль был сброшен. Вы вышли из всех сессий и больше не будете получать push-уведомления. Чтобы возобновить уведомления, войдите снова на каждом устройстве. Назад, чтобы войти в систему - Предупреждение Ваш пароль еще не изменен. \n \nОстановить процесс смены пароля\? - Задать адрес электронной почты Электронная почта Электронная почта (по желанию) Далее - Установить номер телефона Укажите номер своего телефона, чтобы разрешить знакомым найти вас. Пожалуйста, используйте международный формат. Номер телефона Номер телефона (необязательно) Далее - Подтвердить номер телефона Мы только что отправили код на %1$s. Введите его ниже, чтобы подтвердить, что это вы. Введите код Отправить повторно Далее - Международные телефонные номера должны начинаться с \'+\' Номер телефона кажется недействительным. Пожалуйста, проверьте его - Зарегистрироваться в %1$s Имя пользователя или email Имя пользователя @@ -1894,25 +1559,21 @@ Ваш аккаунт еще не создан. \n \nОстановить процесс регистрации\? - Выбрать matrix.org Выбрать Element Matrix Services Выбрать другой сервер Пожалуйста, пройдите проверку капчей Примите условия для продолжения - Пожалуйста, проверьте ваш email Мы только что отправили email на %1$s. \nПожалуйста, нажмите на содержащуюся в нём ссылку, чтобы продолжить создание аккаунта. Домашний сервер устарел Этот домашний сервер использует слишком старую версию для подключения. Попросите администратора обновить домашний сервер. - Отправлено слишком много запросов. Вы сможете повторить попытку через %1$d секунду… Отправлено слишком много запросов. Вы сможете повторить попытку через %1$d секунды… Отправлено слишком много запросов. Вы сможете повторить попытку через %1$d секунд… - Вы вышли из системы Это могло произойти по разным причинам: \n @@ -1922,7 +1583,6 @@ \n \n• Администратор вашего сервера заблокировал вам доступ из соображений безопасности. Войти снова - Вы вышли из системы Войти Администратор вашего домашнего сервера (%1$s) вывел вас из вашего аккаунта %2$s (%3$s). @@ -1934,7 +1594,6 @@ \n \nУдалите их, если вы закончили использовать это устройство или хотите войти в другую учётную запись. Очистить все данные - Очистить данные Очистить все данные, хранящиеся в данный момент на этом устройстве\? \nВойдите заново, чтобы получить доступ к данным своей учётной записи и сообщениям. @@ -1942,10 +1601,8 @@ Очистить данные Текущая сессия предназначена для пользователя %1$s, а вы предоставляете учётные данные для пользователя %2$s. Это не поддерживается в Element. \nПожалуйста, сначала очистите данные, а затем снова войдите под другим аккаунтом. - Ваша ссылка на matrix.to неверна Описание слишком короткое - Посмотреть все мои сессии Дополнительные настройки Режим разработчика @@ -1953,7 +1610,6 @@ Настройки Текущая сессия Другие сессии - Включено шифрование Недоверенный вход Вложения @@ -1962,7 +1618,6 @@ Пожалуйста, выберите пароль. Удалить… Просмотрено - Узнать больше Настройки комнаты Уведомления @@ -1975,120 +1630,92 @@ Администраторы Модераторы Пользователи - Сообщение удалено МЕДИА В этой комнате нет медиафайлов ФАЙЛЫ В этой комнате нет файлов - Это недопустимо Другая причина… Пожаловаться на контент Безопасность Ещё QR-код - Соединение с сервером потеряно Используйте пароль восстановления или ключ Разблокировать историю зашифрованных сообщений - Проверьте свои устройства в разделе Настройки. Парольная фраза для восстановления Пароль учётной записи - Задайте %s Введите %s, чтобы продолжить. - %s способствует доверию, защищает и разблокирует зашифрованные сообщения. Не переиспользуйте пароль учётной записи. - - Это может занять несколько секунд, пожалуйста, наберитесь терпения. %s создал(а) и настроил(а) комнату. - Сообщение… - Доступно обновление шифрования Проверьте себя и других для защиты ваших бесед - Парольная фраза для восстановления Подтвердите вход Подтвердите свою личность и получите доступ к зашифрованным сообщениям, подтвердив этот вход в другой сессии. Лента сообщений - Ключ сообщения Сгенерировать ключ сообщения - Распечатайте его и храните в безопасном месте Установка парольной фразы для восстановления позволяет защитить и разблокировать зашифрованные сообщения и доверие. \n \nЕсли вы не хотите устанавливать пароль сообщения, создайте вместо него ключ сообщения. Установка парольной фразы для восстановления позволяет защитить и разблокировать зашифрованные сообщения и доверие. - - Шифрование включено Шифрование не включено %1$s: %2$s %1$s: %2$s %3$s - Показывать удалённые сообщения Показывать заглушку на месте удалённых сообщений Мы отправили письмо для подтверждения на %s, проверьте почту и нажмите на ссылку для подтверждения Код подтверждения неверный. - Попробуйте снова после принятия условий обслуживания на вашем домашнем сервере. - Похоже, сервер долгое время не отвечает, что может быть вызвано плохим соединением или ошибкой на сервере. Попробуйте снова через некоторое время. - Недавние комнаты Прочие комнаты - Сменить пароль учётной записи… - Поддерживается только в зашифрованных комнатах Создать новый диалог %1$s, %2$s и %3$s прочли %1$s и %2$s прочли Файл \'%1$s\' (%2$s) слишком большой. Максимальный размер для загрузки %3$s. - Произошла ошибка при загрузке вложения. %1$s %2$s Жалоба отправлена - Жалоба на контент отправлена. -\n -\nЕсли вы не хотите видеть контент этого пользователя, вы можете его заблокировать, чтобы скрыть его сообщения + Жалоба на контент отправлена. +\n +\nЕсли вы не хотите видеть контент этого пользователя, вы можете его игнорировать, чтобы скрыть его сообщения. Отмечено как спам - Этот контент был отмечен как спам. -\n -\nЕсли вы не хотите видеть контент этого пользователя, вы можете его заблокировать, чтобы скрыть его сообщения + Этот контент был отмечен как спам. +\n +\nЕсли вы не хотите видеть контент этого пользователя, вы можете его игнорировать, чтобы скрыть его сообщения. Жалоба на неприемлемое содержание отправлена - Этот контент был отмечен как неприемлемый. -\n -\nЕсли вы не хотите видеть контент этого пользователя, вы можете его заблокировать, чтобы скрыть его сообщения - + Этот контент был отмечен как неприемлемый. +\n +\nЕсли вы не хотите видеть контент этого пользователя, вы можете его игнорировать, чтобы скрыть его сообщения. Они совпадают Они не совпадают Закрыть окно бэкапа ключей %s прочитано Не удалось обработать данные - Element требуются права для сохранения ваших ключей шифрования на диск. \n \nПожалуйста, разрешите доступ в следующем всплывающем окне, чтобы экспортировать ключи вручную. - Нет подключения к сети - Воспроизвести Приостановить Копировать Выполнено - Уведомления Звонок не состоялся Не удалось установить соединение реального времени. \nПопросите администратора вашего сервера настроить сервер TURN, чтобы звонки работали надёжно. - Выберите звуковое устройство Телефон Динамик @@ -2099,21 +1726,15 @@ Задняя Выключить HD Включить HD - Ошибка SSL: не удалось идентифицировать другого абонента. Ошибка SSL. Активный звонок (%s) Вернуться к звонку - Отменить приглашение Снизить собственные полномочия\? Отклонить - - Вы не сможете отменить это изменение, поскольку вы понижаете себя в должности, а если вы последний привилегированный пользователь в комнате, то восстановить привилегии будет невозможно. Понизить - - Игнорировать пользователя Игнорирование этого пользователя приведет к удалению его сообщений из общих комнат. \n @@ -2131,7 +1752,6 @@ Причина блокировки Разблокировать пользователя Разблокирование пользователя позволит ему снова присоединиться к комнате. - Безопасное резервное копирование Управление Настройка безопасного резервного копирования @@ -2140,44 +1760,32 @@ Защитите себя от потери доступа к зашифрованным сообщениям и данным, создав резервные копии ключей шифрования на вашем сервере. Создайте новый ключ безопасности или задайте новую секретную фразу для существующей резервной копии. Это заменит ваш текущий ключ или фразу. - Интеграции отключены Включите «Управление интеграциями» в настройках, чтобы сделать это. - %d пользователь заблокирован %d пользователей заблокировано %d пользователей заблокировано - Ключи успешно экспортированы - ОБЗОР Активные виджеты - - Ключ восстановления был сохранён. - Безопасное резервное копирование Защита от потери доступа к зашифрованным сообщениям и данным - Настроить безопасное резервное копирование - - Слиянию не удаётся расшифровать сообщение на временной шкале Добавьте специальную вкладку для непрочитанных уведомлений на главном экране. - - Прочитал 1 пользователь + Прочитал %d пользователь Прочитано %d пользователями Прочитано %d пользователями + Прочитано %d пользователями - Добавить в избранное Убрать из избранного %1$s не внес никаких изменений Вы не внесли никаких изменений Вы не игнорируете никаких пользователей - Вы сделали комнату доступной для всех, у кого есть ссылка. Вы сделали комнату только по приглашению. Сохраняйте приватность ваших переписок с помощью шифрования @@ -2186,10 +1794,8 @@ Войти в %1$s Введите адрес сервера или Element, к которому вы хотите подключиться Введите адрес сервера, который вы хотите использовать - На ваш почтовый ящик будет отправлено письмо с подтверждением установки нового пароля. Я подтвердил свою электронную почту - Установите адрес электронной почты для восстановления вашей учетной записи. Позже вы можете дополнительно разрешить людям, которых вы знаете, обнаружить вас по электронной почте. Введенный код неверен. Пожалуйста, проверьте. Кроме того, если у вас уже есть учетная запись и вы знаете свой идентификатор Matrix и пароль, вы можете использовать этот метод: @@ -2200,28 +1806,20 @@ Если вы не знаете свой пароль, вернитесь, чтобы сбросить его. Это недопустимый идентификатор пользователя. Ожидаемый формат: \'@user:homeserver.org\' Не удалось найти действительный домашний сервер. Пожалуйста, проверьте свой идентификатор - Первичная синхронизация… - Rageshake Порог обнаружения Встряхните телефон, чтобы проверить порог обнаружения Тряска зафиксирована! Показываем только первые результаты, наберите больше букв… - Раннее падение Element может падать чаще, когда происходит непредвиденная ошибка - Добавляет смайл ¯\\_(ツ)_/¯ в начало сообщения - После включения шифрования оно не может быть отключено. - Ваш почтовый домен не имеет права регистрироваться на этом сервере - Проверьте этого пользователя, подтвердив, что следующие уникальные смайлики появляются на его экране в том же порядке. Для максимальной безопасности используйте другое надежное средство связи или сделайте это лично. Ищите зеленый щит, чтобы убедиться, что пользователь доверенный. Доверяйте всем пользователям в комнате, чтобы обеспечить безопасность комнаты. - Не безопасно Одно из следующих условий может быть скомпрометировано: \n @@ -2229,13 +1827,11 @@ \n— Домашний сервер, к которому подключен пользователь, которого вы проверяете \n— Ваше или подключение к интернету других пользователей \n— Ваше или устройство других пользователей - Видео. Изображение. Аудио Файл Стикер - Ожидание… %s отменено Вы отменили @@ -2245,21 +1841,15 @@ Запрос на подтверждение Подтвердите эту сессию Подтверждение вручную - Вы - Сканируйте код с помощью устройства другого пользователя, чтобы надежно проверить друг друга Сканировать их код Невозможно сканировать Если вы не можете лично, сравните эмодзи в таком случае - Подтвердить при помощи сравнения эмодзи - Подтверждение с помощью эмодзи Если вы не можете отсканировать приведенный выше код, проверьте это, сравнив короткий уникальный набор эмодзи. - Изображение QR-кода - Подтверждено %s Подтверждённых %s Ожидание для %s… @@ -2272,45 +1862,33 @@ \nВаши сообщения защищены замками, и только у вас и получателя есть уникальные ключи, чтобы разблокировать их. Действия Администратора Покинуть комнату… - Пользовательский Приглашения Администратор в %1$s Модератор в %1$s По умолчанию в %1$s Пользовательский (%1$d) в %2$s - Перейти к последнему прочтённому им сообщению - Element не обрабатывает события типа \'%1$s\' Element не обрабатывает сообщения типа \'%1$s\' Element столкнулся с проблемой при отображении содержимого события с идентификатором \'%1$s\' - Перестать игнорировать - Эта сессия не может поделиться подтверждением с другими сессиями. \nПодтверждение будет сохранено локально и отправится в будущей версии приложения. - Посылает сообщение, окрашенное в цвет радуги Посылает данную эмоцию, окрашенную в цвет радуги - Редактор сообщений - Включить сквозное шифрование После включения шифрования оно не может быть отключено. - Активировать шифрование\? После включения шифрование для комнаты не может быть отключено. Сообщения, отправленные в зашифрованном помещении, не могут быть замечены сервером, только участниками помещения. Включение шифрования может помешать правильной работе многих ботов и мостов. Активировать шифрование - Чтобы быть в безопасности, проверьте %s, проверив одноразовый код. Чтобы обезопасить себя, сделайте это лично или используйте другой способ общения. - Сравните уникальные эмодзи, убедившись, что они появились в том же порядке. Сравните код с тем, который отображается на экране другого пользователя. Сообщения с этим пользователем полностью зашифрованы и не могут быть прочитаны третьими лицами. Ваша новая сесия теперь подтверждена. Она имеет доступ к вашим зашифрованным сообщениям, и другие пользователи будут считать её надежной. - Кросс-подпись Кросс-подпись включена \nПриватные ключи хранятся на устройстве. @@ -2320,68 +1898,51 @@ Кросс-подпись включена. \nКлючи являются ненадёжными Кросс-подпись выключена - Администратор вашего сервера отключил сквозное шифрование по умолчанию в приватных комнатах и диалогах. Активные сессии Показать все сессии Управление сессиями Выйти из этой сессии - Нет доступного шифрования информации - %d сессия активна %d сессии активны %d сессий активно - Подтвердите эту сессию Используйте существующую сессию для подтверждения этой, предоставив ей доступ к зашифрованным сообщениям. - - Инструменты для разработчиков Данные учётной записи Если вы не можете получить доступ к существующей сессии - Запросы ключей - Используйте эту сессию для подтверждения новой сессии, предоставляя ей доступ к зашифрованным сообщениям. Ваши %2$s и %1$s установлены. \n \nДержите их в безопасности! Они понадобятся для разблокировки зашифрованных сообщений и защиты информации, если у вас не останется активных сессий. - Если вы отмените сейчас, вы можете потерять зашифрованные сообщения и данные, если потеряете доступ к своим логинам. \n \nВы также можете настроить безопасное резервное копирование и управлять своими ключами в настройках. - Сообщения в этой комнате зашифрованы сквозным шифрованием. Посмотрите подробности и подтвердите пользователей в их профиле. Параметры уведомлений Сообщения, содержащие @room Отладка Настройки важности уведомлений для событий - Используйте последнюю версию Element на других ваших устройствах, веб-клиент Element, Element для ПК, Element для iOS, Element для Андроид или другой клиент Matrix, поддерживающий кросс-подпись Используйте последнюю версию Element на других ваших устройствах: Подтвердите новую сессию вашей учетной записи: %1$s - Настроить безопасное резервное копирование - Безопасное резервное копирование Эта сессия является доверенной для безопасного обмена сообщениями, потому что вы проверили её: Подтвердите эту сессию, чтобы пометить её доверенной и предоставить ей доступ к зашифрованным сообщениям. Если вы не входили в эту сессю, ваша учетная запись может быть скомпрометирована: - Другие пользователи могут не доверять ему Завершите настройку безопасности - Верифицировать Верифицировано Внимание - Ошибка получения списка сессий Сессии Доверенные Недоверенные - Эта сессия является доверенной для безопасного обмена сообщениями, так как %1$s (%2$s) проверил(а) его: %1$s (%2$s) вошел(ла), используя новую сессию: @@ -2397,54 +1958,39 @@ Выберите вариант Создать простой опрос Новый вход - Пока этот пользователь не доверяет этой сессии, сообщения, отправленные в обе стороны, помечаются предупреждениями. Кроме того, вы можете подтвердить сессию вручную. - - Инициализировать кросс-подпись Сбросить ключи - Почти готово! Отображает ли %s такой же щит\? Да Нет - Активирован режим \"В самолёте\" - Не удалось найти данные в хранилище Введите парольную фразу для секретного хранилища Предупреждение: Вы должны получать доступ к секретному хранилищу только с доверенного устройства - Вы хотите отправить это вложение в %1$s\? Отправить изображение в оригинальном размере Отправить изображения в оригинальном размере Отправить изображения в оригинальном размере - Подтвердите удаление Вы уверены, что хотите скрыть (удалить) это событие\? Обратите внимание, что если вы удалите название комнаты или измените тему, это может отменить изменение. Указать причину Причина редактирования - Событие удалено пользователем, Причина: %1$s Событие модерируется администратором комнаты, Причина: %1$s - Ключи успешно обновлены! - Element для Android - Обновить - Новый вход в вашу учётную запись. Это были Вы\? Нажмите, чтобы просмотреть и проверить Это был не я Ваш аккаунт может оказаться под угрозой - Если вы прервёте процедуру, то не сможете читать зашифрованные сообщения на этом устройстве, а другие пользователи не будут доверять ему Если вы прервёте процедуру, то не сможете читать зашифрованные сообщения на своем новом устройстве, а другие пользователи не будут доверять ему Если вы прервёте процедуру, пользователь %1$s (%2$s) не будет подтверждён. Начните заново в профиле этого пользователя. - Что-то из этого может быть скомпрометировано: \n \n- Ваш пароль @@ -2453,9 +1999,7 @@ \n- Интернет-соединение, используемое этим или другим устройством \n \nМы рекомендуем вам немедленно изменить свой пароль и ключ восстановления в настройках. - Подтверждение отменено - Генерация ключа безопасности из парольной фразы Генерация ключа SSSS из парольной фразы Генерация ключа SSSS из парольной фразы (%s) @@ -2463,70 +2007,51 @@ Если вы не знаете вашу парольную фразу для резервного копирования ключей, вы можете %s. Задать роль Подтвердить %s - Введите свою %s еще раз для подтверждения. Введите секретную фразу, известную только вам, для защиты данных на вашем сервере. - Настройка восстановления. Ваш ключ восстановления Готово! Храните его в безопасности Завершить - Используйте %1$s как подстраховку на случай, если вы забудете %2$s. - Публикация созданных ключей идентификации Определение ключа SSSS по умолчанию Синхронизация мастер-ключа Синхронизация ключа пользователя Синхронизация ключа самоподписи Настройка резервного копирования ключей - - Сохраните на USB-флешку или резервный диск Скопируйте в персональное облачное хранилище - Вы не можете сделать это с телефона - Шифрование этой комнаты не поддерживается - Вы создали и настроили комнату. - Почти готово! Отображается ли такой же щит в другой вашей сессии\? Почти готово! Ожидание подтверждения… Ожидание для %s… - Не удалось импортировать ключи - Зашифрованные сообщения в персональных чатах Зашифрованные сообщения в групповых чатах При обновлении комнат Посылает сообщение в виде простого текста, не интерпретируя его как разметку - Неверное имя пользователя и/или пароль. Введенный пароль начинается или заканчивается пробелами, пожалуйста, проверьте. Эта учётная запись была деактивирована. - Активировать кросс-подпись Введите %s, чтобы продолжить Использовать файл - Введите %s Это недействительный ключ восстановления Пожалуйста, введите ключ восстановления - Проверка ключа резервного копирования Проверка ключа резервного копирования (%s) Получение кривой ключа Генерация ключа SSSS из ключа восстановления Сохранение резервной копии ключа в SSSS %1$s (%2$s) - используйте ваш ключ восстановления ключа резервной копии Ключ восстановления ключа резервной копии - Блокировать скриншоты в приложении Включение этого параметра добавляет FLAG_SECURE ко всем действиям. Перезапустите приложение, чтобы изменения вступили в силу. - Медиафайл добавлен в галерею Не удалось добавить медиафайл в галерею Не удалось сохранить медиафайл @@ -2541,21 +2066,17 @@ Выберите ключ восстановления или введите его вручную, введя или вставив из буфера обмена Невозможно расшифровать резервную копию с помощью этого ключа восстановления: убедитесь, что вы ввели правильный ключ. Не удалось получить доступ к защищенному хранилищу данных - Не зашифровано Зашифровано неподтверждённой сессией Проверьте, где вы вошли Подтвердите все свои сессии, чтобы убедиться в безопасности вашей учетной записи и сообщений Ручная проверка с помощью текста Пометить как надежный - Перепроверьте эту ссылку Ссылка %1$s перенаправит вас на другой сайт: %2$s. \n \nВы уверены, что хотите продолжить\? - Мы не смогли создать эту беседу. Пожалуйста, проверьте пользователей, которых вы приглашаете, и попробуйте ещё раз. - Добавить участников ПРИГЛАСИТЬ Приглашаем пользователей… @@ -2568,11 +2089,9 @@ Приглашения отправлены %1$s и еще %2$d пользователям Мы не могли пригласить этих пользователей. Пожалуйста, проверьте пользователей, которых вы хотите пригласить, и повторите попытку. - Текущий язык Другие доступные языки Загрузка доступных языков… - Посмотреть условия %s Отключиться от сервера идентификации %s\? Этот сервер идентификации устарел. Element поддерживает только API V2. @@ -2582,7 +2101,6 @@ Для вашей приватности, Element поддерживает отправку адреса электронной почты и номера телефона только в хэшированном виде. Привязка не удалась. Текущая взаимосвязь с этим идентификатором отсутствует. - Ваш домашний сервер (%1$s) предлагает использовать %2$s для вашего сервера идентификации Использовать %1$s Кроме того, вы можете ввести любой другой URL-адрес сервера идентификации @@ -2594,29 +2112,23 @@ Включить звук микрофона Отключить камеру Включить камеру - Защитите себя от потери доступа к зашифрованным сообщениям и данным, создав резервные копии ключей шифрования на вашем сервере. Настроить Используйте ключ безопасности Создайте ключ безопасности для хранения в надежном месте, например в менеджере паролей или сейфе. Использовать секретную фразу Введите секретную фразу, известную только вам, и создайте ключ для резервного копирования. - Сохраните свой ключ безопасности Храните ключ безопасности в надежном месте, например в менеджере паролей или сейфе. - Задайте секретную фразу Введите секретную фразу, известную только вам, для защиты данных на вашем сервере. Секретная фраза Для подтверждения введите вашу секретную фразу ещё раз. - Сохраните ваш ключ безопасности Храните ключ безопасности в надежном месте, например в менеджере паролей или сейфе. - Название комнаты Тема Вы успешно изменили настройки комнаты - У вас нет доступа к этому сообщению Расшифровка этого сообщения может занять некоторое время Не удалось расшифровать @@ -2625,17 +2137,12 @@ Нет доступа к этому сообщению, так как отправитель не доверяет вашей сессии Вы не можете получить доступ к этому сообщению, потому что отправитель намеренно не отправил ключи Ожидание истории шифрования - Riot теперь Element! Мы рады сообщить, что сменили имя! Ваше приложение обновлено, и вы вошли в свою учетную запись. ПОНЯТНО УЗНАТЬ БОЛЬШЕ - element - - Сохранить ключ восстановления в - Добавить из моей телефонной книги Ваша телефонная книга пуста Телефонная книга @@ -2643,13 +2150,10 @@ Получаем ваши контакты… Ваша контактная книга пуста Книга контактов - Отозвать приглашение Отозвать приглашение для %1$s\? - Забанен %1$s Не удалось разбанить пользователя - Push-уведомления отключены Просмотрите свои настройки, чтобы включить push-уведомления Выберите PIN-код для обеспечения безопасности @@ -2679,13 +2183,11 @@ %1$d/%2$d ключа успешно импортированы. %1$d/%2$d ключей успешно импортировано. - Управление интеграциями Нет активных виджетов Комната создана, но некоторые приглашения не отправлены по следующей причине: \n \n%s - %1$s, %2$s и %3$d читает %1$s, %2$s и %3$d других читают @@ -2705,15 +2207,12 @@ Телефонные номера Удалить %s\? Убедитесь, что вы перешли по ссылке в электронном письме, которое мы вам отправили. - Электронная почта и номера телефонов Управляйте электронной почтой и номерами телефонов, привязанными к вашей учетной записи Matrix - Код Используйте международный формат (номер телефона должен начинаться с \'+\') Подтвердите свою личность, проверив этот логин, предоставив ему доступ к зашифрованным сообщениям. К сожалению, эта операция пока недоступна для учетных записей, подключенных с помощью единого входа. - Невозможно открыть комнату, в которую вам запрещён доступ. Невозможно найти эту комнату. Убедитесь, что она существует. @@ -2721,13 +2220,58 @@ %d секунды %d секунд - Показать события статуса участников комнаты Включает в себя события приглашения/ присоединения/выхода/исключения/бана и изменение аватара/отображаемого имени. Голосование Кнопки бота Отреагировал: %s Результат проверки - Ссылка была искажена - + У вас нет разрешения на запуск звонка в этой комнате + Удалить данные учетной записи типа %1$s\? +\n +\nИспользуйте с осторожностью, это может привести к неожиданному поведению. + ПИН-код потребуется каждый раз, когда вы откроете Element. + ПИН-код потребуется через 2 минуты неиспользования Element. + Требовать PIN-код через 2 минуты + Отображать только количество непрочитанных сообщений в простом уведомлении. + Показывать подробности, такие как названия комнат и содержание сообщений. + Показывать содержимое в уведомлениях + PIN-код - единственный способ разблокировать Element. + Включить биометрические данные устройства, такие как отпечатки пальцев и распознавание лиц. + Включить биометрию + Настроить защиту + Защитите доступ с помощью PIN-кода и биометрии. + Защита доступа + Вы перезапустите приложение без истории, сообщений, доверенных устройств или доверенных пользователей + Если сбросить все + Делайте это только в том случае, если у вас нет другого устройства, с которого вы можете проверить это устройство. + Сбросить все + Забыли или потеряли все варианты восстановления\? Сбросить все + Вы вошли. + %s вошёл(ла). + Сообщения в этой комнате зашифрованы сквозным шифрованием. + Покинуть + Настройки + Сообщения здесь зашифрованы. +\n +\nВаши сообщения защищены замками, и только у вас и получателя есть уникальные ключи для их разблокировки. + Сообщения здесь не зашифрованы. + На этом домашнем сервере работает старая версия. Попросите администратора вашего домашнего сервера выполнить обновление. Вы можете продолжить, но некоторые функции могут работать некорректно. + Вы сделали доступ только по приглашению. + %1$s сделал(а) доступ только по приглашению. + Показать полную историю в зашифрованных комнатах + %1$s и %2$s + %1$s в %2$s и %3$s + + %d приглашение + %d приглашения + %d приглашений + %d приглашений + + Поиск в зашифрованных комнатах пока не поддерживается. + Фильтр заблокированных пользователей + У вас нет разрешения на запуск звонка + У вас нет разрешения на запуск конференции + Сброс + \ No newline at end of file diff --git a/vector/src/main/res/values-sk/strings.xml b/vector/src/main/res/values-sk/strings.xml index 66ba1f354d..13c8612e23 100644 --- a/vector/src/main/res/values-sk/strings.xml +++ b/vector/src/main/res/values-sk/strings.xml @@ -1757,7 +1757,6 @@ Na ďalšej obrazovke vás systém požiada o povolenie vždy bežať na pozadí Názov alebo ID (#priklad:matrix.org) Povoliť odpovedať švihnutím na časovej osy - Zlúčiť správy nepodarilo sa dešifrovať na časovej osy Zobrazovať záložku oznámenia na hlavnej obrazovke. Odkaz skopírovaný do schránky diff --git a/vector/src/main/res/values-sq/strings.xml b/vector/src/main/res/values-sq/strings.xml index 43506d8798..aa677f0b41 100644 --- a/vector/src/main/res/values-sq/strings.xml +++ b/vector/src/main/res/values-sq/strings.xml @@ -566,7 +566,7 @@ Të dhëna sesioni dërguesi Emër publik Emër publik - ID + ID Sesioni Kyç sesioni Verifikim Shenja gishtash Ed25519 @@ -1536,7 +1536,7 @@ S’kapet dot shërbyes Home te kjo URL, ju lutemi, kontrollojeni Do të përdoret %s si ndihmë kur shërbyesi juaj Home nuk ofron të tillë (gjatë thirrjes, adresa juaj IP do të ndahet me të tjerë) Që të kryhet ky veprim, shtoni një shërbyes identitetesh, që nga rregullimet tuaja. - Mënyrë Njëkohësimi Në Prapaskenë (Eksperimentale) + Mënyrë Njëkohësimi Në Prapaskenë E optimizuar për baterinë Element-i do të bëjë njëkohësim në prapaskenë, në një mënyrë që kursen burimet e kufizuara të pajisjes (baterinë). \nNë varësi të gjendjes së burimeve tuaja, njëkohësimi mund të shtyhet për më vonë nga sistemi operativ. @@ -1629,7 +1629,7 @@ Raportojeni këtë lëndë Arsye për raportimin e kësaj lënde RAPORTOJENI - BLLOKOJENI PËRDORUESIN + SHPËRFILLE PËRDORUESIN Lënda u raportua Kjo lëndë është raportuar. @@ -1690,7 +1690,7 @@ Hashi i lëndës s’u përputh me atë që pritej S’trajtoi dot të dhëna ndarjeje - Bllokoje përdoruesin + Shpërfille përdoruesin Krejt mesazhet (e zhurmshme) Krejt mesazhet @@ -2333,4 +2333,263 @@ Parashtroje Hap termat e %s + Luaje + Pauzë + Hidhe poshtë + + + S’keni leje të nisni një thirrje konferencë në këtë dhomë + S’keni leje të nisni thirrje në këtë dhomë + Ka tashmë një konferencë në ecuri e sipër! + Nis mbledhje video + Nis mbledhje audio + Mbledhjet përdorin siguri dhe rregulla lejesh Jitsi. Krejt personat aktualisht në dhomë do të shohin një ftesë për të marrë pjesë, teksa mbahet mbledhja juaj. + S’mund të bëni thirrje me vetveten + S’mund të bëni thirrje me vetveten, prisni për pjesëmarrësit të pranojnë ftesën + S’u arrit të shtohej widget + S’u arrit të hiqej widget + Pranoje + Hidhe poshtë + Mbylle + + Kopjoje + Sukses + + Njoftime + Thirrja Element Dështoi + S’u arrit të vendosej lidhje e atypëratyshme. +\nQë thirrjet të punojnë mirë, ju lutemi, kërkojini përgjegjësit të shërbyesit tuaj Home të formësojë një shërbyes TURN. + + Përzgjidhni Pajisje Zëri + Telefon + Altoparlant + Ndërroni Kamera + Ballore + E pasme + Mbyllni HD-në + Hapni HD-në + + Ky numër telefoni është i përcaktuar tashmë. + Gabim SSL: identiteti i ortakut s’është verifikuar. + Gabim SSL. + Parandalo thirrje aksidentale + Kërko për ripohim, para se të fillohet një thirrje + Thirrje Aktive (%s) + Kthehu te thirrje + + Anuloje ftesën + Të zhgradohet vetja\? + S’do të jeni në gjendje ta zhbëni këtë ndryshim, teksa zhgradoni veten, nëse jeni përdoruesi i fundit i privilegjuar, s’do të jetë e mundur të rifitoni privilegjet. + Zhgradoje + + + Shpërfille përdoruesin + Shpërfillja e këtij përdoruesi do të heqë mesazhet e tij prej dhomave që ndani me të. +\n +\nKëtë veprim mund ta zhbëni në çfarëdo kohe, te rregullimet e përgjithshme. + Hiqe shpërfilljen e përdoruesit + Heqja e shpërfilljes së këtij përdoruesi do të shfaqë sërish krejt mesazhet prej tij. + Anuloje ftesën + Jeni i sigurt se doni të anulohet ftesa për këtë përdorues\? + Përzëre përdoruesin + Arsye për përzënie + Përzënia e përdoruesit do ta heqë prej kësaj dhome. +\n +\nQë të pengohet pjesëmarrja sërish e tij, duhet ta dëboni. + Dëboje përdoruesin + Arsye për dëbim + Hiqja dëbimin përdoruesit + Heqja e dëbimit përdoruesit do t’i lejojë të marrë pjesë sërish në dhomë. + + Te llogaria juaj s’është shtuar ndonjë numër telefoni + Adresa email + Te llogaria juaj s’është shtuar ndonjë email + Numra telefoni + Të hiqet %s\? + Sigurohuni që keni klikuar te lidhja në email-in që ju kemi dërguar. + + + %d sekondë + %d sekonda + + + Shfaq akte gjendjeje përdoruesish të dhomës + Përfshin akte ftimi/pjesëmarrjeje/ikjeje/përzënieje/dëbimi dhe ndryshime emri avatari/shfaqjeje. + Kopjeruajtje e Sigurt + Administroni + Ujdisni Kopjeruajtje të Sigurt + Riktheje Kopjeruajtjen e Sigurt te Parazgjedhjet + Ujdise në këtë pajisje + Ruhuni nga humbja e hyrjes te mesazhe & të dhëna të fshehtëzuara, duke kopjeruajtur kyçe fshehtëzimi në shërbyesin tuaj. + Prodhoni një Kyç të ri Sigurie ose caktoni një Frazë të re Sigurie për kopjeruajtjen tuaj ekzistuese. + Kjo do të zëvendësojë Kyçin ose Frazën tuaj aktuale. + + Integrimet janë të çaktivizuara + Që të bëhet kjo, aktivizoni “Lejo integrime”, te Rregullimet. + + Email-e dhe numra telefonash + Administroni email-e dhe numra telefonash të lidhur me llogarinë tuaj Matrix + + + %d përdorues i dëbuar + %d përdorues të dëbuar + + + Kyçet u eksportuan me sukses + + + %1$d/%2$d kyç i importuar me sukses. + %1$d/%2$d kyçe të importuar me sukses. + + + SHIHENI + Widget-e aktivë + + + Administroni Integrime + S’ka widget-e aktivë + Kyçi i rikthimeve u ruajt. + + Kopjeruajtje e Sigur + Ruhuni nga humbja e hyrjes në mesazhe & të dhëna të fshehtëzuara + + Ujdisni Kopjeruajtje të Sigurt + + Dhoma është krijuar, por disa ftesa s’janë dërguar, për arsyen vijuese: +\n +\n%s + + Shtoni një skedë enkas për njoftime të palexuar në skenën kryesore. + + Kod + + %1$s, %2$s dhe %3$d tjetër i lexuar + %1$s, %2$s dhe %3$d të tjerë të lexuar + + Shtoje te të parapëlqyerit + Hiqe prej të parapëlqyerve + S’bëtë ndryshime + E bëtë dhomën publike për këdo që di lidhjen. + E bëtë dhomën vetëm me ftesa. + Jepni adresën e shërbyesit që doni të përdoret + + Ju lutemi, përdorni formatin ndërkombëtar (numrat e telefonave duhet të fillojnë me \'+\') + Nëse s’e dini fjalëkalimin, kthehuni mbrapsht që ta ricaktoni. + Ngjitës + Pyetësor + Butona Roboti + Reagoi me: %s + Përfundim Verifikimi + + Veprime Përgjegjësi + %1$s, si parazgjedhje + Përgjegjësi i shërbyesit tuaj ka çaktivizuar, si parazgjedhje, fshehtëzim skaj-më-skaj në dhoma private & Mesazhe të Drejtpërdrejtë. + Të fshihen të dhënat e llogarisë të llojit %1$s\? +\n +\nPërdoreni me kujdes, mund të shpjerë në sjellje të papritura. + + Jepni një frazë sigurie që e dini vetëm ju, e përdorur për të siguruar të fshehta në shërbyesin tuaj. + + Nëse e anuloni tani, mund të humbni mesazhe & të dhëna të fshehtëzuara, nëse humbni hyrje te kredencialet tuaja. +\n +\nMundeni edhe të ujdisni Kopjeruajtje të Sigurt & të administroni kyçet tuaj, te Rregullimet. + + Krijuat dhe formësuat dhomën. + + Kjo llogari është çaktivizuar. + + S’u ruajt dot kartelë media + Ripohoni identitetin tuaj duke verifikuar këto kredenciale hyrjeje, duke i akorduar hyrje te mesazhe të fshehtëzuar. + Na ndjeni, ky veprim s’është ende i mundshëm për llogari të lidhura duke përdorur Hyrje Njëshe. + + Për privatësinë tuaj, Element-i mbulon vetëm dërgim email-esh dhe numrash telefoni përdoruesi të koduar. + Caktoni rol + Rol + Hapni fjalosje + Mbylle mikrofonin + Hape mikrofonin + Ndale kamerën + Nise kamerën + + Ujdisni Kopjeruajtje të Sigurt + + Kopjeruajtje e sigurt + Ruhuni nga humbja e hyrjes te mesazhe & të dhëna të fshehtëzuara, duke kopjeruajtur në shërbyesin tuaj kyçet e fshehtëzimit. + Ujdiseni + Përdor një Kyç Sigurie + Prodhoni një kyç sigurie që të depozitohet diku në një vend të parrezik, bie fjala, në një përgjegjës fjalëkalimesh ose kasafortë. + Përdor një Frazë Sigurie + Jepni një frazë të fshehtë që e dini vetëm ju, dhe prodhoni një kyç për kopjeruajtjen. + + Ruani Kyçin tuaj të Sigurisë + Depozitojeni Kyçin tuaj të Sigurisë në një vend të parrezik, bie fjala, një përgjegjës fjalëkalimesh ose një kasafortë. + + Caktoni një Frazë Sigurie + Jepni një frazë sigurie që e dini vetëm ju, e përdorur për të siguruar të fshehta në shërbyesin tuaj. + Frazë Sigurie + Rijepni Frazën tuaj të Sigurisë, për ta ripohuar. + + Ruani Kyçin tuaj të Sigurisë + Depozitojeni Kyçin tuaj të Sigurisë në një vend të parrezik, bie fjala, një përgjegjës fjalëkalimesh ose një kasafortë. + + Emër Dhome + Temë + Ndryshuat me sukses rregullimet për dhomën + + S’mund të hapni këtë mesazh + Po pritet për këtë mesazh, kjo mund të zgjasë ca + Nuk Shfshehtëzohet Dot + Për shkak të fshehtëzimit skaj-më-skaj, mund t’ju duhet të prisni që të mbërrijë mesazhi i dikujt, ngaqë kyçet e fshehtëzimit s’qenë ujdisur si duhet për ju. + S’mund ta hapni këtë mesazh, ngaqë jeni bllokuar nga dërguesi + S’mund të hapni këtë mesazh, ngaqë sesionit tuaj nuk i zihet besë nga dërguesi + S’mund ta hapni këtë mesazh, ngaqë dërguesi qëllimisht nuk ju dërgoi kyçet + Po pritet për historik fshehtëzimesh + + Riot-i tanimë quhet Element! + Jemi të ngazëllyer t’ju njoftojmë se kemi ndërruar emër! Aplikacioni juaj është i përditësuar dhe jeni i futur në llogarinë tuaj. + E MORA VESH + MËSONI MË TEPËR + + element + + + Ruaje kyçin e rimarrjeve te + + Shto prej numëratorit tim telefonik + Numëratori juaj telefonik është i zbrazët + Numërator telefonik + Kërko te kontaktet e mia + Po merren kontaktet tuaja… + Numëratori juaj telefonik është i zbrazët + Libër adresash + + Shfuqizo ftesën + Të shfuqizohet ftesa për %1$s\? + + Dëbuar nga %1$s + S’u arrit të Hiqej Dëbimi i përdoruesit + + Njoftimet push janë të çaktivizuar + Që të aktivizoni njoftimet push, shihni te rregullimet tuaja + + Kod i gabuar, edhe %d provë + Kod i gabuar, edhe %d prova + + Kujdes! Prova e fundit e mbetur, përpara daljes! + Shumë gabime, u nxorët nga llogaria + Zgjidhni një PIN për siguri + Ripohoni PIN-in + S’u arrit të vlerësohet pin-i, ju lutemi, jepni një të ri. + Jepni PIN-in tuaj + Harruat PIN-in\? + Ricaktoni pin-in + Pin i ri + Që të ricaktoni PIN-in tuaj, do t’ju duhet të ribëni hyrjen dhe të krijoni një të ri. + Aktivizo PIN-in + Nëse doni të ricaktoni PIN-in tuaj, prekni “Harrova PIN-in”, që të bëhet dalja nga llogaria dhe ricaktimi. + Që të çaktivizohet PIN-i, ripohoni PIN-in + S’mund të hapet një dhomë prej të cilës jeni dëbuar. + S’gjendet dot kjo dhomë. Sigurohuni që ekziston. + + Lidhja qe e keqformësuar diff --git a/vector/src/main/res/values-sv/strings.xml b/vector/src/main/res/values-sv/strings.xml index 7915abb41c..7f68136071 100644 --- a/vector/src/main/res/values-sv/strings.xml +++ b/vector/src/main/res/values-sv/strings.xml @@ -1,20 +1,17 @@ - + sv SE Latn - Ljust tema Mörkt tema Svart tema Status.im-tema - Initialiserar tjänst Synkroniserar… Lyssnar efter händelser Högljudda aviseringar Tysta aviseringar - Meddelanden Rum Inställningar @@ -26,7 +23,6 @@ Säkerhetskopiering av nycklar Använd nyckelsäkerhetskopiering Verifiera session - Säkerhetskopiering av nycklar är inte färdig, vänligen vänta… Du kommer att förlora dina krypterade meddelanden om du loggar ut nu Säkerhetskopiering av nycklar pågår. Om du loggar ut nu så kommer du att förlora åtkomst till dina krypterade meddelanden. @@ -37,11 +33,8 @@ Är du säker\? Säkerhetskopiera Du kommer att förlora åtkomst till dina krypterade meddelanden om du inte säkerhetskopierar dina nycklar innan du loggar ut. - Tredjepartslicenser - Laddar… - OK Avbryt Spara @@ -90,7 +83,6 @@ Ignorera Granska Avslå - Avsluta Handlingar Logga ut @@ -106,27 +98,22 @@ Stäng Kopierat till klippbordet Stäng av - Bekräftelse Varning Fel - Hem Favoriter Personer Rum Gemenskaper - Filtrera rumsnamn Filtrera favoriter Filtrera personer Filtrera rumsnamn Filtrera gemenskapsnamn - Inbjudningar Låg prioritet Systemvarningar - Konversationer Lokal adressbok Användarkatalog @@ -135,20 +122,17 @@ Du gav inte Element tillgång till dina lokala kontakter Inga resultat Ingen identitetsserver konfigurerad. - Rum Rumskatalog Inga rum Inga offentliga rum tillgängliga - 1 användare + %d användare %d användare - Bjud in Gemenskaper Inga grupper - Skicka loggar Skicka kraschloggar Skicka skärmdump @@ -160,14 +144,11 @@ Du verkar skaka din telefon i frustration. Vill du skicka en buggrapport\? Appen kraschade senaste gången. Vill du skicka en buggrapport\? Raseriskaka för att rapportera bugg - Buggrapporten har skickats framgångsrikt Sändning av buggrapporten misslyckades (%s) Framsteg (%s%%) - Skicka in i Läs - Gå med i rummet Användarnamn Skapa konto @@ -176,13 +157,10 @@ URL för hemserver URL för identitetsserver Sök - Starta ny chatt Starta röstsamtal Starta videosamtal - Skicka röst - Är du säker på att du vill skapa en ny chatt med %s\? Är du säker på att du vill starta ett röstsamtal\? Är du säker på att du vill skapa ett videosamtal\? @@ -192,20 +170,16 @@ \nAlternativt så kan du försöka använda den publika servern på %2$s, men detta kommer inte att vara lika pålitligt, och kommer att dela din IP-adress med den servern. Du kan också hantera detta i inställningarna. Försök med %s Fråga mig inte igen - Skicka filer Skicka dekal Ta foto eller video Ta ett foto Ta en video - Du har för närvarande inga dekalpaket aktiva. \n \nLägg till några nu\? - fortsätt med… Tyvärr har ingen extern applikation hittats som kan fullfölja denna handling. - Logga in Logga in med externt konto Skapa konto @@ -259,7 +233,6 @@ \n \nDu har loggats ut ur alla sessioner och kommer inte längre motta pushnotiser. För att återaktivera pushnotiser, logga in igen på varje enhet. Vänligen granska och acceptera villkoren för denna hemserver: - URLen måste börja med http[s]:// Kan inte logga in: Nätverksfel Kan inte logga in @@ -271,7 +244,6 @@ Det här är inte en giltig Matrixserveradress Kan inte nå en hemserver på den här URLen, vänligen kolla den Din enhet använder ett utdaterat TLS-protokoll, sårbart för anfall, så för din säkerhets skull så kommer du inte kunna ansluta - Felaktigt användarnamn/lösenord Den åtkomsttoken du specificerade kändes inte igen Felformaterad JSON @@ -279,47 +251,35 @@ För många förfrågningar har skickats Det här användarnamnet är upptaget E-postlänken har inte klickats på än - Efterfråga krypteringsnycklarna igen från dina andra sessioner. - Nyckelförfrågan har skickats. - Förfrågan har skickats Vänligen öppna Element på en annan enhet som kan dekryptera meddelandet så att den kan skicka nycklarna till den här sessionen. - Läsindikationslista - Grupplista - - 1 medlemsskapsändring + %d medlemsskapsändring %s medlemsskapsändringar - Skicka som Original Stor Medium Liten - Avbryt nedladdningen\? Avbryt uppladdningen\? %d s %1$dm %2$ds - Igår Idag - Rumsnamn Rumsämne - Samtal Använd förvald Element-ringsignal för inkommande samtal Tillåt reservassistansserver för samtal Kommer att använda %s som reserv när din hemserver inte erbjuder en (din IP-adress kommer att delas under samtalet) Ringsignal för inkommande samtal Välj ringsignal för samtal: - Ring Samtal anslutet Samtal ansluter… @@ -330,15 +290,12 @@ Inkommande röstsamtal Samtal pågår… Videosamtal pågår… - Den andra parten svarade inte. Mediaanslutning misslyckades Kan inte initialisera kameran samtal besvarat någon annanstans - Ta en bild eller en video Kan inte spela in video - Information Element behöver tillstånd att komma åt ditt foto- och videobibliotek för att skicka och spara bilagor. \n @@ -358,25 +315,20 @@ Element kan kolla i din adressbok för att hitta andra Matrixanvändare baserat på deras e-postadresser och telefonnummer. \n \nVill du dela din adressbok för detta ändamål\? - Tyvärr. Handlingen utfördes inte på grund av saknade rättigheter - Sparad Spara till nedladdningar\? JA NEJ Fortsätt - Ta bort Gå med Förhandsvisning Avslå - Lista medlemmar Öppna rubrik Synkroniserar… Hoppa till det första olästa meddelandet. - Du har blivit inbjuden till det här rummet av %s Denna inbjudan skickades till %s, vilket inte är associerat med det här kontot. \nDu kanske vill logga in på ett annat konto eller lägga till den här e-postadressen till det här kontot. @@ -387,48 +339,43 @@ Ny chatt Lägg till medlem - 1 aktiv medlem + %d aktiv medlem %d aktiva medlemmar - 1 medlem + %d medlem %d medlemmar 1 medlem - - 1s + %ds %ds - 1m + %dm %dm - 1t + %dt %dt - 1d + %dd %dd - Lämna rum Är du säker på att du vill lämna rummet\? Är du säker på att du vill ta bort %s från den här chatten\? Skapa - Online Offline Inaktiv %1$s nu %1$s för %2$s sen - ADMINISTRATIONSVERKTYG RING DIREKTCHATT SESSIONER - Bjud in Lämna det här rummet Ta bort från det här rummet @@ -443,7 +390,7 @@ RUM RUM - 1 rum + %d rum %d rum @@ -454,25 +401,20 @@ Fäst rum med missade aviseringar Fäst rum med olästa meddelanden Datasparningsläge använder ett specifikt filter så att närvarouppdateringar och \"skriver\"-statusar filtreras ut. - %s försökte ladda en specifik punkt i det här rummets tidslinje, men kunde inte hitta den. - Skriv en hemserver att visa listan över offentliga rum från Alla rum på %s-servern Alla %s-rum på Matrix - - 1 rum + %d rum %d rum - Rum Rum Filtrera gruppmedlemmar Filtrera grupprum - - 1 rum + %d rum %d rum Detta kommer att göra ditt konto permanent oanvändbart. Du kommer inte kunna logga in, och ingen kommer kunna registrera sig med samma användar-ID. Detta kommer att få ditt konto att lämna alla rum det är med i, och det kommer att ta bort din kontoinformation från din identitetsserver. Den här handlingen går inte att ångra. @@ -486,36 +428,27 @@ Du har inga olästa meddelanden kvar Rum Dina rum kommer att visas här - Skapa ett nytt rum Rum Direktmeddelanden - Ge ett förslag Vänligen skriv ditt förslag nedan. Beskriv ditt förslag här Tack, ditt förslag har skickats framgångsrikt Förslaget misslyckades att skickas (%s) - Direktmeddelanden - Filtrera konversationer… Kan du inte hitta det du letar efter\? Skapa ett nytt rum Skicka ett nytt direktmeddelande Se rumskatalogen - Filtrera efter användarnamn eller ID… - Skapa en ny direktkonversation Skapa ett nytt rum Skriv nyckelord för att hitta en reaktion. - Kan inte hitta en giltig hemserver. Vänligen kontrollera din identifierare - Nyliga rum Andra rum - Kan inte hitta hemligheter i lagringen När rum uppgraderas Stöds endast I krypterade rum @@ -539,7 +472,6 @@ Lösenordet är ogiltigt Ditt lösenord har uppdaterats Du är för närvarande inte medlem in någon gemenskap. - Bekräfta genom att jämföra följande nyckel med användarinställningarna i din andra session: Ditt visningsnamn För att fortsätta, skriv in ditt lösenord: @@ -554,16 +486,12 @@ \n \nOm det inte var du som satte upp den nya återställningsmetoden så kan det vara en angripare som försöker komma åt ditt konto. Ändra ditt kontolösenord och sätt en ny återställningsmetod omedelbart i inställningarna. Ingen identitetsserver är konfigurerad, vilket krävs för att återställa ditt lösenord. - Byt nätverk Alla gemenskaper - Allmänt Alternativ Namn eller ID (#example:matrix.org) - Du kommer inte att verifiera %1$s (%2$s) om du avbryter nu. Börja igen i hens användarprofil. - Meddelanden i det här rummet är totalsträckskrypterade. Lär dig mer och verifiera användare i deras profiler. Avignorera Är du säker på att du vill bjuda in %s till den här chatten\? @@ -571,7 +499,6 @@ Bjud in användare med ID Vänligen skriv in en eller flera e-postadresser eller Matrix-IDn E-postadress eller Matrix-ID - Kunde inte verifiera den externa serverns identitet. Rumsdetaljer Personer @@ -582,7 +509,6 @@ \nObservera att den har handlingen kommer att starta om appen, och det kan ta lite tid. PERSONER FILER - Alla meddelanden (högljutt) Alla meddelanden Endast omnämnande @@ -597,19 +523,16 @@ Visa info om appen i systeminställningarna. Avancerade aviseringsinställningar Aviseringsprioritet för händelser - Aviseringssekretess Felsök aviseringar Grundläggande felsökning lyckades. Om du fortfarande inte får aviseringar, vänligen skicka en buggrapport för att hjälpa oss att undersöka. Ett eller flera tester har misslyckats, testa de föreslagna fixarna. Ett eller flera tester har misslyckats, vänligen skicka en buggrapport för att hjälpa oss att undersöka. - Aviseringar är aktiverade i systeminställningarna. Aviseringar är inaktiverade i systeminställningarna. \nVänligen kolla systeminställningarna. Aviseringar är aktiverade för ditt konto. Aviseringar är inaktiverade för ditt konto. -\n \nVänligen kolla kontoinställningarna. Aviseringar är aktiverade för den här sessionen. Aviseringar är inaktiverade för den här sessionen. @@ -624,7 +547,6 @@ Tjänsten kommer inte att starta när enheten startas om, så du kommer inte att få aviseringar förrens Element har öppnats en gång. Bakgrundsbegränsningar är aktiverade för Element. \nSaker som appen försöker göra kommer att kraftigt begränsas när appen är bakgrunden, och detta kan påverka aviseringar. -\n \n%1$s Om en användare lämnar sin enhet urkopplad och stilla en längre tid, med skärmen av, så går enheten in i Doze-läget. Detta hindrar appar från att komma åt nätverket och skjuter upp deras uppgifter, synkroniseringar och standardlarm. • Aviseringar skickas via Firebase Cloud Messaging @@ -632,7 +554,6 @@ • Meddelandeinnehåll i aviseringen hämtas på ett säkert sätt från Matrix-hemservern • Aviseringar innehåller meta- och meddeladedata • Aviseringar kommer inte att visa meddelandeinnehåll - Aviseringsljud Aktivera aviseringar för det här kontot Aktivera aviseringar för den här sessionen @@ -640,8 +561,6 @@ Konfigurera samtalsaviseringar Konfigurera tysta aviseringar Välj LED-färg, vibration, ljud… - - Meddelanden innehållande mitt användarnamn Meddelanden i direktchattar Meddelanden i gruppchattar @@ -652,7 +571,6 @@ Behåll media Rensa cache Rensa mediacache - Aviseringar Ignorerade användare Avancerat @@ -678,12 +596,10 @@ Den här e-postadressen är upptagen. Den här e-postadressen kunde inte hittas. Ett fel hände vid verifiering av din e-postadress. - Visa alla meddelanden från %s\? \n \nObservera att den har handlingen kommer att starta om appen, och det kan ta lite tid. Är du säker på att du vill ta bort det här aviseringsmålet\? - Media Förvald mediakälla Emblem @@ -694,37 +610,29 @@ Totalsträckskryptering Totalsträckskryptering är aktiv Skicka aldrig krypterade meddelanden till overifierade sessioner i det här rummet från den här sessionen. - Det här rummet visar inte emblem för några gemenskaper Kryptering är aktiverat i det här rummet. Totalsträckskrypteringsinformation - Publikt namn (synligt för folk som du pratar med) En sessions publika namn är synligt för personer du pratar med Återställning av krypterade meddelanden Skicka aldrig krypterade meddelanden till overifierade sessioner från den här sessionen. - %d avisering %d aviseringar - Läs DRM-skyddad media - Kommandot \"%s\" behöver fler parametrar, eller så är några av parametrarna felaktiga. Lämna rummet På/av Markdown Av Tysta Högljudda - Personer Inaktivera konto Vänligen glöm alla meddelanden jag har skickat när mitt konto avaktiveras (varning: detta får framtida användare att se en inkomplett vy av konversationer) Inaktivera konto - Ingen giltig \"Google Play-tjänster\"-APK hittades. Aviseringar kanske inte funkar ordentligt. - Bli aldrig av med krypterade meddelanden Meddelanden i krypterade rum är säkrade med totalsträckskryptering. Bara du och mottagaren har nycklarna för att läsa dessa meddelanden. \n @@ -740,17 +648,13 @@ Version Ogiltigt hemserversvar Du har blivit utloggad på grund av ogiltiga eller utgångna uppgifter. - Verifiera den här sessionen för att markera den som betrodd. Att lita på folks sessioner ger dig extra sinnesro när du skickar totalsträckskrypterade meddelanden. Säkra meddelanden mellan dig och den här användaren är totalsträckskrypterade och kommer inte att vara läsbara för tredje parter. Du använder ingen identitetsserver - Matrix-SDK-version Säkerhet och sekretess Röst & video Hjälp & om - - Bli upptäckbar av andra Identitetsserver Koppla bort identitetsserver @@ -772,54 +676,42 @@ Identitetsservern du har valt har inga användarvillkor. Fortsätt endast om du litar på ägaren av servern Du delar för närvarande e-postadresser och telefonnummer på hemservern %1$s. Du kommer behöva återansluta till %2$s för att sluta dela dem. Acceptera identitetsserverns (%s) användarvillkor för att låta dig själv vara upptäckbar med e-postadress eller telefonnummer. - Mångordiga loggar kommer att hjälpa utvecklare genom att skicka mer loggar när du skickar en raseriskakning. Även när det är aktiverat, så loggar appen inga meddelandeinnehåll eller annan privat data. - - Visa lösenord Dölj lösenord MEDIA Det finns ingen media i det här rummet FILER Det finns inga filer i det här rummet - Det här innehållet rapporterades. \n -\nOm du inte vill se något mer innehåll från den här användaren så kan du blockera hen för att dölja hens meddelanden +\nOm du inte vill se något mer innehåll från den här användaren så kan du ignorera denne för att dölja dennes meddelanden. Det här innehållet rapporterades som spam. \n -\nOm du inte vill se något mer innehåll från den här användaren så kan du blockera hen för att dölja hens meddelanden +\nOm du inte vill se något mer innehåll från den här användaren så kan du ignorera denne för att dölja dennes meddelanden. Det här innehållet rapporterades som olämpligt. \n -\nOm du inte vill se något mer innehåll från den här användaren så kan du blockera hen för att dölja hens meddelanden - +\nOm du inte vill se något mer innehåll från den här användaren så kan du ignorera denne för att dölja dennes meddelanden. Alla meddelanden (högljutt) Alla meddelanden Endast omnämnande Tysta Lämna rummet Långklicka på ett rum för att se fler alternativ - - %1$s ändrade rummet till endast inbjudna. Chatta med folk direkt eller i grupp Precis som med e-post så har konton ett hem, fast du kan prata med vem som helst Lär dig mer Anpassade & avancerade inställningar - Den här e-postadressen är inte associerad med något konto. - Återställ lösenordet på %1$s Ett verifikations-e-brev kommer att skickas till din inkorg för att bekräfta bytet av ditt lösenord. E-post Nytt lösenord - Att ändra ditt lösenord kommer att återställa alla nycklar för totalsträckskryptering på alla dina sessioner, och göra krypterad chatthistorik oläslig. Sätt upp nyckelsäkerhetskopiering eller exportera dina rumsnycklar från en annan session innan du återställer ditt lösenord. Den här e-postadressen är inte länkad till något konto - Ett verifikations-e-brev skickades till %1$s. Jag har verifierat min e-postadress - Du har blivit utloggad ur alla sessioner och kommer inte längre motta pushnotiser. För att återaktivera pushnotiser, logga in igen på varje enhet. Sätt e-postadress Sätt en e-postadress för att kunna återförva ditt konto. Senare kan du valfritt låta personer du känner upptäcka dig med din e-postadress. @@ -831,19 +723,14 @@ Vi skickade just ett e-brev till %1$s. \nKlicka på länken i det för att fortsätta med kontoskapandet. Den här hemservern kör en för gammal version för att ansluta till. Be administratören för hemservern att uppdatera. - Logga in för att återfå krypteringsnycklar som endast lagras på den här enheten. Du behöver dem för att läsa alla dina säkra meddelanden på andra enheter. Rensa all data lagrad på den här enheten\? \nLogga in igen för att komma åt din kontodata och dina meddelanden. Avancerade inställningar Visar endast de första resultaten, skriv mer… - Element kan krascha mer när ett oväntat fel inträffar - Efter aktivering så kan kryptering inte avaktiveras. - Din e-postdomän är inte auktoriserad för att registrera på den här servern - Meddelanden i det här rummet är inte totalsträckskrypterade. Meddelanden i det här rummet är totalsträckskrypterade. \n @@ -862,36 +749,26 @@ Moderatorer Den här sessionen kan inte dela den här verifikationen med dina andra sessioner. \nVerifikationen kommer att sparas lokalt och delas i en framtida version av appen. - Aktivera totalsträckskryptering Efter aktivering så kan kryptering inte avaktiveras. - Efter aktivering så kan kryptering inte avaktiveras. Meddelanden som skickas i ett krypterat rum kan inte läsas av servern, bara av deltagarna i rummet. Att aktivera kryptering kan kan göra att många bottar och bryggor inte funkar ordentligt. Meddelanden mellan dig och den här användaren är totalsträckskrypterade och kan inte läsas av tredje parter. Din nya session är nu verifierad. Den har tillgång till dina krypterade meddelanden, och andra användare kommer att se den som betrodd. - - Verifiera den här sessionen gör att markera den som betrodd och ge den tillgång till dina krypterade meddelanden. Om det inte var du som loggade in i den här sessionen så kan ditt konto vara kapat: - Använd en existerande session för att verifiera den här och ge den tillgång till krypterade meddelanden. - - Din %2$s & %1$s är nu satta. \n \nHåll dem säkra! Du kommer att behöva dem för att låsa upp krypterade meddelanden och säker information om du förlorar alla dina aktiva sessioner. - Kryptering aktiverat Kryptering inte aktiverat Aviseringskonfiguration Meddelanden som innehåller @room Sätt aviseringsprioritet för händelser - Mediafiler tillagda till galleriet Kunde inte lägga till mediafilen till galleriet Granska var du är inloggad Verifiera alla dina sessioner för att se till att ditt konto och dina meddelanden är säkra Vi kunde inte skapa ditt DM. Vänligen kolla användarna du vill bjuda in och försök igen. - BJUD IN Bjud in användare @@ -899,7 +776,6 @@ Inbjudan skickad till %1$s och %2$d till Vi kunde inte bjuda in användarna. Vänligen kolla användarna du vill bjuda in och försök igen. - Koppla bort från identitetsservern %s\? Den här identitetsservern är utdaterat. Element stöder endast API V2. Vänligen konfigurera en identitetsserver. @@ -908,7 +784,6 @@ Alternativt så kan du skriva in en annan identitetsserver-URL Skriv in URL för en identitetsserver Lås upp krypterad meddelandehistorik - Använd den här sessionen för att verifiera din nya och ge den tillgång till krypterade meddelanden. Om du avbryter så kommer du inte kunna läsa krypterade meddelanden på den här enheten, och andra användare kommer inte att lita på den Om du avbryter så kommer du inte kunna läsa krypterade meddelanden på din nya enhet, och andra användare kommer inte att lita på den @@ -917,31 +792,22 @@ \n \nOm du inte vill sätta ett meddelandelösenord, generera en meddelandenyckel istället. Att sätta en återställningslösenfras låter dig säkra och låsa upp krypterade meddelanden och pålitlighet. - - När jag bjuds in till ett rum Samtalsinbjudningar Meddelanden skickade av en bott - Användargränssnitt Språk Välj språk - Tema - Ditt tema Nuvarande språk Andra tillgängliga språk Laddar tillgängliga språk… - Spela Pausa Avfärda - - Kopiera Framgång - Aviseringar Byt kamera Att ignorera den här användaren kommer att ta bort hens meddelanden från rum ni delar. @@ -951,33 +817,28 @@ Skicka ett meddelande (okrypterat)… Filen hittades inte - 1 nytt meddelande + %d nytt meddelande %d nya meddelanden - Certifikatet har ändrats från ett betrott till ett som inte är betrott. Servern kan ha förnyat sitt certifikat. Kontakta serveradministratören för det förväntade fingeravtrycket. Inställningar Inställningar Systeminställningar. Öppna inställningar - Kontoinställningar. Sessionsinställningar. Anpassade inställningar. Kolla inställningar - Normal Integrationer Använd en integrationshanterare för att hantera bottar, bryggor, widgets och dekalpaket. \nIntegrationshanterare får konfigurationsdata, och kan ändra widgets, skicka rumsinbjudningar, och ändra behörighetsnivåer å dina vägnar. Skicka meddelande med retur Returtangenten på mjukvarutangentbordet kommer att skicka ett meddelande istället för att lägga till ett radslut - Skydda dig mot att tappa åtkomst till krypterade meddelanden och data genom att säkerhetskopiera krypteringsnycklarna på din server. Tillåt integrationer Integrationer är avstängda Aktivera \'Tillåt integrationer\' I inställningarna för att göra detta. - Exportera nycklarna till en lokal fil Krypteringsnycklarna har sparats till \'%s\'. \n @@ -988,18 +849,17 @@ \nVi rekommenderar att du går igenom verifieringsprocessen för varje session innan du fortsätter, men du kan skicka meddelandet utan att verifiera om du föredrar. \n \nOkända sessioner: - Välj en rumskatalog - 1 oläst aviserat meddelande + %d oläst aviserat meddelande %s olästa aviserade meddelanden - 1 oläst aviserat meddelande + %d oläst aviserat meddelande %s olästa aviserade meddelanden - %1$s: 1 meddelande + %1$s: %2$d meddelande %1$s: %2$d meddelanden Teckenstorlek @@ -1010,50 +870,37 @@ Större Störst Enorm - Använd kameran Lägg till Matrix-appar Använd den inbyggda kameran Starta systemets kamera istället för den inbyggda kameraskärmen. Använd returtangenten för att skicka meddelande Krypterat meddelande - Kontakta administratören - kontakta din tjänstadministratör - Spara som fil Återställningsnyckeln har sparats till \'%s\'. \n \nVarning: den har filen kan komma att raderas om appen avinstalleras. Meddelandeåterställning - Radera dina säkerhetskopierade krypteringsnycklar från servern\? Du kommer inte längre kunna använda din återställningsnyckel för att läsa krypterad meddelandehistorik. - Skydda dig mot att tappa åtkomst till krypterade meddelanden och data - Nya säkra meddelandenycklar Den här sessionen fick ett oväntat meddelande Ett ogiltigt meddelande mottogs Dina direktmeddelandekonversationer kommer att visas här Meddelande raderat Importera krypteringsnycklar från filen \"%1$s\". - Krypterar fil… Skickar fil (%1$s / %2$s) - Laddar ner filen %1$s… Filen %1$s har laddats ner! - Meddelanderedigeringar - Sammanfogaren misslyckades med att avkryptera ett meddelande i tidslinjen Lägg till med Matrix-ID Inga resultat hittade, använd \"Lägg till med Matrix-ID\" för att söka på servern. Använd bottar, bryggor, widgets och dekalpaket - Ett textmeddelande har skickats till %s. Vänligen skriv in verifieringskoden det innehåller. Filen \'%1$s\' (%2$s) är för stor för att laddas upp. Gränsen är %3$s. - Fil Kontakt Kamera @@ -1063,38 +910,28 @@ Skickar det valda meddelandet som en spoiler Gå med miljontals användare gratis på den största publika servern Lägger till ¯\\_(ツ)_/¯ till början av ett textmeddelande - Ljud Fil Dekal - Element hanterar inte meddelanden av typen \'%1$s\' Skickar det valda meddelandet I regnbågsfärger Meddelanderedigering - Meddelandenyckel Generera en meddelandenyckel - Om du avbryter nu så kan du komma att förlora krypterade meddelanden och data om du förlorar åtkomst till dina inloggade sessioner. \n \nDu kan också sätta upp säker säkerhetskopiering och hantera dina nycklar in inställningarna. - Krypterade meddelanden i en-till-en-chattar Krypterade meddelanden i gruppchattar Felsökning Skicka ett meddelande som vanlig text, utan att tolka det som Markdown - Meddelande… - Använd fil - Kunde inte spara mediefil Sätt ett nytt kontolösenord… - Bekräfta din identitet genom att verifiera den här inloggningen från en av dina andra sessioner, och ge den åtkomst till dina krypterade meddelanden. Stoppa kameran Starta kameran - Skydda dig mot att tappa åtkomst till krypterade meddelanden och data genom att säkerhetskopiera krypteringsnycklarna på din server. Du kan inte komma åt det här meddelandet Väntar på det här meddelandet, det här kan ta ett tag @@ -1116,7 +953,6 @@ Elementsamtal misslyckades Misslyckades att upprätta en realtidsuppkoppling. \nVänligen be administratören för din hemserver att konfigurera en TURN-server för att samtal ska fungera pålitligt. - Välj ljudenhet Telefon Högtalare @@ -1126,25 +962,20 @@ Bak Stäng av HD Sätt på HD - SSL-fel: andra partens identitet har inte verifierats. SSL-fel. Förhindra oavsiktligt samtal Fråga efter bekräftelse innan ett samtal startas Aktivt samtal (%s) Återgå till samtal - Avbryt inbjudan Omnämn Visa sessionslista Du kommer inte kunna ångra den här ändringen eftersom du befordrar användaren till samma behörighetsnivå som dig själv. \nÄr du säker\? - Degradera dig själv\? Du kommer inte kunna ångra den här ändringen eftersom du degraderar dig själv. Om du är den sista privilegierade användaren i det här rummet så kommer det att vara omöjligt att återfå privilegier. Degradera - - Ignorera användare Avignorera användare Att avignorera den här användaren kommer att visa alla meddelanden från hen igen. @@ -1160,17 +991,13 @@ Avbanna användaren Att banna användaren kommer att kicka hen från det här rummet och hindra hen från att gå med igen. Att avbanna användaren kommer att låta hen gå med i rummet igen. - Anledning - "%1$s, " %1$s och %2$s %1$s %2$s - LOKALA KONTAKTER (%d) ANVÄNDARKATALOG (%s) Endast Matrixanvändare - Sök %s skriver… %1$s och %2$s skriver… @@ -1194,18 +1021,15 @@ Om serveradministratören har sagt att detta är förväntat, försäkra att fingeravtrycket nedan matchar fingeravtrycket de angav. Certifikatet har ändrats från ett som var betrott av din mobil. Detta är HÖGST OVANLIGT. Du rekommenderas att INTE ACCEPTERA det nya certifikatet. Acceptera endast certifikatet om serveradministratören har publicerat ett fingeravtryck som matchar det ovan. - - 1 valt + %d valt %d valda INBJUDNA ANSLUTNA - Anledning för att rapportera det här innehållet Avbryt uppladdning Avbryt nedladdning - Sök Inga resultat MEDDELANDEN @@ -1219,40 +1043,31 @@ Gå med i rum Gå med i ett rum Skriv ett rums-ID eller ett rumsalias - Bläddra i katalogen Söker i katalog… - Favorit Nedprioritera Glöm - Lägg till en genväg på hemskärmen - + Lägg till på hemskärmen Meddelanden Användarvillkor Meddelanden från tredje part Upphovsrätt Integritetspolicy - Telefon Du kan inte göra detta från element på mobilen Autentisering krävs - - Diagnostik för felsökning Kör tester Kör… (%1$d av %2$d) Sätt på - Sätt på - Misslyckades att ladda anpassade regler, vänligen försök igen. Kolla Play-tjänster APK:n Google Play-tjänster är tillgänglig och uppdaterad. Element använder Google Play-tjänster för att skicka pushmeddelanden, men den verkar inte vara korrekt konfigurerad: \n%1$s Fixa Play-tjänster - Firebase-token FCM-token hämtades framgångsrikt: \n%1$s @@ -1265,30 +1080,23 @@ [%1$s] \nDet här felet är ur Elements kontroll. Det finns inget Google-konto på telefonen. Vänligen öppna kontohanteraren och lägg till ett Google-konto. Lägg till konto - Token-registrering FCM-token registrerades framgångsrikt på hemservern. Misslyckades att registrera FCM-token på hemservern: \n%1$s - Starta tjänst - Tjänsten dödades och startades om automatiskt. Tjänsten misslyckades att starta om - Starta vid boot Tjänsten kommer att startas när enheten startas om. Aktivera start på boot - Kolla bakgrundsrestriktioner Bakgrundsrestriktioner är inaktiverade för Element. Detta test bör köras med mobildata (inte Wi-Fi). \n%1$s Inaktivera restriktioner - Batterioptimering Element påverkas inte av batterioptimering. Ignorera optimering - Reducerad sekretess Den här appen behöver behörighet att köra i bakgrunden Apparna behöver inte ansluta till hemservern i bakgrunden, det bör reducera batterianvändning @@ -1326,7 +1134,6 @@ Exportera Vänligen skapa en lösenfras för att kryptera de exporterade nycklarna. Du kommer behöva skriva in samma lösenfras för att kunna importera nycklarna. Hantera nyckelsäkerhetskopiering - Importera krypteringsnycklar Importera rumsnycklar Importera @@ -1344,26 +1151,19 @@ \nSessionsnamn: %1$s \nSenast sedd: %2$s \nOm du inte loggade in i en annan session, ignorera denna begäran. - Markdown har aktiverats. Markdown har inaktiverats. - För att fortsätta använda hemservern %1$s så måste du läsa och godkänna användarvillkoren. Ingen Matrixsession tillgänglig - Börja använda nyckelsäkerhetskopiering Exportera nycklar manuellt - Säkra din säkerhetskopia med en lösenfras. Eller säkra din säkerhetskopia med en återställningsnyckel och spara den någonstans säkert. Dina nycklar säkerhetskopieras. Det ser ut som att du redan har konfigurerat säker säkerhetskopiering från en annan session. Vill du ersätta den med den du skapar\? Dina krypteringsnycklar säkerhetskopieras just nu till din hemserver i bakgrunden. Den initiala säkerhetskopieringen kan ta flera minuter. - - Förlorat din återställningsnyckel\? Du kan sätta upp en ny i inställningarna. Säkerhetskopieringen kunde inte avkrypteras med den här återställningsnyckeln: vänligen verifiera att du skrev in rätt återställningsnyckel. - Återställde en säkerhetskopia med %d nyckel. Återställde en säkerhetskopia med %d nycklar. @@ -1372,17 +1172,12 @@ %d ny nyckel har lagts till till den här sessionen. %d nya nycklar har lagts till till den här sessionen. - Sessionskrypto är inte aktiverats - - Återställ från säkerhetskopia Radera säkerhetskopia - Nyckelsäkerhetskopiering har blivit korrekt konfigurerad för den här sessionen. Nyckelsäkerhetskopiering är inte aktiverad på den här sessionen. Dina nycklar säkerhetskopieras inte från den här sessionen. - Säkerhetskopian har en signatur från en okänd session med ID %s. Säkerhetskopian har en giltig signatur från den här sessionen. Säkerhetskopian har en giltig signatur från den verifierade sessionen %s. @@ -1391,31 +1186,23 @@ Säkerhetskopian har en ogiltig signatur från den overifierade sessionen %s För att använda nyckelsäkerhetskopiering i den här sessionen, återställ med din lösenfras eller återställningsnyckel nu. Misslyckades att radera säkerhetskopia (%s) - Radera säkerhetskopia Ny nyckelsäkerhetskopia Börja använda nyckelsäkerhetskopiering - Säker säkerhetskopiering Använd nyckelsäkerhetskopiering - Hantera i nyckelsäkerhetskopieringen - Sätt upp säker säkerhetskopiering - Alla nycklar säkerhetskopierade Algoritm Att verifiera den här sessionen kommer att markera den som betrodd, och även markera din session som betrodd för den andra parten. - Verifiera den här sessionen genom att bekräfta att följande emojier visas på den andra partens skärm Verifiera den här sessionen genom att bekräfta att följande nummer visas på den andra partens skärm - Du har framgångsrikt verifierat den här sessionen. Den andra parten avbröt verifikationen. \n%s Interaktiv sessionsverifiering %s vill verifiera din session - Sessionen känner inte till den överföringen Sessionen kan inte komma överens om ett nyckelavtal, hash, MAC eller SAS-metod Visa borttagna meddelanden @@ -1424,13 +1211,10 @@ Pushregler Inga pushregler satta Visa dolda händelser i tidslinjen - Tillåt att svajpa för att svara i tidslinjen Lägg till en dedikerad flik för olästa aviseringar på startskärmen. - Aktivera mångordiga loggar. Vänligen försök igen när du har godkänt användarvillkoren för din hemserver. - %1$s, %2$s och %3$d annan har läst %1$s, %2$s och %3$d andra har läst @@ -1438,9 +1222,7 @@ Element behöver behörighet för att spara dina nycklar för totalsträckskryptering i lagringen. \n \nVänligen ge tillstånd i nästa popup för att kunna manuellt exportera dina nycklar. - Du ignorerar inga användare - Annan Om du har skapat ett konto på en hemserver så kan du använda ditt Matrix-ID (t.ex. @användare:domän.com) och lösenord nedan. Detta kan bero på flera orsaker: @@ -1455,7 +1237,6 @@ \nRensa den om du inte ska använda den här enheten längre, eller vill logga in på ett annat konto. Den nuvarande sessionen är för användaren %1$s och du försedde uppgifter för användaren %2$s. Detta stöds inte av Element. \nVänligen rensa först data, och logga sen in på ett annat konto. - Se alla mina sessioner Utvecklarläge Utvecklarläget aktiverar dolda funktioner och kan också göra appen mindre stabil. Endast för utvecklare! @@ -1464,7 +1245,6 @@ Skaka din mobil för att testa detektionsgränsen Nuvarande session Andra sessioner - Misslyckas snabbt En av följande kan vara äventyrad: \n @@ -1472,11 +1252,9 @@ \n - Hemservern användaren du verifierar är ansluten till \n - Din eller den andra användarens internetanslutning \n - Din eller den andra användarens enhet - Verifiera den här sessionen Skanna koden med den andra användarens enhet för att säkert verifiera varandra Tidslinje - Jämför koden med den som visas på den andra användarens skärm. Korssignering Korssignering är aktiverad @@ -1484,35 +1262,28 @@ Korssignering är aktiverad \nNycklar är betrodda. \nPrivata nycklar är inte kända - Korssignering är aktiverad + Korssignering är aktiverad. \nNycklar är inte betrodda Korssignering är inte aktiverat - Aktiva sessioner Visa alla sessioner Hantera sessioner Logga ut ur den här sessionen - Den här sessionen är betrodd för säker kommunikation för att du verifierade den: %d aktiv session %d aktiva sessioner - Andra användare kanske inte litar på den Misslyckades att hämta sessioner Sessioner Den här sessionen är betrodd för säker kommunikationen för säker kommunikation för att %1$s (%2$s) verifierade den: %1$s (%2$s) loggade in i en ny session: Tills den här användaren litar på den här sessionen så kommer meddelanden skickade till och från den att bli märkta med varningar. Alternativt så kan du manuellt verifiera den. - - Utvecklarverktyg Kontodata Om du inte kan komma åt en existerande session - Nyckelförfrågningar - En av följande kan vara äventyrad: \n \n- Ditt lösenord @@ -1521,14 +1292,10 @@ \n- Internetuppkopplingen en av enheterna använder \n \nVi rekommenderar att du ändrar ditt lösenord och återställningsnyckel i inställningarna omedelbart. - Sätter upp nyckelsäkerhetskopiering - - Spara den på ett USB-minne eller en säkerhetskopieringsenhet Nästan färdigt! Visar den andra enheten samma sköld\? Misslyckades att importera nycklar - Aktivera korssignering Kollar säkerhetskopieringsnyckel Kollar säkerhetskopieringsnyckel (%s) @@ -1536,29 +1303,23 @@ använda din återställningsnyckel för säkerhetskopiering Om du inte känner till din lösenfras för säkerhetskopia så kan du %s. Återställningsnyckel för säkerhetskopiering - Förhindra skärmdumpar av appen Att aktivera den här inställningen lägger till FLAG_SECURE till alla aktiviteter. Starta om appen för att inställningen ska få effekt. - Använd senaste Element på dina andra enheter; Element Web, Element iOS, Element för Android, eller annan Matrixklient som stöder korssignering eller en annan Matrixklient som städer korssignering Använd senaste Element på dina andra enheter: Tvingar den nuvarande utgående gruppsessionen i ett krypterat rum att kasseras Säkerhetskopian kunde inte avkrypteras med den här återställningsnyckeln: vänligen verifiera att du skrev in rätt återställningsnyckel. Sätt upp säker säkerhetskopiering - Säker säkerhetskopiering Sätt upp Skriv in en hemlig fras endast du känner till, och generera en nyckel för säkerhetskopiering. - Element kommer att synka i bakgrunden på ett sätt som sparar på enhetens begränsade resurser (batteri). \nBeroende på din enhets resurser, så kan synkroniseringen bli uppskjuten av operativsystemet. Optimerad för realtid Ingen bakgrundssynk Du kommer inte att bli aviserad om inkommande meddelanden när appen är i bakgrunden. Misslyckades att uppdatera inställningarna. - - Starta vid boot Aktivera bakgrundssynk Timeout för synkbegäran @@ -1566,7 +1327,6 @@ %s \nSynkroniseringen kan skjutas upp beroende på resurserna (batteri) eller status för enheten (vila). Fördröjning mellan varje synkronisering - Lokala kontakter Kontaktbehörighet Land för telefonbok @@ -1579,19 +1339,15 @@ Vibrera när en användare nämns Hantera Detta kommer att ersätta din nuvarande nyckel eller fras. - Ge behörighet Välj ett annat alternativ - Bakgrundsanslutning Ge behörighet - Statistik Skicka statistikdata Element samlar in anonym statistik för att låta oss förbättra appen. Vänligen aktivera statistik för att hjälpa oss att förbättra Element. Ja, jag vill hjälpa till! - Databesparingsläge ID Publikt namna @@ -1600,17 +1356,12 @@ %1$s @ %2$s Autentisering Skicka in - Integrationshanterare - Verifikation pågår Det här telefonnumret används redan. Lösenorden matchar inte - Är du säker på att du vill ta bort %1$s %2$s\? - Välj ett land - Land Vänligen välj ett land Telefonnummer @@ -1622,112 +1373,88 @@ Kod Ett fel inträffade vid verifiering av ditt telefonnummer. Ytterligare info: %s - Förvald komprimering Välj Välj Spela slutarljud - 3 dagar 1 vecka 1 månad För alltid - Rumsbild Rumsnamn Ämne Rumsetikett Märkt som: - Favorit Låg prioritet Ingen - Behörighet och synlighet Lista det här rummet i rumskatalogen Rumstillgång Synlighet för rumshistorik Vem kan läsa historiken\? Vem kan komma åt det här rummet\? - Vem som helst Endast medlemmar (från när det här alternativet valdes) Endast medlemmar (sen de bjöds in) Endast medlemmar (sen de gick med) - För att länka till ett rum så måste det ha en adress. Vem som helst som har rummets adress, förutom gäster Vem som helst som har rummets adress, inklusive gäster - Bannade användare %d bannad användare %d bannade användare - Rummets interna ID Adresser Du behöver logga ut för att kunna aktivera krypteringen. Det här rummet har inga lokala adresser Ny adress (t.ex. #foo:matrix.org) - Nytt gemenskaps-ID (t.ex. +foo:matrix.org) Ogiltigt gemenskaps-ID \'%s\' är inte ett giltigt gemenskaps-ID - - Ogiltigt aliasformat \'%s\' är inte ett giltigt format för ett alias Du kommer inte att ha någon huvudadress specificerad för det här rummet. Huvudadressvarningar - Sätt som huvudadress Avsätt som huvudadress Kopiera rums-ID Kopiera rumsadress - Kryptering är avaktiverat i det här rummet. Aktivera kryptering \n(varning: kan inte avaktiveras igen!) - Katalog Händelseinformation Användar-ID Curve25519-identitetsnyckel Gjorde anspråk på Ed25519-fingeravtrycksynyckel Avkrypteringsfel - Publikt namn Publikt namn Verifiering Ed25519-fingeravtryck - Nycklar framgångsrikt importerade - %1$d/%2$d nyckel importerad framgångsrikt. %1$d/%2$d nycklar importerade framgångsrikt. - Inte verifierad Verifierad Svartlistad - okänd IP ingen - Verifiera Avverifiera Svartlista Avsvartlista - Om de inte matchar så kan kommunikationens säkerhet vara äventyrad. Jag verifierar att nycklarna matchar - Servern kan vara otillgänglig eller överbelastad Hemserver-URL Skriv här… - %1$s i %2$s Ny händelse Rum @@ -1737,21 +1464,17 @@ ** Misslyckades att skicka - vänligen öppna rummet %1$s: %2$s %1$s: %2$s %3$s - Sök efter historiska - Du behöver behörighet för att hantera widgets i det här rummet Widgetskapande har misslyckats Skapa gruppsamtal med Jitsi Är du säker på att du vill radera den här widgeten från det här rummet\? - 1 aktiv widget + %d aktiv widget %d aktiva widgets VISA Aktiva widgets - - Widgets Ladda widget Den här widgeten lades till av: @@ -1762,13 +1485,10 @@ Ladda om widget Öppna i webbläsaren Återkalla tillgång för mig - Din avatar-URL Ditt användar-ID Widget-ID Rums-ID - - Tyvärr, gruppsamtal med Jitsi stöds inte på gamla enheter (enheter med Android äldre än 5.0) Den här widgeten vill använda följande resurser: Tillåt @@ -1789,7 +1509,6 @@ Inga aktiva widgets Skicka röstmeddelanden För att fortsätta behöver du acceptera villkoren för den här tjänsten. - Starta verifiering Verifiera Dela utan att verifiera @@ -1797,10 +1516,8 @@ Nyckeldelningsbegäran Ignorera begäran Ignorera - Varning! Gruppsamtal är under utveckling och kanske inte är pålitligt. - Kommandofel Okänt kommando: %s Visar handling @@ -1814,66 +1531,50 @@ Sparkar ut användaren med det angivna ID:t Ändrar ditt visningsnamn För att fixa Matrixapphantering - Skapa Skapa gemenskap Gemenskapsnamn Exempel Gemenskaps-ID exempel - Hem Inga användare - Gick med Bjöd in - 1 medlem + %d medlem %d medlemmar - Gemenskapsadministratören har inte försett en lång beskrivning för den här gemenskapen. - Du har blivit utsparkad från %1$s av %2$s Du har blivit bannad från %1$s av %2$s Orsak: %1$s Gå med igen Glöm rummet - Läsindikationsavatar Notisavatar Avatar - Granska nu - Vänligen skriv in ett användarnamn. Det här rummet har blivit ersatt och är inte längre tillgängligt Samtalet fortsätter här Det här rummet är en fortsättning av en annan konversation Tryck här för att se äldre meddelanden - Resursgränsen överskriden Hemservern har överskridit en av sina resursbegränsningar så vissa användare kommer inte att kunna logga in. Den här hemservern har överskridit en av sina resursbegränsningar. - Den här hemservern har nått sin månatliga aktiva användargräns så vissa användare kommer inte att kunna logga in. Den här hemservern har nått sin månatliga aktiva användargräns. - Vänligen %s för att öka den här gränsen. Vänligen %s för att fortsätta använda den här tjänsten. - Din hemserver stöder inte fördröjd inladdning av rumsmedlemmar än. Försök igen senare. - Tyvärr, ett fel inträffade - expandera kollapsa - Visa infoarean Alltid För meddelanden och fel Endast för fel - %1$s: %1$s: %2$s +%d @@ -1884,7 +1585,6 @@ Lösenfrasen matchar inte Vänligen skriv en lösenfras Lösenfrasen är för svag - Vänligen radera lösenfrasen om du vill att Element ska generera en återställningsnyckel. Sätt lösenfras Skapar säkerhetskopia @@ -1894,11 +1594,9 @@ Spara återställningnyckel Dela Återställningsnyckeln har sparats. - En säkerhetskopia finns redan på din hemserver Ersätt Stoppa - Vänligen gör en kopia Dela återställningsnyckel med… Genererar återställningsnyckel med hjälp av lösenfras, den här processen kan ta flera sekunder. @@ -1907,15 +1605,11 @@ Säkerhetskopiering startad Är du säker\? Du kan förlora åtkomst till dina meddelanden om du loggar ut ur eller förlorar den här enheten. - använd din återställningsnyckel Kan du inte din återställningslösenfras så kan du %s. - Skriv in återställningsnyckel - Säkerhetskopian kunde inte avkrypteras med den här lösenfrasen: vänligen verifiera att du skrev in rätt lösenfras. Nätverksfel: vänligen kolla din anslutning och försök igen. - Återställer säkerhetskopia: Räknar ut återställningsnyckel… Laddar ner nycklar… @@ -1924,26 +1618,19 @@ Vänligen skriv in en återställningsnyckel Säkerhetskopia återställd %s ! Misslyckades att få betroendeinfo för säkerhetskopian (%s). - Raderar säkerhetskopia… Kollar status för säkerhetskopian Det var jag - Säkerhetskopierar dina nycklar. Detta kan ta flera minuter… - - Säkerhetskopierar %d nyckel… Säkerhetskopierar %d nycklar… - Signatur - Autokomplettera serveralternativ Element detekterade en anpassad serverkonfiguration för din användar-ID-domän \"%1$s\": \n%2$s Använd konfig - Verifiera genom att jämföra en kort textsträng. För maximal säkerhet så rekommenderar vi att du gör det här personligen eller med en annan pålitlig form av kommunikation. Påbörja verifiering @@ -1951,18 +1638,14 @@ Du mottog en inkommande verifieringsbegäran. Visa begäran Väntar på att den andra parten ska bekräfta… - Verifierad! Jag förstår - Dyker inget upp\? Inte alla klienter stöder interaktiv verifiering än. Använd legacyverifiering. Använd legacyverifiering. - Nyckelverifiering Begäran avbruten Verifieringen är avbruten. \nAnledning: %s - Verifieringsbegäran Användaren avbröt verifieringen Verifieringsprocessen timeade ut @@ -1971,17 +1654,13 @@ Nyckelmismatch Användarmismatch Okänt fel - Det verkar som att du försöker ansluta till en annan hemserver. Vill du logga ut\? - Redigera Svara - Försök igen Gå med i ett rum för att börja använda appen. Skickade en inbjudan till dig Inbjuden av %s - Välkommen hit! Kom ikapp med olästa meddelanden här Konversationer @@ -1991,19 +1670,15 @@ Lägg till reaktion Visa reaktioner Reaktioner - Händelsen raderades av användaren Händelsen modererades av en rumsadministratör Senast redigerad av %1$s vid %2$s - - Felformaterad händelse, kan inte visa Inget nätverk. Vänligen kolla din internetanslutning. Ändra Vänligen vänta… Det här rummet kan inte förhandsgranskas Förhandsgranskning av världsläsbara rum stöds inte än av Element - Nytt rum SKAPA Rumsnamn @@ -2014,71 +1689,50 @@ Rummet har skapats, men vissa inbjudningar har inte skickats av följande anledning: \n \n%s - Ett fel inträffade vi hämtning av betroendeinfo Ett fel inträffade vid hämtning av nyckelsäkerhetskopia - Du tittar redan på det här rummet! - Snabbreaktioner - Expert Inga registrerade pushgateways - app_id: push_key: app_display_name: session_name: Url: Format: - Registrera token - Väntar… Krypterar miniatyrbild… Skickar miniatyrbild (%1$s / %2$s) (redigerad) - Inga redigeringar funna - Länk kopierad till klippbordet - Skapar rum… Börja skriva för att få resultat Går med i rummet… - Visa redigeringshistoria - Användarvillkor Granska villkor Läst vid - - Väntar - Verifikationskoden är felaktig. - Det verkar som att servern tar för lång tid att svara. Detta kan orsakas av antingen dålig uppkoppling eller ett fel på servern. Vänligen försök igen senare. - Skicka bilaga - Öppna navigationslådan Öppna rumskapandemenyn Stäng rumskapandemenyn… Stäng nyckelsäkerhetskopieringsbannern Hoppa till botten - %1$s, %2$s och %3$s läste %1$s och %2$s läste %s läste - 1 användare läste + %d användare läste %d användare läste - Ett fel inträffade vid hämtning av bilagan. Kunde inte hantera delningsdata - %1$s vid %2$s Det är skräppost Det är olämpligt @@ -2087,14 +1741,11 @@ Anledning till att rapportera detta innehåll RAPPORTERA IGNORERA ANVÄNDARE - Innehåll rapporterat Rapporterad som skräppost Rapporterad som olämplig Det finns ingen nätverksanslutning just nu - Ignorera användare - Inställningar Lägg till i favoriter Ta bort från favoriter @@ -2105,12 +1756,10 @@ Du gjorde rummet publikt för alla som har länken. Du ändrade rummet till endast inbjudna. Olästa meddelanden - Det är din konversation. Äg det. Håll konversationer privata med kryptering Utöka och anpassa din upplevelse Kom igång - Välj en server Premiumservervärd för organisationer Fortsätt @@ -2121,55 +1770,45 @@ Bli medlem Logga in Fortsätt med SSO - Element Matrix Services-adress Adress Premiumservervärd för organisationer Skriv in adressen för den Modular Element eller server du vill använda Skriv in adressen för en server eller Element du vill ansluta till Skriv in adressen för servern du vill använda - Ett fel inträffade vid laddning av sidan: %1$s (%2$d) - Appen kan inte logga in på den här hemservern. Hemservern stöder följande inloggninstyp(er): %1$s. + Appen kan inte logga in på den här hemservern. Hemservern stöder följande inloggningstyp(er): %1$s. \n \nVill du logga in med en webbklient\? Tyvärr, den här servern accepterar inte nya konton. Appen kan inte skapa ett konto på den här hemservern. \n \nVill du registrera dig med en webbklient\? - Nästa Varning! Fortsätt - Kolla din inkorg Tryck på länken för att bekräfta ditt nya lösenord. När du har följt länken det innehåller, klicka nedan. Framgång! Ditt lösenord har återställts. Tillbaka till inloggning - Varning Ditt lösenord har inte ändrats än. \n \nVill du avbryta återställningsprocessen\? - Nästa - Sätt telefonnummer Vänligen använd internationellt format. Telefonnummer Telefonnummer (valfritt) Nästa - Bekräfta telefonnummer Vi skickade just en kod till %1$s. Skriv in den nedan för att bekräfta att det är du. Skriv in kod Skicka igen Nästa - Internationella telefonnummer måste börja med \'+\' Telefonnumret verkar vara ogiltigt. Vänligen kolla det - Logga in på %1$s Användarnamn Lösenord @@ -2179,20 +1818,17 @@ Ditt konto är inte skapat än. \n \nVill du avbryta registreringsprocessen\? - Välj matrix.org Välj Element Matrix Services Välj en annan hemserver Vänligen utför captchautmaningen Acceptera villkoren för att fortsätta - Den inskrivna koden är felaktig. Vänligen kolla den. Utdaterad hemserver För många förfrågningar har skickats. Du kan försöka igen om %1$d sekund… För många förfrågningar har skickats. Du kan försöka igen om %1$d sekunder… - Alternativt, om du redan har ett konto och du kan ditt Matrix-ID och ditt lösenord, så kan du använda den här metoden: Logga in med ditt Matrix-ID Logga in med ditt Matrix-ID @@ -2200,10 +1836,8 @@ Om du inte kan ditt lösenord, gå tillbaka för att återställa det. Det här är inte ett giltigt användar-ID. Förväntat format: \'@användare:hemserver.org\' Sett av - Du är utloggad Logga in igen - Du är utloggad Logga in Administratören för din hemserver (%1$s) har loggat ut dig ur ditt konto %2$s (%3$s). @@ -2211,15 +1845,12 @@ Lösenord Rensa personlig information Rensa all data - Rensa data Du kommer att bli av med åtkomsten till dina krypterade meddelanden om du inte loggar in igen för att återfå dina krypteringsnycklar. Rensa data Din matrix.to-länk var felformaterad Beskrivningen är för kort - Inledande synk… - Skakning upptäckt! Inställningar Aktivera kryptering @@ -2229,7 +1860,6 @@ Verifiera den här användaren genom att bekräfta att följande unika emojier visas på hens skärm i samma ordning. För maximal säkerhet, använd en annan pålitlig kommunikationsform eller gör detta personligen. Kolla efter den gröna skölden för att försäkra dig om att en användare är betrodd. Lita på alla användare i ett rum för att försäkra dig om att rummet är säkert. - Inte säker Video. Bild. @@ -2241,20 +1871,14 @@ Verifikation skickad Verifieringsbegäran Verifiera manuellt - Du - Skanna deras kod Kan inte skanna Om ni inte träffas personligen, jämför emojier istället - Verifiera genom att jämföra emojier - Verifiera med emojier Om du inte kan skanna koden ovan, verifiera genom att jämföra ett kort, unikt urval av emojier. - QR-kodsbild - Verifiera %s Verifierade %s Väntar på %s… @@ -2264,61 +1888,43 @@ Säkerhet Adminhandlingar Lämnar rummet… - Anpassad Inbjudningar Användare - Administratör i %1$s Moderator i %1$s Standard i %1$s Anpassad (%1$d) i %2$s - Hoppa till läskvitto - Element hanterar inte händelser av typen \'%1$s\' Element stötte på ett fel vid rendering av innehållet i händelsen med id \'%1$s\' - Avignorera - Skickar den givna emoten i regnbågsfärg - Godkänn Avslå Lägg på - Aktivera kryptering\? Aktivera kryptering - För att vara säker, verifiera %s genom att kolla en engångskod. För att vara säker, gör detta personligen eller använd en annan säker kommunikationsform. - Jämför de unika emojierna, och försäkra dig om att de visas i samma ordning. Din serveradministratör har inaktiverat totalsträckskryptering som förval för privata rum och direktmeddelanden. Ingen kryptografisk information tillgänglig - Verifiera den här inloggningen Fullborda säkerhet - Verifiera Verifierad Varning - Betrodd Inte betrodd - Initierar korssignering Återställ nycklar - QR-kod - Nästan färdigt! Ser %s samma sköld\? Ja Nej - Anslutningen till servern har tappats Flygplansläge är på - %d röst %d röster @@ -2331,62 +1937,45 @@ Skapar en enkel omröstning Använd en återställningslösenfras eller -nyckel Ny inloggning - Skriv in en lösenfras för hemlig lagring Varning: Du bör endast komma åt hemlig lagring från en betrodd enhet - Ta bort… Vill du skicka den här bilagan till %1$s\? Skicka bild i originalstorlek Skicka bilder i originalstorlek - Bekräfta borttagning Är du säker på att du vill ta bort (radera) den här händelsen\? Observera att om du raderar ett rumsnamn eller ämnesändring så kan det upphäva ändringen. Inkludera en anledning Anledning för borttagning - Händelsen raderades av användaren, anledning: %1$s Händelsen modererades av rumsadministratören. Anledning: %1$s - Nycklarna är redan uppdaterade! - Element Android - Ladda om - Ny inloggning. Var det du\? Tryck för att granska och verifiera Det var inte jag Ditt konto kan vara äventyrat - Verifiera dina enheter i inställningarna. Verifikation avbruten - återställningslösenfras kontolösenord - Sätt en %s Bekräfta %s - Skriv in ditt %s för att fortsätta. - Skriv in din %s igen för att fortsätta. Använd inte ditt kontolösenord. - Skriv in en säkerhetsfras endast du känner till, vilken används för att säkra hemligheter på din server. - Detta kan ta flera sekunder, ha tålamod. Sätter upp återställning. Din återställningsnyckel Du är färdig! Håll den säker Slutför - Använd denna %1$s som ett skyddsnät ifall du glömmer din %2$s. - Publicerar skapade identitetsnycklar Genererar säkra nycklar baserat på lösenfras Definierar förvald nyckel för SSSS @@ -2395,36 +1984,27 @@ Synkroniseras självsigneringsnyckel Skriv ut den och spara den någonstans säkert Kopiera den till din personliga molnlagring - Du kan inte göra det där på mobilen - Krypteringen som används i det här rummet stöds inte - %s skapade och konfigurerade rummet. Du skapade och konfigurerade rummet. - Nästan färdigt! Väntar på bekräftelse… Väntar på %s… - Felaktigt användarnamn och/eller lösenord. Lösenordet du skrev börjar eller slutar med mellanslag, vänligen kolla det. Kontot har inaktiverats. - Krypteringsuppgradering tillgänglig Verifiera dig själv och andra för att hålla dina chattar säkra - Skriv in din %s för att fortsätta Skriv in %s Återställningslösenfras Det är inte en giltig återställningsnyckel Vänligen skriv in en återställningsnyckel - Hämtar kurvnyckel Generar SSSS-nyckel baserat på lösenfras Generar SSSS-nyckel baserat på lösenfras (%s) Generar SSSS-nyckel baserat på återställningsnyckel Lagrar hemlighet för nyckelsäkerhetskopiering i SSSS %1$s (%2$s) - Element Webb \nElement Skrivbord Element iOS @@ -2433,23 +2013,19 @@ Använd återställningsnyckel Välj din återställningsnyckel eller mata in den manuellt genom att skriva den eller klistra in från klippbordet Misslyckades att komma åt säker lagring - Okrypterad Krypterad av en overifierad enhet Verifiera den nya inloggningen på ditt konto: %1$s - Verifiera manuellt med text Verifiera inloggning Verifiera interaktivt med emojier Markera som betrodd - Vänligen välj ett användarnamn. Vänligen välj att lösenord. Dubbelkolla den här länken Länken %1$s tar dig till en annan sida: %2$s. \n \nÄr du säker på att du vill fortsätta\? - Lägg till medlemmar Bjuder in användare… Inbjudan skickad till %1$s @@ -2459,7 +2035,6 @@ För ditt privatlivs skull stöder Element bara att skicka hashade e-postadresser och telefonnummer. Associationen har misslyckats. Det finns ingen nuvarande association med den här identifieraren. - Använd %1$s Skicka in Sätt roll @@ -2472,44 +2047,32 @@ Använd en säkerhetsfras Spara din säkerhetsnyckel Spara din säkerhetsnyckel någonstans säkert, som en lösenordshanterare eller ett kassaskåp. - Sätt an säkerhetsfras Skriv in en säkerhetsfras endast du känner till, vilken används för att säkra hemligheter på din server. Säkerhetsfras Skriv in säkerhetsfrasen igen för att bekräfta den. - Spara din säkerhetsnyckel Spara din säkerhetsnyckel någonstans säkert, som en lösenordshanterare eller ett kassaskåp. - Rumsnamn Ämne Du ändrade rumsinställningarna framgångsrikt - Kan inte avkryptera Väntar på krypteringshistorik - Riot är nu Element! Vi är glada att meddela att vi har bytt namn! Din app är uppdaterad och du är inloggad på ditt konto. UPPFATTAT LÄR DIG MER - element - - Spara återställningsnyckel i - Lägg till från min telefonbok Din telefonbok är tom Telefonbok Hämtar dina kontakter… Kontaktbok - Återkalla inbjudan Återkalla inbjudan av %1$s\? - Bannad av %1$s Misslyckades att avbanna användaren - Pushnotiser är inaktiverade Granska dina inställningar för att aktivera pushnotiser @@ -2520,11 +2083,11 @@ För många fel, du har blivit utloggad Välj en PIN-kod för säkerhet Bekräfta PIN - Misslyckades med att validera PIN, vänligen skriv en ny. + Misslyckades med att validera PIN-kod, vänligen skriv en ny. Skriv din PIN-kod Glömt PIN\? - Återställ PIN - Ny PIN + Återställ PIN-kod + Ny PIN-kod För att återställa din PIN-kod behöver du logga in igen och skapa en ny. Aktivera PIN Om du vill återställa din PIN-kod, tryck på \"Glömt PIN\?\" för att logga ut och återställa. @@ -2536,28 +2099,80 @@ Telefonnummer Ta bort %s\? Se till att du har klickat på länken i e-brevet vi skickade till dig. - E-postadresser och telefonnummer Hantera e-postadresser och telefonnummer länkade till ditt Matrix-konto - Kod Vänligen använd internationellt format (telefonnumret måste börja med \'+\') Bekräfta din identitet genom att verifiera den här inloggningen, och ge den åtkomst till dina krypterade meddelanden. Tyvärr stöds den här handlingen inte än för konton anslutna med externt konto. - Kan inte öppna ett rum du är bannad från. Kan inte hitta det här rummet. Se till att det existerar. %d sekund %d sekunder - Visa statushändelser angående rumsmedlemmar Inkluderar när personer bjuds in, går med, lämnar, kickas, bannas eller byter sin avatar eller sitt namn. Omröstning Bottknappar Reagerade med: %s Verifieringsavslutning - Länken var felformaterad - + Du har inte behörighet att starta ett samtal i det här rummet + Radera kontodata av typen %1$s\? +\n +\nAnvänd varsamt, det kan leda till oväntat beteende. + så kommer du att börja om utan historik, meddelanden, betrodda enheter eller betrodda användare + PIN-kod krävs varje gång du öppnar Element. + PIN-kod krävs efter att du inte har använt Element på 2 minuter. + Kräv PIN-kod efter 2 minuter + Visa bara antal olästa meddelanden i en enkel avisering. + Visa detaljer som rumsnamn och meddelandeinnehåll. + Visa innehåll i aviseringar + PIN-kod är det enda sättet att låsa upp Element. + Aktivera enhetsspecifik biometri, som fingeravtryck och ansiktsigenkänning. + Aktivera biometri + Ställ in skydd + Skydda åtkomst med PIN-kod och biometri. + Skydda åtkomst + + Visa enheten du kan verifiera med nu + Visa %d enheter du kan verifiera med nu + + Om du återställer allt + Glömt eller förlorat alla återställningsalternativ\? Återställ allt + Gör bara detta om du inte har någon annan enhet att verifiera den här enheten med. + %s gick med. + Lämna + Återställ allt + Du gick med. + Meddelanden i det här rummet är totalsträckskrypterade. + Inställningar + Meddelanden här är totalsträckskrypterade. +\n +\nDina meddelanden är säkrade med lås och bara du och mottagaren har nycklarna för att låsa upp dem. + Meddelanden här är inte totalsträckskrypterade. + Den här hemservern kör en gammal version. Be din hemserveradministratör att uppgradera. Du kan fortsätta, men vissa funktioner kanske inte fungerar ordentligt. + Du ändrade detta till endast inbjudna. + %1$s ändrade detta till endast inbjudna. + Visa komplett historik i krypterade rum + %1$s och %2$s + %1$s i %2$s och %3$s + + %d inbjudan + %d inbjudningar + + Aviseringen har klickats! + Vänligen klicka på aviseringen. Om du inte ser aviseringen, vänligen kolla systeminställningarna. + Aviseringsvisning + Du kollar på aviseringen! Klicka på mig! + Misslyckades att ta emot push. Lösningen kan vara att installera om appen. + Appen tar emot PUSH:en + Appen väntar på PUSH:en + Testa push + Sökning i krypterade rum stöds inte än. + Filtrera bannade användare + Du har inte behörighet att starta ett samtal + Du har inte behörighet att starta ett gruppsamtal + Återställ + \ No newline at end of file diff --git a/vector/src/main/res/values-uk/strings.xml b/vector/src/main/res/values-uk/strings.xml index 7ed1791908..f2434c6af3 100644 --- a/vector/src/main/res/values-uk/strings.xml +++ b/vector/src/main/res/values-uk/strings.xml @@ -1110,4 +1110,28 @@ Будь ласка, ознайомтесь та прийміть правила цього серверу: Позначити як прочитане + Cyrl + + Запуск сервісу + Резервна копія ключа + Використати резервну копію ключа + Тут буде список Ваших прямих чатів + Повідомлення видалено + Прямі чати + + Прямі чати + + Відправити нове пряме повідомлення + Додає ¯\\_(ツ)_/¯ перед текстовим повідомленням + + Element не підтримує повідомлення типу \'%1$s\' + Відправляє повідомлення розмальоване веселково + Редактор повідомлень + + Відправляє повідомлення як текст без інтерпретації його як Markdown + + Повідомлення… + + Ви не маєте доступу до цього повідомлення + Очікуємо повідомлення, це може тривати певний час diff --git a/vector/src/main/res/values-zh-rCN/strings.xml b/vector/src/main/res/values-zh-rCN/strings.xml index 3cbcbb0875..17bf14161c 100644 --- a/vector/src/main/res/values-zh-rCN/strings.xml +++ b/vector/src/main/res/values-zh-rCN/strings.xml @@ -1,14 +1,12 @@ - + zh CN - 接受 拒绝 挂断 - 重新发送 引用 分享 @@ -26,31 +24,23 @@ 登录 登出 搜索 - 发起新的聊天 发送文件 - 昨天 今天 - 聊天室名称 聊天室主题 - 通话结束 拍摄照片或视频 无法录制视频 - 已保存 继续 - 加入 查看 拒绝 - %s 邀请您加入这个聊天室 新的聊天 添加成员 - 退出聊天室 您确定要退出聊天室吗? 您确定要把 %s 从这个聊天中移除吗? @@ -62,23 +52,18 @@ 聊天室 设置 成员信息 - 保存 离开 发送 查看源代码 查看解密后的源代码 - 登出 禁用 - 警告 - 请描述您遇到的问题。您做了什么?您期望发生什么?实际上发生了什么? 在这里描述您的问题 进度(%s%%) - 主服务器 URL 身份服务器 URL 登入 @@ -120,7 +105,6 @@ 无法注册:网络错误 无法注册 请输入有效的 URL - 无效的的用户名/密码 没有包含有效的 JSON 发送了过多的请求 @@ -128,7 +112,6 @@ - 取消下载? 取消上传? @@ -136,21 +119,17 @@ 一个聊天室 1 位成员 创建 - 文件 - 搜索 过滤聊天室成员 没有结果 聊天室 消息 文件 - 加入 聊天室 开始聊天 创建聊天室 - 私聊 消息 设置 @@ -168,7 +147,6 @@ 设备名称 密码: 提交 - 修改密码 新密码 确认新密码 @@ -179,14 +157,12 @@ 算法 会话 ID 解密错误 - 发送者的会话信息 公开名称 会话 ID 会话密钥 验证 Ed25519 指纹 - 导入 已验证 取消 @@ -201,12 +177,9 @@ 确认 发送崩溃日志 上次启动后本应用发生了崩溃。是否打开崩溃报告页面? - 开始语音通话 开始视频通话 - 拍摄照片或视频 - 重复密码 使用自定义服务器选项(高级) 请检查您的电子邮箱以继续注册 @@ -214,7 +187,6 @@ 一封电子邮件已发送至 %s。点击了其中的链接后,请点击下面。 电子邮箱地址验证失败:请确保您已点击邮件中的链接 密码已重置。 您已从所有设备注销并且不再接受推送通知。要重新启用通知,请重新在相应设备上登录。 - 原始 %d 秒 通话已连接 @@ -228,7 +200,6 @@ 管理工具 私聊 设备列表 - 邀请 退出这个聊天室 从这个聊天室移除 @@ -243,9 +214,7 @@ 提及 显示设备列表 您确定要邀请 %s 到这个聊天吗? - 本地联系人(%d 个) - %1$s 和 %2$s 正在输入… %1$s 和 %2$s 及其他人正在输入… 发送一条加密消息… @@ -259,7 +228,6 @@ 删除未发送的消息 文件未找到 您没有发送到这个聊天室的权限 - 信任 不信任 注销 @@ -270,36 +238,29 @@ 设置 已邀请 已加入 - 您要隐藏所有来自这个用户的消息吗? 注意,此操作会重启应用并将花费一些时间。 取消上传 取消下载 - 收藏夹 低优先级 降低优先级 退出对话 - 版权 隐私政策 - 手机号码 应用信息 - 启用这个账户的通知 启用这个设备的通知 来自私聊的消息 来自群聊的消息 当我被邀请加入聊天室时 来自机器人的消息 - 后台同步 启用后台同步 同步请求超时 每次同步请求之间的间隔 - 版权 隐私政策 清空缓存 @@ -314,9 +275,7 @@ 是否重新显示所有来自 %s 的消息? 注意,此操作会重启应用并将花费一些时间。 - 选择国家 - 国家 请选择一个国家 手机验证 @@ -329,18 +288,14 @@ 聊天室历史可见性 谁可以阅读历史消息? 谁可以访问这个聊天室? - 任何人 仅本群成员(自选中此项后开始) 只有成员(从他们被邀请开始) 只有成员(从他们加入开始) - 只有被邀请的人 除游客之外任何知道这个聊天室链接的人 任何知道这个聊天室链接的人,包括游客 - 被封禁的用户 - 高级 此聊天室的内部 ID 地址 @@ -348,10 +303,8 @@ 端对端加密 您需要注销来启用加密。 在此聊天室中、使用本设备时,从不向未验证的设备发送加密消息。 - 这个聊天室没有本地地址 新地址(如 #foo:matrix.org) - 别名格式无效 “%s” 不是有效的别名格式 这个聊天室已启用加密。 @@ -359,9 +312,7 @@ 启用加密 (警告:无法再禁用!) 目录 - 端对端加密信息 - 事件信息 用户 ID 导出端到端聊天室密钥 @@ -373,20 +324,17 @@ 端到端聊天室密钥已经被保存到“%s”。 \n \n注意:如果应用被卸载,此文件可能将会被移除。 - 导入端到端聊天室密钥 导入聊天室密钥 从本地文件导入密钥 仅向已验证的设备发送加密消息 使用本设备时,从不向未验证的设备发送加密消息。 - 未验证 未知会话 验证 取消验证 验证设备 我验证此密钥匹配 - 聊天室包含未知设备 稍后再说 永久链接 @@ -404,7 +352,6 @@ 问题反馈发送成功 问题反馈发送失败(%s) 阅读 - 无效令牌 在出现相应的 API 前,暂不支持同时使用电子邮箱地址与手机号码注册。只有手机号码会被添加到此帐户。 @@ -414,20 +361,16 @@ 无法识别指定的访问令牌 异常的 JSON 您还未点击电子邮件链接 - %1$d 分钟 %2$d 秒 - 正在呼叫… 呼入的通话 呼入的视频通话 呼入的语音通话 通话中… - 通话未被接听。 媒体连接失败 无法初始化相机 其他设备已接听 - 信息 " \n @@ -439,16 +382,13 @@ 请在接下来弹出的窗口中授权允许访问。 对不起。因为权限不足,操作已取消 - 保存至下载? 移除 此邀请已发送至未与此账户关联的 %s。 您可能希望用一个不同的账户登录,或者把这个电子邮箱加入到这个账户。 这是此聊天室的预览。与聊天室的交互已禁用。 - 通话 您将不能撤销这个修改,因为您正在让这个用户和您拥有相同的特权级别。 您确定吗? - 这可能意味着有人在恶意劫持您的通讯,或者您的手机不信任远程服务器的数字证书。 如果服务器管理员说这是正常的,请确保以下的指纹与管理员提供的指纹相符。 报告这个内容的原因 @@ -456,13 +396,11 @@ 邀请 浏览目录 正在搜索目录… - 收藏夹 使用条款 昵称 显示系统设置中的应用程序信息。 点亮屏幕 3 秒 - 通话请求 使用条款 其他 @@ -470,42 +408,31 @@ 登录为 请检查您的电子邮箱并点击里面包含的链接。完成时请点击继续。 无法验证电子邮箱地址。请检查您的邮件并点击其中的链接。完成后,请点击“继续”。 - 此电子邮箱地址已被使用。 找不到此电子邮箱地址。 此手机号码已被使用。 - 您确认要移除此通知目标吗? - 您确认要移除 %1$s %2$s 吗? - 无效的已选国家的手机号码 聊天室图片 聊天室标签 标签为: - 收藏夹 低优先级 - 要链接一个聊天室,必须有一个地址。 仅向已验证的设备发送加密消息 您没有指定此聊天室的主地址。 主地址警告 - 设置为主地址 取消设置为主地址 复制聊天室 ID 复制聊天室地址 - 声称的 Ed25519 指纹密钥 已列入黑名单 - - 列入黑名单 移出黑名单 - 确认 移除 您似乎沮丧地摇了摇手机。您想打开问题反馈界面吗? @@ -518,9 +445,7 @@ Element 需要访问您的通讯录,才能根据电子邮箱地址和手机号码查找其他 Matrix 用户。 允许 Element 访问您的通讯录? - 空闲 - 仅 Matrix 用户 您的手机信任的证书已被更改。这非常反常。建议您不要接受此新证书。 证书已从以前受信任的更改为不受信任的证书。服务器可能已更新其证书。请联系管理员并核对服务器的指纹。 @@ -555,7 +480,6 @@ \n我们建议您在继续操作之前,先验证每个设备,但如果您愿意也可以不验证而重新发送消息。 \n \n未知设备: - 历史消息 邀请 全局搜索 @@ -563,16 +487,13 @@ 收藏夹 联系人 聊天室 - 按聊天室名称过滤 按收藏夹过滤 按联系人过滤 按聊天室名称过滤 - 邀请 低优先级 - 对话 本地通讯录 @@ -580,18 +501,15 @@ 没有对话 没有结果 您没有授予 Element 访问本地通讯录的权限 - 聊天室 聊天室目录 没有聊天室 没有公开聊天室 - 用 ID 邀请用户 请输入一个或多个电子邮箱地址或 Matrix ID 电子邮箱地址或 Matrix ID 忘记 - 选择一个聊天室目录 服务器可能不可用或过载 @@ -599,44 +517,33 @@ 主服务器 URL %s 服务器上的所有聊天室 所有本地 %s 聊天室 - 搜索历史 - 历史消息 - 用 ID 邀请 进入聊天室 进入一个聊天室 输入聊天室 ID 或者聊天室别名 - 跳到第一条未读消息。 - 主页显示 置顶含有错过的通知的聊天室 置顶含有未读消息的聊天室 用户界面 语言 请选择语言 - 离线 - 用户目录 用户目录(%s) 开机时自动启动 清空缓存的媒体文件 保留媒体文件 - 为所有消息显示时间戳 省流量模式 - 3天 1周 1个月 永远 - 主题 - 文字大小 微小 @@ -648,15 +555,12 @@ 浅色主题 深色主题 黑色主题 - 通知声音 使用12小时制显示时间戳 - 您需要权限来管理这个聊天室的小部件 创建小部件失败 用 jitsi 创建会议通话 您确定要删除这个小部件吗? - 无法创建小部件。 发送请求失败。 @@ -669,7 +573,6 @@ 添加 Matrix 应用 正在同步… 监听事件更新 - 通话 包含我昵称的消息 包含我用户名的消息 @@ -678,76 +581,53 @@ 开始验证 不验证而分享 忽略请求 - 反馈问题 - 拍摄照片 拍摄视频 - 使用原生相机 - 警告! 会议通话正在开发中,可能不可靠。 - 命令错误 不可识别的命令:%s - 关闭 已加密消息 - 响铃通知 静默通知 - 数据收集 - 响铃 - 正在加载… - 退出 邀请 您确定要与 %s 发起新的聊天吗? 您确定要发起语音通话吗? 您确定要发起视频通话吗? - 封禁踢掉将从聊天室中踢掉他们并阻止他们再次加入。 - 全部消息 只限提及 静音 - 添加主屏幕快捷方式 - + 添加到主屏幕 启用链接预览 使用 @ 时震动 - 通知 创建 样例名称 - %d 条新消息 - 重新加入 忘记聊天室 所有消息(响铃) 社区信息 - 操作 社区 - 按社区名称过滤 - 社区 没有群组 - 摇一摇快捷反馈问题 - 群组列表 - 正在同步… %d 人在线 @@ -755,14 +635,11 @@ %d 位成员的状态发生了变化 - %d 位成员 无效的社区 ID “%s” 不是一个有效的社区 ID - - %d 条未读消息 @@ -775,14 +652,12 @@ 通知隐私 本应用需要在后台运行的权限 徽章 - %d 个聊天室 已启用 %d 个小部件 - 社区名称 社区 ID @@ -791,25 +666,20 @@ 聊天室 聊天室 没有用户 - 已加入 已邀请 过滤社区成员 过滤社区聊天室 - 您已被 %2$s 从 %1$s 中移除 您已被 %2$s 从 %1$s 中封禁 理由:%1$s 头像 - 已读提示头像 通知头像 - 有 %1$s 个聊天室符合 “%2$s” %1$s 条在 %2$s 中 - %d 条未读消息 @@ -817,24 +687,19 @@ Element 可以在后台运行以安全隐密地管理您的通知(这可能会影响电池消耗)。 获取权限 选择其他选项 - • 通知通过 Firebase Cloud Messaging 发送 • 通知只含有元数据 • 通知不会显示消息内容 - 新的社区ID(如 +foo:matrix.org) 社区管理员没有提供这个社区的具体描述。 - 标准 低隐私模式 停用账户 停用我的账户 - 发送统计分析数据 Element 会收集匿名统计数据来帮助我们改进程序。 请允许资料分析以帮助我们改进 Element。 是的,我愿意帮助! - 停用账户 这将使您的账户永远不再可用。您将不能登录,或使用相同的用户 ID 重新注册。您的账户将退出所有已加入的聊天室,身份服务器上的账户信息也会被删除。此操作是不可逆的。 @@ -844,52 +709,37 @@ Matrix 中的消息可见性类似于电子邮件。我们忘记您的消息意 请在我停用账户的同时忘记我发送的所有消息(警告:这将导致未来的用户看到残缺的对话) 请输入您的密码以继续: 停用账户 - 发送贴纸 - 发送贴纸 您目前没有启用任何贴纸包。 \n \n要添加一些吗? - • 通知中的消息内容从 Matrix 主服务器直接安全的获取 • 通知含有消息与元数据 这个聊天室不会显示任何社区的徽章 缺少所需的参数。 无效参数。 样例 ID - 要想继续使用主服务器 %1$s 您必须阅读并同意其服务条款。 现在阅读 - 第三方开源协议 - 下载 清除 发送语音消息 - 使用…打开 对不起,没有可完成此操作的外部应用。 - 从其他设备上 重新请求密钥 - 已发送密钥共享请求。 - 已请求 请在其他可解密此消息的设备上启动 Element,以便其将密钥发送至当前设备。 - 在此输入… - 发送语音消息 - 请输入您的密码。 - 发言 如果可能的话,请使用英文撰写问题描述。 发送加密的回复… 发送回复(未加密)… 发送前预览媒体文件 - 使用回车键发送消息 显示动作 依照 ID 封禁用户 @@ -904,19 +754,15 @@ Matrix 中的消息可见性类似于电子邮件。我们忘记您的消息意 更改您显示的昵称 打开/关闭 markdown 修复 Matrix Apps 管理 - 这个聊天室已经被替换并且不再活跃 对话在此继续 这个聊天室是另一个对话的延续 点击此处查看更早的消息 - 您目前不是任何社区的成员。 - 由于缺少权限,此操作无法完成。 %d 个用户 - %d 秒 @@ -929,58 +775,41 @@ Matrix 中的消息可见性类似于电子邮件。我们忘记您的消息意 %d 天 - 现在%1$s %2$s前%1$s - "%1$s、 " %1$s 和 %2$s %1$s%2$s - 已选择 %d 个 %d 个成员 - %d 个聊天室 系统警告 - 超出资源限制 联系管理员 - 联系您的服务管理员 - 本服务器其中一项资源已超出限制,部分用户将无法登录 本服务器其中一项资源已超出限制。 - 本服务器已达到每月活跃用户限制,部分用户将无法登录 本服务器已达到每月活跃用户限制。 - 请 %s 以继续使用本服务。 - 请 %s 以增加此限制的额度。 - Status.im 主题 - 仍然呼叫 接受 - 错误 - 请审阅并接受此主服务器的政策: - 通话 为来电使用 Element 的默认铃声 来电铃声 请选择来电铃声: - 移除 理由 - 版本 %s 通知故障排除 故障排除诊断 @@ -989,54 +818,44 @@ Matrix 中的消息可见性类似于电子邮件。我们忘记您的消息意 基本诊断结果正常。若您还是沒有收到通知,请提交错误报告以协助我们调查此问题。 一个或多个测试没有通过,请尝试建议的修复方法。 一个或多个测试没有通过,请提交错误反馈以协助我们调查此问题。 - 系统设置。 通知已在系统设置中启用。 通知已在系统设置中禁用。 请检查系统设置。 打开设置 - 帐号设置。 您的帐号已启用通知。 您的帐号已禁用通知。 请检查帐号设置。 启用 - 设备设置。 已为此设备启用通知。 已为此设备禁用通知。 请检查 Element 设置。 启用 - Play 服务检查 Google Play 服务的 APK 文件可用且为最新版本。 Element 使用 Google Play 服务来推送通知,但它似乎并未正确设置: \n%1$s 修复 Play 服务 - Firebase 令牌 成功获取 FCM Token: %1$s FCM Token 获取失败: %1$s - 注册 Token FCM Token 已成功注册至主服务器。 将 FCM Token 注册至主服务器时失败: %1$s - 通知服务 通知服务正在运行。 通知服务尚未运行。 请尝试重启本应用程序。 启动服务 - 调用系统相机应用而非使用 Element 内置的相机界面。 此选项需要第三方应用录制消息。 - 开机时启动 启用开机时启动 - 检查后台限制 电池优化 若主服务器支持本功能,在聊天时预览链接内容。 @@ -1054,13 +873,10 @@ Matrix 中的消息可见性类似于电子邮件。我们忘记您的消息意 Element需要保持一个低影响的后台连接才能保证可靠的通知。 在下一个弹出窗口中,系统将提示您允许 Element 始终在后台运行,请点击“允许“。 授予权限 - 在验证您的电子邮件地址时发生了一个错误。 - 密码 在验证您的手机号码时发生了一个错误。 额外信息:%s - %d+ +%d %1$s:%2$s @@ -1068,26 +884,19 @@ Matrix 中的消息可见性类似于电子邮件。我们忘记您的消息意 总是 消息与错误 仅错误 - 显示信息区域 收起 - 展开 抱歉,发生了一个错误 - 您的主服务器尚未支持延迟加载聊天室成员,请稍候再试。 - 通过仅载入最近聊天中出现的聊天室成员来提升性能。 延迟加载聊天室成员 已停用 Markdown。 - 已启用 Markdown。 视频通话中… - 自动重启通知服务 服务被停止,并已自动重启。 服务重启失败 - 服务将在设备重启后启动。 服务不会在设备重启后启动,在您打开 Element 一次之前您将不会收到消息通知。 对 Element 的后台限制已被关闭。此测试应在移动数据(无Wi-Fi)环境下进行。 @@ -1096,20 +905,16 @@ Matrix 中的消息可见性类似于电子邮件。我们忘记您的消息意 Element 在后台时的工作将被显著的限制,这可能会影响消息通知。 %1$s 关闭后台限制 - Element 未被电池优化影响。 如果设备在未充电的情况下关屏静置一段时间,其将进入打盹模式(Doze)。这将阻止应用访问网络并延后其运行、同步、与响铃。 忽略电池优化 - 请输入用于加密密钥备份的密码。您在恢复此备份时需要使用此密码。 创建密码 密码必须对应 指令 %s 需要更多参数,或者有些参数不正确。 没有可用的 Google Play Services APK。消息通知可能不能正常工作。 - 密钥备份 使用备份密钥 - 密钥备份尚未完成,请等待… 如果您此时登出账号,您将会失去您的已加密信息 密钥备份进行中。如果您此时登出账号将无法再访问您的已加密信息。 @@ -1120,22 +925,18 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 确定吗? 备份 如果您在登出账号之前不备份密钥,您将失去您的已加密信息的访问权。 - 留下 跳过 完成 中止 - 您确定要登出账号吗? 高级通知设置 事件的通知重要程度 - 自定义设置。 请注意一些消息类型已设置为静音(将会生成一条没有铃声的通知)。 有些通知已在您的自定义设置中被禁用。 自定义规则加载失败,请重试。 检查设置 - [%1$s] \n此错误不受 Element 控制,根据 Google 的说法,此错误表示该设备在 FCM 中注册了太多应用。该错误仅在应用程序数量极多的情况下发生,因此不应影响普通用户。 [%1$s] @@ -1143,27 +944,20 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 [%1$s] \n此错误不受 Element 控制。此设备上没有登录 Google 账号。请打开账号管理器并添加一个 Google 账号。 添加账号 - 设置响铃通知 设置电话通知 设置静音通知 选择指示灯颜色,震动,铃声… - - 加密密钥管理 省流量模式使用了特定的过滤器,所以状态更新和输入状态通知将会被过滤掉。 - 已加密信息恢复 管理密钥备份 - 静音 请输入一个用户名。 请输入密码 密码太弱 - 如果您想要 Element 生成一个恢复密钥,请删除密码。 没有可用的 Matrix 会话 - 已加密信息永不丢失 加密聊天室中的信息会被端对端加密以确保安全。只有您和拥有密钥的接收方可以读取这些信息。 \n @@ -1171,7 +965,6 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 开始使用备份密钥 (高级) 手动导出密钥 - 使用密码以保护您的备份。 我们将会在主服务器上保存一份您的密钥的加密拷贝。设置一个密码来保护您的备份的安全。 \n @@ -1193,7 +986,6 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 恢复密钥已被保存到 \'%s\' 。 \n \n警告:如果应用被卸载,此文件可能会被删除。 - 请制作一份拷贝 分享恢复密钥 … 用密码来生成恢复密钥,此过程可能会花费几秒钟。 @@ -1201,25 +993,18 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 意外错误 备份开始 您的加密密钥正在后台被备份到您的主服务器上。初始备份可能花费几分钟。 - - 您确定吗? 如果您登出账号或者丢失此设备,您可能再也无法访问您的信息。 - 正在获取备份的版本 … 使用恢复密码解锁您的已加密历史消息 使用您的恢复密钥 不知道您的恢复密码,您可以 %s 。 - 使用恢复密钥解锁您的已加密历史消息 输入恢复密钥 - 信息恢复 - 丢失了恢复密钥?您可以在设置中新建一个。 无法使用此密码解密备份:请检查您输入的恢复密码是否正确。 网络错误:请检查您的网络连接并重试。 - 备份恢复中: 恢复密钥计算中 … 密钥下载中 … @@ -1227,7 +1012,6 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 解锁历史 请输入恢复密钥 无法使用此恢复密钥解密备份:请检查您输入的恢复密钥是否正确。 - 备份已恢复 %s ! 恢复了一个包含 %d 个密钥的备份。 @@ -1235,18 +1019,13 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 %d 个新密钥已被添加到此设备中。 - 无法获取最新的恢复密钥版本 (%s) 。 会话加密没有被启用 - - 从备份恢复 删除备份 - 已为此设备正确设置密钥备份。 密钥备份在此设备上未激活。 您的密钥未从此设备备份。 - 备份含有一个来自 ID 为 %s 的未知设备的签名。 备份具有此设备的有效签名。 备份具有已验证设备 %s 的有效签名。 @@ -1254,14 +1033,11 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 备份具有已验证设备 %s 的无效签名 备份具有未验证设备 %s 的无效签名 无法获得备份 (%s)的信任信息。 - 要在此设备上使用密钥备份,请立即使用密码或恢复密钥进行恢复。 备份删除中 … 备份(%s)删除失败 - 删除备份 要从此服务器中删除您备份的加密密钥吗?您将无法再使用恢复密钥来读取加密的历史消息。 - 备份新密钥 已检测到新的安全消息密钥备份。 \n @@ -1269,42 +1045,32 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 那是我 永不丢失已加密信息 开始使用备份密钥 - 永不丢失已加密信息 使用备份密钥 - 新加密信息密钥 管理密钥备份 - 密钥备份中 … - 所有密钥都已备份 %d 个密钥备份中 … - 版本 算法 签名 - 忽略 - 以单点登录方式登入 无法连接到此 URL,请检查 您的设备使用了过时的 TLS 安全协议,容易受到攻击,为保证安全,您将无法进行连接 按回车发送消息 软键盘的 Enter 按钮将发送消息而不是添加换行符 - 密码更新 密码无效 密码不匹配 - 无效的主服务器探测响应 自动完成服务器选项 Element 侦测到您的 userId 域名 \"%1$s\" 有自定义的服务器设置: \n%2$s 使用设置 - 正在初始化服务 媒体 默认压缩 @@ -1312,7 +1078,6 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 默认媒体来源 选择 播放快门声 - 标记为已读 本应用 需要在后台连接主服务器,应能减少电量消耗 @@ -1321,18 +1086,14 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 %d 条通知 - 新活动 聊天室 新消息 新邀请 ** 发送失败 - 请打开聊天室 - 抱歉,旧设备(Android 系统版本低于 5.0)不支持使用 Jitsi 创建电话会议 - 验证会话 - 未知 IP 一个新设备正在请求加密密钥。 \n设备名称:%1$s @@ -1342,15 +1103,12 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 \n设备名称:%1$s \n最近上线于:%2$s \n若您未曾在另一个设备上登录,请忽略此请求。 - 验证 分享 密钥分享请求 忽略 - 替换 终止 - 正在检查备份状态 通过对比一段简短的文本字符串来验证设备。 为保证尽可能高的安全性,我们建议您与对方当面交换,或使用另一种可信任的通讯方式。 @@ -1358,30 +1116,22 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 传入的验证请求 查看请求 正在等待对方确认… - 已验证! 您已成功验证此设备。 了解了 - 什么都没有出现?并非所有的客户端都已支持交互式验证。使用旧版验证吧。 使用旧版验证。 - 密钥验证 请求已取消 交互式设备验证 验证请求 %s 想验证您的装置 - 未知错误 - - 编辑 回复 - 重试 向您发送邀请 由 %s 邀请 - 欢迎回家! 对话 聊天室 @@ -1391,13 +1141,11 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 响应 查看响应 响应 - 由用户删除的事件 创建新聊天室 修改 请稍候… 所有社区 - 无法预览此聊天室 聊天室 创建 @@ -1405,7 +1153,6 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 聊天室目录 Matrix SDK 版本 快捷响应 - 通用 选项 安全与隐私 @@ -1413,37 +1160,28 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 推送规则 尚未定义任何推送规则 没有已注册的推送通道 - app_id: push_key: app_display_name: device_name: URL: 格式: - 音频与视频 帮助和关于 - - (已编辑) - 撤消 断开连接 检查 拒绝 - 没有设置身份服务器。 - 服务器的错误配置导致通话失败 Hans - 请要求您的家庭服务器 (%1$s) 的管理员配置 TURN 服务器,以使通话可靠地工作。 \n \n或者,您可以尝试使用 %2$s 的公共服务器,但这将不那么可靠,并且它将与该服务器共享您的 IP 地址。您也可以在“设置”中进行管理。 尝试使用 %s 不要再问我 - 设置用于帐户恢复的电子邮件,然后就可以让认识您的人选择性探索到您。 设定电话,然后就可以让认识您的人选择性探索到您。 设定电子邮件以供帐号复原。 然后就可以让认识您的人用电子邮件或电话选择性探索到您。 @@ -1454,16 +1192,12 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 播放 暂停 忽略 - - 复制 成功 - 通知 Element 呼叫失败 无法建立实时连接。 \n请要求您的家庭服务器管理员配置 TURN 服务器以使通话可靠工作。 - 选择声音设备 电话 扬声器 @@ -1474,20 +1208,16 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 关闭 HD 打开 HD - SSL 错误:尚未验证对等端身份。 SSL 错误。 当您的家庭服务器未提供时将使用 %s 作为辅助(在通话时将分享您的 IP 地址) 活动通话 (%s) 返回通话 - 在您的设置中添加身份服务器以执行此操作。 取消邀请 降低您自己的级别? 您家无法撤销此操作因为您正在降低您的级别,如果您是聊天室中最后一个特权用户将无法恢复特权。 降级 - - 忽略用户 忽略此用户将从您共享的聊天室移除他们的消息。 \n @@ -1505,13 +1235,10 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 封禁理由 取消封禁用户 取消封禁用户将允许他们再次加入聊天室。 - 确认您的密码 您无法在 Element 移动版中这么做 需要身份认证 - - - 后台同步模式(实验性) + 后台同步模式 电池优化 Element 将在后台以保留设备有限资源(电池)的方式同步。 \n取决于您的设备资源状态,同步可能被操作系统推迟。 @@ -1521,8 +1248,6 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 无后台同步 应用在后台时您不会收到消息通知。 更新设置失败。 - - 偏好同步间隔 %s \n取决于资源(电量)或设备状态(睡眠)同步可能会延迟。 @@ -1537,30 +1262,22 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 通过在您的服务器上备份加密密钥保障加密消息和数据的访问权。 为您已有的备份生成新的安全密钥或设置新的安全口令。 这将替换您的当前密钥或短语。 - 发现 管理您的发现设置。 允许集成 集成管理器 - 集成已禁用 请在设置中启用“允许集成”。 - %d 个封禁用户 - 公开名称(对通信参与者可见) 会话的公开名称对通信的参与者可见 成功导出密钥 - %1$s: %2$s %1$s: %2$s %3$s - 查看 活动小部件 - - 小部件 载入小部件 此小部件添加者: @@ -1571,49 +1288,37 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 重载小部件 在浏览器中打开 撤消我的访问权限 - 您的昵称 您的头像 URL 您的用户 ID 您的主题 小部件 ID 聊天室 ID - - 小部件想使用以下资源: 允许 阻止全部 使用相机 使用麦克风 读取受 DRM 保护的媒体 - 未配置集成管理器。 若要继续请接受服务条款。 - 恢复密钥已保存。 - 您的家庭服务器上已存在备份 您似乎已在另一个会话中设置密钥备份。您想要将其替换为正在创建的吗? 安全备份 保护加密信息及数据的访问权 - 设置安全备份 - 由于无效或过期的凭据您已登出。 - 验证会话已将其标记为可信。当使用端到端加密消息时信任参与者的会话将给您额外的内心平静。 验证会话将标记其为可信,同时将您的会话对对方标记为可信。 - 通过确认以下表情符号出现在对方的屏幕上来验证此会话 通过确认屏幕上对方显示以下数字来验证此会话 - 您收到传入验证请求。 与此用户的安全消息端到端加密,无法被第三方读取。 对方取消了验证。 \n%s 验证已取消。 \n理由:%s - 用户已取消验证 验证过程超时 会话不知此事务 @@ -1626,99 +1331,70 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 用户不匹配 您未使用身份服务器 未配置身份服务器,需要重置您的密码。 - 您似乎正在试图连接到另一个家庭服务器。您想要登出吗? - 加入一个聊天室开始使用应用。 您已经跟上了! 您没有未读消息 从这里跟上未读消息 您的私聊消息将在此处显示 您的聊天室将在此显示 - 消息已删除 显示已移除消息 对已移除消息显示占位符 聊天室管理员主持的事件 最后由 %1$s 编辑于 %2$s - - 格式错误事件,无法显示 无网络。请检查您的网络连接。 更改网络 Element 尚不支持公开聊天室预览 - 私聊消息 - 新聊天室 公开 任何人都可以加入此聊天室 将此聊天室发布到聊天室目录 - 获取信任信息时发生错误 获取密钥备份数据时发生错误 - 从文件 \"%1$s\" 导入端到端密钥。 - 其他第三方通知 您已经在查看此聊天室! - 注册令牌 - 提出建议 请在下方写下您的建议。 请在此描述您的建议 谢谢,建议已成功发送 建议发送失败 (%s) - 在时间线上显示隐藏事件 - 私聊消息 - 正在等待… 正在加密缩略图… 正在发送缩略图 (%1$s / %2$s) 正在加密文件… 正在发送文件 (%1$s / %2$s) - 正在下载文件 %1$s… "文件 %1$s 已下载!" - 消息编辑 未找到编辑 - 过滤对话… 找不到您要找的? 创建新聊天室 发送新私聊消息 查看聊天室目录 - 名称或 ID (#example:matrix.org) - 在时间线中启用滑动回复 - 在时间线中合并解密失败消息 在主屏幕上添加未读通知选项卡。 - 链接已复制到剪贴板 - 通过 matrix ID 添加 正在创建聊天室… 无结果,使用通过 matrix ID 添加在服务器上搜索。 开始输入以获得结果 按用户名或 ID 过滤… - 正在加入聊天室… - 查看编辑历史 - 服务条款 审核条款 可被其他人发现 使用机器人,小部件和贴纸包 - 已读于 - - 身份服务器 断开身份服务器 配置身份服务器 @@ -1733,7 +1409,6 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 我们向 %s 给您发送了确认电子邮件,检查您的电子邮件并点击确认链接 我们向 %s 给您发送了确认电子邮件,请先检查您的电子邮件并点击确认链接 正在等待 - 输入身份服务器 URL 无法连接到身份服务器 请输入身份服务器 url @@ -1741,20 +1416,13 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 您选择的身份服务器无任何服务条款。仅在您信任服务所有者时继续 已向 %s 发送文字消息。请输入它包含的验证码。 验证码不正确。 - 您当前在身份服务器 %1$s 上分享电子邮件地址或电话号码。您需要重连接 %2$s 已停止分享。 同意身份服务器 (%s) 服务条款使您可以通过电子邮件地址或电话号码被发现。 - 启用详细日志。 当您发送 RageShake 时详细日志将帮助开发者提供更多日志。即使启用,应用也不会记录消息内容或任何其他私有数据。 - - 接收您的家庭服务器条款和条件后请重试。 - 服务器似乎响应时间太长,这可能是由于连接不良或服务器错误引起的。 请稍后再试。 - 发送附件 - 打开导航菜单 打开创建聊天室菜单 关闭创建聊天室菜单… @@ -1764,16 +1432,13 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 显示密码 隐藏密码 跳到底部 - %1$s,%2$s 和 %3$s 已读 %1$s 和 %2$s 已读 %s 已读 %d 个用户已读 - 上传文件 \'%1$s\' (%2$s) 过大。限制为 %3$s。 - 获取附件时出错。 文件 联系人 @@ -1782,13 +1447,11 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 相册 贴纸 无法处理共享数据 - 媒体 此聊天室中无媒体 文件 %1$s 于 %2$s 此聊天室中无文件 - 垃圾信息 不合适的内容 自定义报告…… @@ -1796,28 +1459,23 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 报告此内容的理由 报告 忽略用户 - 内容已报告 - 此内容已报告。 -\n -\n如果您不希望再看到此用户的更多内容,您可以屏蔽他以隐藏他的信息 + 此内容已报告。 +\n +\n如果您不希望再看到此用户的更多内容,您可以忽略他们以隐藏他们的信息。 报告为垃圾信息 - 此内容已报告为垃圾信息。 -\n -\n如果您不希望再看到此用户的更多内容,您可以屏蔽他以隐藏他的信息 + 此内容已报告为垃圾信息。 +\n +\n如果您不希望再看到此用户的更多内容,您可以忽略他们以隐藏他们的信息。 报告为不合适的内容 - 此内容已报告为不合适。 -\n -\n如果您不希望再看到此用户的更多内容,您可以屏蔽他以隐藏他的信息 - + 此内容已报告为不合适。 +\n +\n如果您不希望再看到此用户的更多内容,您可以忽略他们以隐藏他们的消息。 Element 需要权限在磁盘上保存您的端到端密钥。 \n \n请在下个弹窗中允许访问以便手动导出密钥。 - 目前没有网络连接 - 忽略用户 - 全部消息(嘈杂) 全部消息 仅提到我的 @@ -1829,22 +1487,17 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 %1$s 未做更改 您未做更改 您未忽略任何用户 - 长按聊天室查看更多选项 - - %1$s 将聊天室设为对任何知道链接的用户公开。 您将聊天室设为对任何知道链接的用户公开。 %1$s 将聊天室设为仅限邀请。 您将聊天室设为仅限邀请。 未读消息 - 这是你的对话。拥有它。 与人们私聊或群聊 通过加密保证对话私密 扩展 & 自定义您的体验 开始吧 - 选择服务器 就像电子邮件,账户有一个家,尽管您可以与任何人聊天 在最大的公共服务器上免费加入数百万用户 @@ -1852,7 +1505,6 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 了解更多 其他 自定义 & 高级设置 - 继续 连接到 %1$s 连接到 Element Matrix 服务 @@ -1861,14 +1513,12 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 注册 登入 使用单点登录继续 - Element Matrix 服务地址 地址 面向组织的高级托管 输入 Modular Element 或您想使用的服务器地址 输入您想要连接的服务器或 Element 的地址 输入您想使用的服务器的地址 - 载入页面时出错:%1$s (%2$d) 应用无法登录到此家庭服务器。家庭服务器支持以下登录类型:%1$s。 \n @@ -1877,58 +1527,46 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 应用无法在此服务器上创建账户。 \n \n您想要通过网页客户端注册吗? - 电子邮件未关联到任何账户。 - 在 %1$s 上重置密码 验证邮件将发送到您的收件箱以确认设置您的新密码。 下一个 电子邮件 新密码 - 注意! 更改您的密码将重置所有会话上的端到端加密密钥,从而使加密聊天记录无法读取。在重设密码之前,请设置“密钥备份”或从另一个会话中导出聊天室密钥。 继续 - 电子邮件未链接到任何账户 - 检查您的收件箱 验证电子邮件已发送到 %1$s。 点击链接以确认您的新密码。跟随包含的链接验证后,请点击下方。 我已验证我的电子邮件地址 - 成功! 您的密码已重置。 您已登出全部会话,不会再接收到推送通知。若要重新启用通知,请在每个设备上再次登录。 返回登录 - 注意 您的密码尚未更改。 \n \n是否中止密码更改过程? - 设置电子邮件地址 设置电子邮件用于恢复您的帐户。之后,您可以选择允许您认识的人通过电子邮件发现您。 电子邮件 电子邮件(可选) 下一个 - 设置电话号码 设置电话号码,以选择允许您认识的人发现您。 请使用国际格式。 电话号码 电话号码(可选) 下一个 - 确认电话号码 我们向 %1$s 发送了验证码。在下方输入它以验证您的身份。 输入验证码 重新发送 下一个 - 国际电话号码必须以 ‘+’ 开头 电话号码似乎无效。请检查 - 在 %1$s 上注册 用户名或电子邮件 用户名 @@ -1939,24 +1577,20 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 您的帐户尚未创建。 \n \n是否中止注册过程? - 选择 matrix.org 选择 Element Matrix Services 选择自定义家庭服务器 请进行人机验证 接受条款以继续 - 请检查您的电子邮件 我们向 %1$s 发送了电子邮件。 \n请点击其中包含的链接继续账户创建。 输入的验证码不正确。请检查。 过时的家庭服务器 此家庭服务器运行的版本过旧以至于无法连接。要求您的家庭服务器管理员升级。 - 发送了太多请求。您可以在 %1$d 秒后重试… - 或者,如果您已经拥有账户并知道您的 Matrix 标识符和密码,您可以使用这种方式: 使用 Matrix ID 登录 使用 Matrix ID 登录 @@ -1965,7 +1599,6 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 如果您不知道您的密码,返回并重置。 这不是一个有效的用户标识符。期望的格式:\'@user:homeserver.org\' 无法找到有效的家庭服务器。请检查您的标识符 - 您已登出 这可能由于多种原因: \n @@ -1975,7 +1608,6 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 \n \n• 您的服务器管理员出于安全原因已取消您的访问权限。 重新登入 - 您已登出 登入 您的家庭服务器 (%1$s) 管理员将您从您的账户 %2$s (%3$s) 登出。 @@ -1987,7 +1619,6 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 \n \n如果您不再使用此设备,或想登入另一个帐户,请清除它。 清除全部数据 - 清除数据 是否清除当前存储在此设备上的全部数据? \n再次登入以访问您的帐户数据和消息。 @@ -1995,12 +1626,9 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 清除数据 当前会话用于用户 %1$s 而您提供了用户 %2$s 的凭证。Element 不支持此功能。 \n请先清除数据,然后重新登入另一个账户。 - 您的 matrix.to 链接更是不正确 描述太短 - 初始同步… - 查看我的全部会话 高级设置 开发者模式 @@ -2012,26 +1640,19 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 设置 当前会话 其他会话 - 仅显示第一个结果,请输入更多字符… - 快速失败 发生意外错误时,Element 可能更经常崩溃 - 在明文消息前添加 ¯\\_(ツ)_/¯ - 启用加密 一旦启用,加密无法禁用。 - 您的电子邮件域无权注册此服务器 - 不可信登入 匹配 不匹配 确认下方独特表情以相同顺序出现在他们的屏幕上,以验证此用户。 为了获得最高的安全性,请使用其他可信通信方式或亲自确认。 寻找绿色盾牌以确保用户可信。信任聊天室中的所有用户以确保聊天室的安全。 - 不安全 以下其中一项可能会受到威胁: \n @@ -2039,13 +1660,11 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 \n - 您验证的用户连接到的家庭服务器 \n - 您或其它用户的网络连接 \n - 您或其他用户的设备 - 视频。 图片。 音频 文件 贴纸 - 正在等待… %s 已取消 您已取消 @@ -2055,21 +1674,15 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 验证请求 验证此会话 手动验证 - - 使用其他用户的设备扫描此码以安全地相互验证 扫描他们的码 无法扫描 如果您不在现场,请比较表情符号 - 通过比较表情符号验证 - 通过表情验证 如果您无法扫描上方的码,通过比较简短独特的表情序列验证。 - 二维码图片 - 验证 %s 已验证 %s 正在等待 %s… @@ -2092,52 +1705,38 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 上传 离开聊天室 正在离开聊天室… - 管理员 审查员 自定义 邀请 用户 - %1$s 管理员 %1$s 审查员 %1$s 默认权限 %2$s 自定义权限 (%1$d) - Element 无法处理 \'%1$s\' 类型事件 Element 无法处理 \'%1$s\' 类型消息 Element 在渲染 id 为 \'%1$s\' 的事件内容时遇到了一个问题 - 取消忽略 - 该会话无法与您的其他会话共享此验证。 \n验证将保存在本地,并在此应用的未来版本中共享。 - 最近使用的聊天室 其他聊天室 - 发送彩虹色给定消息 发送彩虹色给定表情 - 时间线 - 消息编辑器 - 启用端到端加密 一旦启用,加密无法禁用。 - 是否启用加密? 启用后,将无法禁用聊天室加密。服务器无法看到加密聊天室中发送的消息,只有聊天室的参与者才能看到。启用加密可能会阻止许多机器人和桥接正常工作。 启用加密 - 为保证安全,通过检查一次性代码验证 %s。 为保证安全,亲自或使用其他通信方式验证。 - 比较独特表情,确保它们以相同顺序出现。 与其他用户设备上显示的代码比较。 与此用户的消息端到端加密,无法被第三方读取。 您的新会话已验证。它可以访问您的加密消息,其他用户会将其视为可信。 - 交叉签名 交叉签名已启用 \n设备上的私钥。 @@ -2147,55 +1746,39 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 交叉签名已启用。 \n密钥不可信 交叉签名未启用 - 您的服务器管理员已默认禁用私有聊天室和私聊消息端到端加密。 激活会话 显示全部会话 管理会话 登出此会话 - 加密信息不可用 - 此会话对安全消息可信因为您已验证它: 验证此会话以将其标记为可信,并授予其访问加密消息的权限。如果您未登录此会话,则您的帐户可能已被盗: - %d 个活动会话 - 验证此登录 其他用户可能不信任它 完善安全 - 使用现有会话来验证此会话,并授予其访问加密消息的权限。 - - 验证 已验证 注意 - 无法获取会话 会话 可信 不可信 - 此会话对加密消息可信,%1$s (%2$s) 已验证它: %1$s (%2$s) 使用新会话登入: 在此用户信任此会话之前,发送到该会话和从该会话发送的消息均标有警告。或者,您可以手动进行验证。 - - 初始化交叉签名 重置密钥 - 二维码 - 快要完成了!%s 显示相同的盾牌了吗? 使 - 到服务器的连接已丢失 飞行模式已打开 - 开发工具 账户数据 @@ -2208,44 +1791,33 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 创建简单调查 使用恢复短语或密钥 如果您无法访问已有会话 - 新登入 - 无法在存储中找到秘密 输入秘密存储密码 注意: 您应仅在可信设备上访问秘密存储 - 移除… 您想要发送此附件到 %1$s 吗? 发送原始尺寸图片 - 确认移除 您确实想要移除(删除)此事件吗?注意如果您删除聊天室名或话题更改,可以撤销更改。 附加理由 编辑理由 - 事件被用户删除,理由:%1$s Element Android - 密钥请求 - 解锁加密消息历史 - 刷新 - 新登录。是您吗? 轻按以审核和验证 使用此会话验证新的会话,授权访问加密消息。 这不是我 您的账户可能已被盗用 - 如果您取消,您将无法在此设备上读取加密消息,其他用户不会信任它 如果您取消,您将无法在新设备上读取加密消息,其他用户不会信任它 如果您现在取消将不会验证 %1$s (%2$s)。在他们的用户个人档案中重新开始。 - 以下其中一项可能有风险: \n \n- 您的密码 @@ -2254,35 +1826,25 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 \n- 设备使用的网络连接 \n \n我们推荐您在设置中立即更换您的密码和恢复密钥。 - 通过设置验证您的设备。 验证已取消 - 恢复密码 消息密钥 账户密码 - 设置一个 %s 生成消息密钥 - 确认 %s - 输入您的 %s 以继续。 - 再次输入您的 %s 确认。 不要使用您的账户密码。 - 输入只有你知道的安全口令,用于保护服务器上的秘密。 - 这可能会花费数秒,请耐心等待。 设置恢复。 您的恢复密钥 完成了! 保持安全 完成 - 使用此 %1$s 作为安全网以防您忘记您的 %2$s。 - 发布创建的身份密钥 从密码生成安全密钥 正在定义 SSSS 默认密钥 @@ -2290,31 +1852,21 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 正在同步用户密钥 正在同步自签名密钥 正在设置密钥备份 - - 您的 %2$s 和 %1$s 已设置。 \n \n请安全地保管它。如果您丢失了全部活动会话您将需要使用它们解锁加密消息和安全信息。 - 将它打印出来并存放在安全的地方 发送爆雷信息 爆雷 输入关键字以查找表情。 - 已阅 - 跳至已读回执 - 事件被聊天室管理员调整,理由:%1$s - 密钥已是最新! - 保护与解锁已加密信息并信任%s。 保存到优盘或者备份盘 复制到您的个人云存储 - 您无法在移动设备上执行此操作 - 设置恢复密码让您能够保护和解锁加密信息并信任设备。 \n \n如果您不希望设置文本密码,那么生成密钥亦可。 @@ -2322,21 +1874,16 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 如果您现在取消,那么当您失去登录权限时也会丢失加密的信息和数据。 \n \n您也可以通过设置菜单来建立保护备份以及管理您的密钥。 - 加密开启 本聊天室信息已经端对端加密。验证成员时,请查看其个人档案以了解更多信息。 加密未开启 不支持本聊天室使用的加密方式 - %s 创建并配置了聊天室。 您创建并配置了聊天室。 - 接近完成!另外的设备是否正显示相同的盾牌图标? 接近完成!等候确认…… 正在等候 %s… - 导入密钥失败 - 通知配置 一对一聊天的加密信息 群聊的加密信息 @@ -2344,26 +1891,19 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 当聊天室升级 故障诊断 按事件设置通知重要性 - 以纯文本形式发送消息,而不将其解释为 markdown - 用户名和/或密码不正确。输入的密码以空格开头或结尾,请检查。 此帐户已停用。 - 消息… - 加密升级可用 启用交叉签名 验证您自己和其他人以保证您的聊天安全 - 输入您的 %s 以继续 使用文件 - 输入 %s 恢复密码 无效的恢复密钥 请输入恢复密钥 - 检查备份密钥 检查备份密钥 (%s) 获取曲线密钥 @@ -2372,20 +1912,16 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 从恢复密钥生成 SSSS 密钥 正在在 SSSS 中保存密钥备份秘密 %1$s (%2$s) - 输入您的密钥备份密码以继续。 使用您的密钥备份恢复密钥 不知道您的密钥备份密码,您可以 %s。 密钥备份恢复密钥 - 阻止应用内屏幕截图 启用此设置添加 FLAG_SECURE 到所有活动。重启应用使更改生效。 - 媒体文件已添加到相册 无法添加媒体文件到相册 无法保存媒体文件 选择新的账户密码… - 在您的其他设备上使用最新的 Element,Element Web,Element Desktop,Element iOS,Element for Android,或其他能够交叉签名的 Matrix 客户端 Element Web \nElement Desktop @@ -2400,28 +1936,23 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 选择您的恢复密钥,或手动输入或从剪贴板粘贴 无法使用此恢复密钥解密备份:请确认您输入了正确的恢复密钥。 访问安全存储失败 - 未加密 由未验证设备加密 查看您的登入位置 验证您的全部会话确保您的账户和消息安全 验证访问您的账户的新登录:%1$s - 使用文本手动验证 验证登录 使用表情交互式验证 通过从您的其他会话验证此登录确认您的身份,授权它访问您的加密消息。 标记为可信 - 请选择用户名。 请选择密码。 仔细检查此链接 链接 %1$s 正在将您带到另一个站点:%2$s。 \n \n您确定想要继续吗? - 我们无法创建您的私聊消息。请检查您要邀请的用户并重试。 - 添加成员 邀请 正在邀请用户… @@ -2432,11 +1963,9 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 邀请已发送到 %1$s 和 %2$s 等 我们无法邀请用户,请检查您想要邀请的用户并重试。 - 当前语言 其他可用语言 正在载入可用语言… - 打开 %s 条款 是否从身份服务器 %s 断开? 身份服务器已过期。Element 仅支持 API V2。 @@ -2446,7 +1975,6 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 为了您的隐私,Element 仅支持发送用户电子邮件和电话号码的哈希值。 关联失败。 此标识符无当前关联。 - 您的家庭服务器 (%1$s) 建议使用 %2$s 作为您的身份服务器 使用 %1$s 或者,您可以输入任何其他身份服务器 URL @@ -2459,9 +1987,7 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 取消静音麦克风 停止相机 启动相机 - 设置安全备份 - 安全备份 通过在您的服务器上备份加密密钥防止丢失对加密消息和数据的访问。 设置 @@ -2469,22 +1995,17 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 生成安全密钥存储在安全的地方如密码管理器或保险箱。 使用安全口令 输入仅有您知道的安全口令,生成备份用的密钥。 - 保存您的安全密钥 将您的安全密钥存储在安全的地方如密码管理器或保险箱。 - 设置安全口令 输入只有您知道的安全口令,用于保护您的服务器上的秘密。 安全口令 再次输入您的安全口令以确认。 - 保存您的安全密钥 将您的安全密钥存储在安全的地方如密码管理器或保险箱。 - 聊天室名称 主题 您已成功更改聊天室设置 - 您无法访问此消息 正在等待此消息,可能会花费一些时间 无法解密 @@ -2493,17 +2014,12 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 您无法访问此消息因为您的会话不被发送者信任 您无法访问此消息因为发送者有意不发送密钥 正在等待加密历史 - Riot 现在是 Element! 我们兴奋地宣布我们改名了!您的应用已经是最新的并且您已登入您的帐户。 明白了 了解更多 - element - - 将恢复密钥保存到 - 从我的电话簿添加 您的电话簿是空的 电话簿 @@ -2511,22 +2027,19 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 正在获取您的联系人… 您的通讯录是空的 通讯录 - 撤销邀请 是否撤销对 %1$s 的邀请? - 被 %1$s 封禁 解封用户失败 - 推送通知已禁用 查看您的设置以启用推送通知 选择 PIN 以确保安全 确认 PIN - 验证 pin 失败,请输入新的。 + 验证 PIN 失败,请输入新的。 输入您的 PIN 忘记 PIN? - 重置 pin - 新 pin + 重置 PIN + 新 PIN 为重置您的 PIN,您将需要重新登录并创建新的。 启用 PIN 如果您想要重置您的 PIN,点按忘记 PIN 登出并重置。 @@ -2545,13 +2058,11 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 成功导入 %1$d/%2$d 个密钥。 - 管理集成 无活动小部件 聊天室已创建,但由于以下原因一些邀请尚未发送: \n \n%s - %1$s,%2$s 和其他 %3$d 人已读 @@ -2567,15 +2078,77 @@ Element 在后台时的工作将被显著的限制,这可能会影响消息通 电话号码 移除 %s? 请确认您已点击我们向您发送的电子邮件中的链接。 - 电子邮件和电话号码 管理链接到您的 Matrix 账户的电子邮件和电话号码 - 代码 请使用国际格式(电话号码必须以 ‘+’ 开始) 通过验证此登录确认您的身份,授权它访问加密信息。 抱歉,对于连接到单点登录的账户此操作尚不可用。 - 无法打开您被封禁的聊天室。 无法找到此聊天室。请确认它存在。 - + 您没有权限在此聊天室发起通话 + + %d 秒 + + 显示聊天室成员状态事件 + 包括邀请/加入/离开/踢掉/封禁事件和头像/昵称变更。 + 轮询 + 机器人按钮 + 回应:%s + 验证结果 + 是否删除类型 %1$s 的账户数据? +\n +\n小心使用,它可能导致意外行为。 + 链接格式不正确 + 每次打开 Element 都要求 PIN 码。 + 在 2 分钟未使用 Element 后要求 PIN 码。 + 2 分钟后要求 PIN + 仅在一个简单通知中显示未读消息内容数量。 + 显示细节如聊天室名称和消息内容。 + 在通知中显示内容 + PIN 码是解锁 Element 的唯一方式。 + 启用设备特定的生物特征识别,如指纹和面部识别。 + 启用生物特征识别 + 配置保护 + 使用 PIN 和生物特征识别保护访问。 + 保护访问 + + 显示您现在可以验证的 %d 个设备 + + 您将重新启动,没有历史记录,消息,受信任的设备或受信任的用户 + 如果您重置一切 + 仅当没有其他设备可用来验证此设备时,才执行此操作。 + 重置一切 + 忘记或丢失了所有的回复选项?重置一切 + 您已加入。 + %s 已加入。 + 此聊天室的消息是端到端加密的。 + 离开 + 设置 + 此处的消息是端到端加密的, +\n +\n您的消息已用锁保护并且只有您和收件人拥有唯一的钥匙解锁它们。 + 此处的消息不是端到端加密。 + 此家庭服务器正在运行较旧版本。要求您的家庭服务器管理员升级。您可以继续,但一些功能可能无法正确工作。 + 您仅发出此邀请。 + %1$s 仅发出此邀请。 + 在加密聊天室显示完整历史 + %1$s 和 %2$s + %1$s 条在 %2$s 和 %3$s 中 + + %d 个邀请 + + 通知已点击! + 请点击通知。如果您未看到通知,请检查系统设置。 + 通知显示 + 您正在查看通知!点我! + 接受推送失败。重装应用或可解决。 + 应用正在接受推送 + 应用正在等待推送 + 测试推送 + 尚不支持在加密聊天室中搜索。 + 过滤被封禁的用户 + 您没有权限发起通话 + 您没有权限发起会议通话 + 重置 + \ No newline at end of file diff --git a/vector/src/main/res/values-zh-rTW/strings.xml b/vector/src/main/res/values-zh-rTW/strings.xml index efbb583ec9..e4ece6c83f 100644 --- a/vector/src/main/res/values-zh-rTW/strings.xml +++ b/vector/src/main/res/values-zh-rTW/strings.xml @@ -1,19 +1,15 @@ - + zh TW - 淺色主題 深色主題 黑色主題 - 正在同步…… 聆聽事件 - 接受 拒絕 掛斷 - 訊息 聊天室 設定 @@ -24,17 +20,14 @@ 離開 儲存 載入中… - 發送 重新發送 分享 查看源代碼 通知音效 靜音通知 - 歷史 社群細節 - 移除 引用 稍後 @@ -59,7 +52,6 @@ 邀請 離線 - 離開 動作 登出 @@ -73,25 +65,20 @@ 關閉 複製到剪貼簿 停用 - 確認 警告 - 首頁 最愛 聯絡人 聊天室 社群 - 過濾聊天室名稱 過濾最愛 過濾人 過濾聊天室名稱 過濾社群名稱 - 邀請 低優先度 - 聊天 裝置上的通訊錄 使用者目錄 @@ -99,7 +86,6 @@ 沒有對話 您沒有允許 Element 存取裝置上的聯絡資訊 沒有結果 - 聊天室 聊天室目錄 沒有聊天室 @@ -107,11 +93,9 @@ %d 個使用者 - 邀請 社群 沒有群組 - 傳送記錄 傳送當機紀錄 傳送螢幕截圖 @@ -122,14 +106,11 @@ 您似乎沮喪的搖晃著手機。您要開啟錯誤回報畫面嗎? 這個應用程式上次當掉了。您想要開啟錯誤回報畫面嗎? 憤怒搖晃來回報臭蟲 - 此錯誤回報已成功送出 此錯誤回報無法送出(%s) 進度(%s%%) - 傳送到 讀取 - 加入聊天室 使用者名稱 建立帳號 @@ -138,20 +119,16 @@ 家伺服器網址 身份識別伺服器網址 搜尋 - 開始新聊天 開始語音通話 開始視訊通話 - 您確定想要與 %s 開始新的聊天? 您確定想要與開始語音通話? 您確定想要與開始視訊通話? - 傳送檔案 拍照或錄影 拍照 錄影 - 登入 建立帳號 傳送 @@ -197,7 +174,6 @@ 電子郵件已傳送至 %s,點擊下方的連結來前往。 電子郵件地址驗證失敗: 請確保您已點擊郵件中的連結 您的密碼已重設。 您已登出所有工作階段,並且不會再收到推送通知。要重新啟用通知,再次於每個裝置上登入。 - 網址必須是 http[s]:// 開頭 無法登入:網路錯誤 無法登入 @@ -205,7 +181,6 @@ 無法註冊 無法註冊:電子郵件的擁有權失敗 請輸入有效的網址 - 無效的使用者名稱/密碼 指定的存取權杖無法被認可 異常的 JSON @@ -213,28 +188,21 @@ 傳送太多請求 此使用者名稱已被使用 此電子郵件連結已經被點擊過 - 讀取接收者清單 - 群組清單 - 傳送為 原始 - 取消下載? 取消上傳? %d 秒 %1$d 分 %2$d 秒 - 昨天 今天 - 聊天室名稱 聊天室主題 - 通話 通話已連接 通話正在連接… @@ -244,15 +212,12 @@ 視訊來電 語音來電 通話進行中…… - 遠端未能接聽。 媒體連線失敗 無法初始化相機 電話在另一處已答覆 - 拍照或錄影 無法錄影 - 資訊 Element 需要權限來存取你的照片與影片庫,以傳送及儲存附件。 @@ -272,31 +237,25 @@ Element 可以檢查您的電話簿並以電子郵件與電話號碼為基礎來尋找其他 Matrix 使用者。 \n \n您同意為此用途分享您的電話簿嗎? - 抱歉。由於缺少權限,所以無法執行動作 - 已儲存 儲存到下載? 繼續 - 移除 加入 預覽 拒絕 - 列出成員 打開標頭 正在同步…… 跳到第一個未讀的訊息。 - 你已經被 %s 邀請加入此聊天室 邀請已經被傳送給 %s,但與此帳號沒有進行關連。 你也許希望以不同帳號登入,或將電子郵件加入到您的帳號。 您正在嘗試存取 %s,您想要加入討論嗎? 聊天室 這是聊天室的預覽,聊天室互動已停用。 - 新聊天 加入成員 @@ -306,21 +265,17 @@ %d 個成員 1 個成員 - 離開聊天室 您確定想要離開聊天室? 你確定想從此聊天室移除 %s? 建立 - 線上 離線 閒置 - 管理者工具 通話 私人訊息 工作階段 - 邀請 離開聊天室 從此房間移除 @@ -336,22 +291,18 @@ 顯示工作階段清單 您確定想要邀請 %s 到聊天室? 封鎖使用者將會把他們從此聊天室中踢除,並避免他們再次加入。 - 透過 ID 邀請 裝置上的聯絡人(%d) 使用者目錄 (%s) 只顯示 Matrix 使用者 - 透過 ID 邀請使用者 請輸入一或多個電子郵件地址或 Matrix ID 電子郵件或 Matrix ID - 搜尋 %s 正在打字… %1$s 和 %2$s 正在打字… 由於您提升此使用者擁有的權限等級與你自己相同,因此您將無法還原此變更。 你確定? - %1$s 及 %2$s 和其他人正在打字… 傳送加密的訊息… 傳送訊息(未加密)… @@ -367,7 +318,6 @@ %d 則新訊息 - 信任 不信任 登出 @@ -377,37 +327,27 @@ %d 會員變動 - 這可能表示有人正惡意的攔截你的流量,或你的手機不信任由遠端伺服器提供的憑證。 如果伺服器管理員說這是正常的,請確保底下的指紋與他們所提供的指紋符合。 你的手機曾經信任的憑證已經變動。這非常不尋常。建議你不要接受此新的憑證。 發送貼圖 - 第三方開放原始碼授權 - 下載 說話 清除 發送語音訊息 - 發送貼圖 您目前沒有任何貼圖。 要新增一些貼圖嗎? - 開啟以…… 對不起,沒有應用程式可以完成此操作。 - 從其他工作階段重新請求金鑰。重新請求金鑰 - 已發送金鑰分享請求。 - 已發送請求 "請在另一個可以解密訊息的裝置上啟動 Element, 以便它將金鑰發送到此工作階段。" - 憑證已從以前受信任的更改為不受信任的憑證。伺服器可能已續訂其憑證。請與伺服器管理員聯繫以尋找所需的指紋。 僅當伺服器管理員發佈的指紋與上面的指紋匹配時才接受此憑證。 - 聊天室詳情 成員 檔案 @@ -415,15 +355,12 @@ ID 格式不正確。它應該是電子郵件地址或 Matrix ID,如 ‘@localpart:domain’ 已邀請 已加入 - 報告此內容的原因 您想要隱藏此使用者的所有訊息嗎? 注意此動作將會重新啟動應用程式,而其可能需要一點時間。 取消上載 取消下載 - - 搜尋 過濾聊天室成員 沒有結果 @@ -431,7 +368,6 @@ 訊息 聯絡人 檔案 - 加入 目錄 最愛 @@ -443,16 +379,14 @@ 加入聊天室 加入聊天室 輸入聊天室 ID 或聊天室別名 - 瀏覽目錄 - %1$d 個聊天室 + %d 個聊天室 有 %1$d 個聊天室匹配 “%2$s” 正在搜尋目錄…… - 所有訊息(通知並震動) 所有訊息 僅限提及 @@ -462,8 +396,7 @@ 私人訊息 離開對話 忘記 - 添加主熒幕快捷方式 - + 新增到主畫面 訊息 設定 版本 @@ -471,7 +404,6 @@ 第三方通知 版權 隱私權政策 - 個人檔案圖片 顯示名稱 電子郵件地址 @@ -480,7 +412,6 @@ 新增電話號碼 顯示在系統設定中的應用程式資訊。 應用程式資訊 - 通知隱私 標準 低隱私模式 @@ -490,12 +421,10 @@ • 通知的消息內容將從 Matrix 主服務器安全取得 • 通知含有訊息與中繼資料 • 通知不會顯示訊息內容 - 通知音效 啟用這個帳號的通知 啟用此工作階段的通知 將螢幕開啟 3 秒 - 包含我顯示名稱的訊息 包含我用戶名稱的訊息 來自私訊的訊息 @@ -503,13 +432,11 @@ 當我被邀請進聊天室時 通話請求 來自機器人的訊息 - 在裝置啓動時自動啓動 後臺同步 開啓後臺同步 同步請求超時 每次同步間的延遲 - 版本 olm 版本 條款與條件 @@ -519,7 +446,6 @@ 清空暫存檔 清除媒體暫存檔 保留媒體檔案 - 使用者設定 通知 已忽略的使用者 @@ -538,23 +464,18 @@ 對所有訊息顯示時間戳 用12小時制顯示時間戳 提及使用者時震動 - 停用帳號 停用我的帳號 - 通知隱私 Element 可以在後臺安全隱密地管理通知。這可能會影響電池的使用。 獲取權限 選擇其他選項 - 傳送分析資料 傳送分析資料 Element 會收集匿名分析以讓我們可以改進此應用程式。 請允許收集匿名分析以讓我們可以改進此應用程式。 是的,我想要協助! - 節省流量模式 - 工作階段資訊 ID 公開名稱 @@ -566,22 +487,18 @@ 授權 密碼: 傳送 - 登入爲 家伺服器 身份認證伺服器 - 使用者介面 語言 選擇語言 - 等待驗證 請檢查您的電子郵件並點選其中包含的連結。只要這個完成了,就點選選繼續。 無法驗證電子郵件地址。請檢查您的電子郵件,並點選它所包含的連結。完成此動作後,按一下「繼續」。 此電子郵件地址已經被使用。 找不到該電子郵件信箱。 這個電話號碼已被使用。 - 變更密碼 目前的密碼 新密碼 @@ -591,13 +508,9 @@ 顯示所有來自 %s 的訊息? 注意此動作將會重新啟動應用程式,而其可能需要一點時間。 - 確實要刪除此通知目標嗎? - 您確認要移除 %1$s %2$s 嗎? - 選擇國家 - 國家 請選擇一個國家 電話號碼 @@ -607,25 +520,19 @@ 輸入驗證碼 驗證您的電話號碼時出錯 代碼 - - 特色 - 3 天 1 週 1 個月 永遠 - 聊天室圖片 聊天室名稱 主題 聊天室標籤 標記為: - 我的最愛 低優先度 - 訪問和可見度 將此聊天室列入聊天室目錄 通知 @@ -633,19 +540,15 @@ 房間歷史可讀性 誰能檢視歷史訊息? 誰能進入這個聊天室? - 任何人 僅成員(自選取此選項開始) 僅成員(自他們被邀請開始) 僅成員(自他們加入開始) - 要連結一個聊天室,該聊天室必須設定地址。 僅有被邀請的人 任何知道該聊天室連結的人,但訪客除外 任何知道該聊天室連結的人,包括訪客 - 被封鎖的用戶 - 進階 此聊天室的內部 ID 為 地址 @@ -656,38 +559,28 @@ 您需要登出才能啟用加密。 只匯出到已驗證的工作階段 從不在此房間中從此工作階段上傳送未加密的訊息到未驗證的工作階段。 - 此房間沒有本機地址 新地址 (如 #foo:matrix.org) - 此聊天室並未對任何社群顯示特色 新社群地址(如 +foo:matrix.org) 無效的社群地址 “%s”不是一個有效的社群 ID - - 別名格式錯誤 \'%s\' 不是有效的別名格式 您沒有為此聊天室指定主要地址。 主地址警告 - 設定為主地址 取消設定為主地址 複製聊天室 ID 複製聊天室地址 - 此聊天室已啟用加密。 此聊天室未啟用加密。 啟用加密 (警告: 啟用後無法停用) - 目錄 主題 - %s 試圖載入此聊天室的一個特定的時間點, 但無法找到它。 - 端到端加密資訊 - 事件資訊 使用者 ID Curve25519 認證金鑰 @@ -695,7 +588,6 @@ 演算法 會話 ID 解密錯誤 - 發送者的工作階段訊息 公開名稱 公開名稱 @@ -703,7 +595,6 @@ 工作階段金鑰 驗證 Ed25519 指紋 - 匯出聊天室的端到端加密金鑰 匯出聊天室的加密金鑰 匯出金鑰到本機檔案 @@ -713,60 +604,49 @@ 聊天室的端到端加密金鑰已儲存爲「%s」。 警告:如果應用程式被解除安裝,這個檔案可能會被刪除。 - 匯入聊天室端到端加密密鑰 匯入聊天室金鑰 從本機檔案匯入金鑰 匯入 僅加密驗證過的工作階段 從不自此工作階段傳送加密的訊息到未驗證的工作階段。 - 未驗證 已驗證 已列入黑名單 - 未知工作階段 - 驗證 取消驗證 列入黑名單 解除黑名單 - 驗證工作階段 透過將以下內容與您的其他工作階段中的使用者設定來確認: 如果不符合的話,您的通訊安全可能正受到威脅。 我驗證金鑰相符 - 聊天室包含未知工作階段 此聊天室包含未經驗證的工作階段。 \n無法保證這些工作階段屬於他們聲稱的使用者。 \n我們建議在繼續操作前驗證每一個工作階段,但是你也可以選擇不驗證而重新傳送該訊息。 \n \n未知工作階段: - 選擇一個聊天室目錄 伺服器可能不可用或超載 輸入一個主伺服器來列出所有公開聊天室 家伺服器 URL 在 %s 伺服器上的所有聊天室 所有本地 %s 聊天室 - 在此輸入… - - %d 條未讀訊息 + %d 條未讀的已通知訊息 - %d 條未讀訊息 + %d 條未讀的已通知訊息 %d 個聊天室 %1$s 條在 %2$s 中 - 搜尋歷史 - 文字大小選擇 微小 @@ -775,15 +655,13 @@ 更大 最大 巨大 - 您需要相關權限以管理此聊天室的小部件 創建小部件失敗 使用 jitsi 建立會議通話 您確定要從聊天室刪除小工具嗎? - 已啓用 %d 個小部件 + %d 個作用中的小工具 - 無法建立小部件。 發送請求失敗。 權限等級必需為正整數。 @@ -797,57 +675,44 @@ 新增 Matrix 應用程式 使用原生相機 傳送語音訊息 - 您新增的工作階段「%s」正在請求加密金鑰。 您未驗證的工作階段「%s」正在請求加密金鑰。 開始驗證 不驗證就分享 忽略請求 - 警告! 會議通話正在開發階段,可能不可靠。 - 指令出錯 無法辨識的指令:%s - 關閉 通知並震動 - 已加密的訊息 - 建立 建立社群 社群名稱 範例 社群地址 範例 - 首頁 成員 聊天室 沒有使用者 - 聊天室 已加入 已邀請 篩選社群成員 篩選社群聊天室 - 社群管理員未提供此社群的具體描述。 - 您已被 %2$s 從 %1$s 踢出 您已被 %2$s 從 %1$s 封鎖 理由:%1$s 重新加入 忘記聊天室 - 已讀提示用戶資料圖片 通知用戶資料圖片 用戶資料圖片 - 如要繼續使用此 %1$s 主伺服器,您必須同意該合約條款。 現在檢視 - 停用帳號 這將使您的帳戶永久不可用。您將無法登錄, 並且沒有人能夠重新註冊相同的使用者 ID。這將導致您的帳戶離開它所參與的所有房間, 它將從您的身份伺服器上刪除您的帳戶詳細資訊。此操作是不可逆轉的。 @@ -857,16 +722,12 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 請在我的帳號停用時忘記我傳送過的所有訊息(警告:這將會造成未來的使用者無法看見完整的對話紀錄) 若要繼續進行,請輸入您的密碼: 停用帳號 - 請輸入您的密碼。 - 如果可以,請使用英文撰寫描述。 傳送加密回覆…… 傳送回覆(未加密)…… 在傳送前預覽媒體 - 您目前並非任何社群的成員。 - 使用鍵盤 Enter 以傳送訊息 顯示動作 阻擋指定 id 的使用者 @@ -881,12 +742,10 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 變更您的顯示暱稱 開啟/關閉 markdown 為了修復 Matrix 應用程式管理 - 這個聊天室已被取代,且不再活躍 對話在此繼續 這個聊天示是其他對話的延續 點選這裡以檢視更舊的訊息 - 因為沒有權限,此動作無法執行。 %d 秒 @@ -900,61 +759,45 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 %d 天 - 目前 %1$s %1$s %2$s 前 - "%1$s, " %1$s 與 %2$s %1$s %2$s - %d 已選取 %d 個成員 - %d 個聊天室 系統警告 - 超過資源限制 聯絡管理員 - 聯絡您的服務管理員 - 此家伺服器已經超過其中一項資源限制,所以有一些使用者將會無法登入 此家伺服器已經超過其中一項資源限制。 - 此家伺服器已經達到了每月活躍使用者限制,所以有一些使用者將會無法登入 此家伺服器已經達到其每月活躍使用者限制。 - 請 %s 以讓此限制增加。 請 %s 以繼續使用此服務。 - 錯誤 - 延遲載入聊天室成員 透過僅載入在第一次看到的地方的聊天室成員來增進效能。 您的家伺服器尚不支援延遲載入聊天室成員。請稍後再試。 - 抱歉,遇到錯誤 - Status.im 主題 - 版本 %s 請建立密語以加密您匯出的金鑰。您將需要輸入這些密語以匯入金鑰。 建立密語 密語必須符合 展開 摺疊 - 無論如何都要通話 踢出 理由 - 當您的家伺服器支援此功能時在聊天中預覽連結。 傳送輸入通知 讓其他使用者知道您正在輸入。 @@ -969,30 +812,23 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 密碼 啟動系統相機而非自訂的相機畫面。 這個選項需要第三方應用程式來錄製訊息。 - 指令「%s」需要更多參數,或是有一些參數不正確。 Markdown 已啟用。 Markdown 已停用。 - 顯示資訊區域 總是 訊息與錯誤 僅錯誤 - %1$s: %1$s:%2$s +%d %d+ - 通話 為來電使用預設的 Element 鈴聲 來電鈴聲 選取通話鈴聲: - 接受 - 請審閱並接受此家伺服器的政策: - 疑難排解通知 疑難排解診斷 執行測試 @@ -1000,57 +836,47 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 基本診斷正常。如果您仍然沒有收到通知,請遞交錯誤回報以協助我們調查原因。 一個或更多的測試失敗,嘗試建議的修復。 一個或更多的測試失敗,請遞交錯誤回報以協助我們調查原因。 - 系統設定。 通知已在系統設定中啟用。 通知已在系統設定中停用。 請檢查系統設定。 開啟設定 - 帳號設定。 通知已為您的帳號啟用。 通知已為您的帳號停用。 \n請檢查帳號設定。 啟用 - 工作階段設定。 通知已為此工作階段啟用。 此工作階段未啟用通知。 \n請檢查 Element 設定。 啟用 - Play 服務檢查 Google Play 服務 APK 可用且已為最新。 Element 使用 Google Play 服務來傳遞推送訊息,但它似乎並未正確設定: %1$s 修復 Play 服務 - Firebase 權杖 成功擷取 FCM token: %1$s 擷取 FCM token 失敗: %1$s - 註冊 token FCM token 成功註冊至家伺服器。 FCM token 註冊至家伺服器失敗: %1$s - 通知服務 通知服務正在執行。 通知服務並未執行。 嘗試重新啟動應用程式。 啟動服務 - 通知服務自動重新啟動 服務被自動砍除並重新啟動。 服務重新啟動失敗 - 開機時啟動 服務將會在裝置重新啟動時自行啟動。 服務不會在裝置重心啟動時自行啟動,您將不能在 Element 開啟前收到通知。 啟用開機時啟動 - 檢查背景限制 背景限制已為 Element 停用。本測試應該使用行動數據執行(不是 WiFi)。 %1$s @@ -1058,42 +884,31 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 應用程式要在背景執行的工作將被顏格限制,這可能會影響通知。 %1$s 停用限制 - 電池最佳化 Element 不會被電池最佳化影響。 如果使用者不為裝置充電,並讓其靜置一段時間,且將螢幕關閉,裝置將會進入 Doze 模式。這可能會導致應用程式無法存取網路,並延遲它們的工作、同步與標準警報。 忽略最佳化 - 背景連線 Element 需要保持最低影響的背景連線以接收可靠的通知。 在下一個畫面,您必須允許 Element 總是在背景執行,請接受。 授予權限 - 當驗證您的電子郵件地址時遇到錯誤。 - 當驗證您的手機號碼時遇到錯誤。 額外資訊:%s - 找不到有效的 Google Play 服務 APK。通知可能無法正常運作。 - 視訊通話進行中…… - 金鑰備份 使用金鑰備份 金鑰備份尚未完成,請稍候…… - 略過 完成 - 進階通知設定 活動通知重要程度 - 自訂設定。 注意,某些訊息類型會被設定為靜音(將會生成沒有音效的通知)。 有一些通知在您的自訂設定中被停用了。 載入自訂規則失敗,請重試。 檢查設定 - [%1$s] 這個錯誤並非 Element 所能控制,而是與 Google 有關,這個錯誤代表裝置註冊了太多使用 FCM 的應用程式。這個錯誤只會發生在有超大量的應用程式的裝置上,所以不應該影響一般的使用者。 [%1$s] @@ -1101,23 +916,17 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 [%1$s] 這個錯誤並非 Element 所能控制。手機上沒有 Google 帳號。請開啟帳號管理員並新增一個 Google 帳號。 新增帳號 - 設定吵鬧的通知 設定通話通知 設定安靜的通知 選擇 LED 顏色、震動、音效…… - - 加密金鑰管理 管理金鑰備份 - 安靜 請輸入通關密語 通關密語太弱了 - 如果您想要讓 Element 生成復原金鑰的話,請刪除通關密語。 沒有可用的 Matrix 工作階段 - 永不遺失已加密的訊息 在加密聊天室裡的訊息是使用端到端加密。只有您和接收者有金鑰可以閱讀這些訊息。 @@ -1129,7 +938,6 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 復原金鑰已被儲存為「%s」。 警告:如果應用程式被解除安裝的話,此檔案可能會被刪除。 - 請複製 分享復原金鑰與…… 正在使用通關密語生成復原金鑰,這個過程可能需要數秒鐘。 @@ -1137,30 +945,22 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 未預期的錯誤 備份已開始 您的加密金鑰現在已背景備份到您的家伺服器。初始備份可能需要數分鐘。 - - 您確定嗎? 如果您登出或遺失您的裝置的話,您可能會失去對您的訊息的存取權。 - 正在擷取…… 使用您的復原通關密語以解鎖您的加密訊息歷史 使用您的復原金鑰 不知道您的復原通關密語,您可以 %s。 - 使用您的復原金鑰以解鎖您的加密訊息歷史 輸入復原金鑰 - 訊息復原 - 遺失您的復原金鑰?您可以在設定中設定一個新的。 備份可能無法使用此通關密語解密:請驗證您是否輸入正確的復原通關密語。 網路錯誤:請檢查您的連線並重試。 - 正在復原備份: 解鎖歷史紀錄 請輸入復原金鑰 備份可能無法使用此復原金鑰解密:請驗證您是否輸入正確的復原金鑰。 - 備份已復原 %s! 使用 %d 金鑰復原備份。 @@ -1168,19 +968,13 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 %d 新的金鑰已加入到此工作階段。 - 取得最新的復原金鑰版本 (%s0。 工作階段加密未啟用 - - 自備份復原 刪除備份 - 金鑰備份已為此工作階段正確設定。 金鑰備份未在此工作階段上啟用。 您的金鑰尚未從此工作階段中備份。 - - 備份有從 ID %s 的未知工作階段而來的簽章。 備份有從此工作階段而來的有效簽章。 備份有從已驗證的 %s 工作階段而來的有效簽章。 @@ -1188,13 +982,10 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 備份有從已驗證的 %s 工作階段而來的無效簽章 備份有從未驗證的 %s 工作階段而來的無效簽章 取得備份的信任資訊失敗 (%s)。 - 請刪除備份…… 刪除備份失敗 (%s) - 刪除備份 要從伺服器刪除您已備份的加密金鑰?您將無法使用您的復原金鑰來讀取已加密的訊息歷史。 - 如果您現在登出的話,您將會失去您的加密訊息 金鑰備份進行中。如果您現在登出,您將會失去您的加密訊息。 安全金鑰備份應該在您所有的工作階段中啟用以避免失去對您的加密訊息的存取權。 @@ -1204,19 +995,15 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 您確定嗎? 備份 除非您在登出前備份您的金鑰,否則您將會失去對您的加密訊息的存取權。 - 保留 放棄 - 您想要登出嗎? 數據節省模式會套用特定的過濾器,這樣狀態更新與輸入通知就會被過濾掉。 - 加密訊息復原 請輸入使用者名稱。 開始使用金鑰備份 (進階) 手動匯出金鑰 - 以通關密語保護您的備份。 我們將會在您的家伺服器上儲存一份加密過的您的金鑰副本。使用通關密語保護您的備份以確保其安全。 \n @@ -1233,24 +1020,18 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 分享 永遠不失去加密訊息 開始使用金鑰備份 - 永遠不失去加密訊息 使用金鑰備份 - 新安全訊息金鑰 管理金鑰備份 - 正在備份金鑰…… - 所有金鑰都已備份 正在備份 %d 金鑰…… - 版本 演算法 簽章 - 要在此工作階段上使用金鑰備份,現在就使用您的通關密語或復原金鑰復原。 新的金鑰備份 已偵測到新的安全訊息金鑰備份。 @@ -1261,23 +1042,19 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 正在下載金鑰…… 正在匯出金鑰…… 忽略 - 使用單一登入系統登入 這個 URL 不可用,請檢查 您的裝置正在使用過期的 TLS 安全協定,容易遭受攻擊,為了您的安全將不會連線 使用 Enter 傳送訊息 軟體鍵盤的 Enter 按鈕將會傳送訊息而非換行 - 更新密碼 密碼無效 密碼不符合 - 無效的家伺服器探索回應 自動完成伺服器選項 Element 偵測到您的 userId 網域「%1$s」有自訂的伺服器設定: \n%2$s 使用設定 - 正在初始化服務 媒體 預設壓縮 @@ -1285,7 +1062,6 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 預設媒體來源 選擇 播放快門聲 - 標示為已讀 應用程式需要在背景連線到家伺服器,它應該可以降低耗電量 @@ -1294,18 +1070,14 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 %d 個通知 - 新活動 聊天室 新訊息 新邀請 ** 傳送失敗 - 請開啟聊天室 - 抱歉,使用 Jitsi 建立會議通話在舊裝置上並不支援(Android 系統版本小於 5.0 的裝置) - 驗證工作階段 - 未知的 IP 有新工作階段正在要求加密金鑰。 \n工作階段名稱:%1$s @@ -1315,45 +1087,36 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 \n工作階段名稱:%1$s \n上次檢視:%2$s \n如果您沒有登入其他工作階段,請忽略本請求。 - 驗證 分享 金鑰分享請求 忽略 - 透過比較短文字字串來驗證。 為了最佳的安全性,我們建議您面對面或以其他可信的通訊方式執行此操作。 開始驗證 傳來的驗證請求 驗證此工作階段以將其標記為可信任。在使用端到端加密訊息時,信任夥伴的工作階段可以讓您更安心。 驗證此工作階段會將其標記為可信任,同時也會在對方的裝置上將您的工作階段標記為可信任。 - 透過確認出現在夥伴螢幕下方的顏文字來驗證此工作階段 透過確認出現在夥伴螢幕下方的數字來驗證此工作階段 - 您已收到傳來的驗證請求。 檢視請求 正在等待夥伴確認…… - 已驗證! 您已成功驗證此工作階段。 與此使用者的安全訊息是端到端加密,無法被第三方讀取。 知道了 - 沒有出現東西嗎?不是所有客戶端都支援互動式驗證。使用舊版驗證方式。 使用舊版驗證方式。 - 金鑰驗證 請求已取消 其他夥伴已取消驗證。 \n%s 驗證已取消。 \n理由:%s - 互動式工作階段驗證 驗證請求 %s 想要驗證您的工作階段 - 使用者已取消驗證 驗證程序逾時 工作階段不知道該處理過程 @@ -1365,23 +1128,18 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 金鑰不符合 使用者不符合 未知錯誤 - 您的家伺服器上已有備份 看起來您已經從其他工作階段設定金鑰備份了。您想要用您正在建立的這個來取代它嗎? 取代 停止 - 正在檢查備份狀態 您已經因為無效或過期的憑證而登出。 - 編輯 回覆 - 重試 加入聊天室以開始使用應用程式。 傳送給您邀請 由 %s 邀請 - 您都看完了! 您已經沒有未讀的訊息了 歡迎回家! @@ -1390,19 +1148,15 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 您的直接訊息對話將會在此顯示 聊天室 您的聊天室將會在此顯示 - 反應 同意 喜歡 新增反應 檢視反應 反應 - 由使用者刪除的活動 由聊天室管理員審核的活動 最後編輯由 %1$s 於 %2$s - - 活動格式錯誤,無法顯示 建立新聊天室 沒有網路。請檢查您的網路連線。 @@ -1410,13 +1164,10 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 變更網路 請稍候…… 所有社群 - 無法預覽此聊天室 Element 尚不支援預覽所有人皆可讀的聊天室 - 聊天室 直接訊息 - 新聊天室 建立 聊天室名稱 @@ -1424,18 +1175,13 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 任何人都可以加入此聊天室 聊天室目錄 將此聊天室公開於聊天室目錄中 - 取得信任資訊時發生錯誤 取得金鑰備份資料時發生錯誤 - 從檔案「%1$s」匯入 e2e 金鑰。 - Matrix SDK 版本 其他第三方提醒 您已在檢視此聊天室了! - 快速反應 - 一般 偏好設定 安全與隱私 @@ -1443,94 +1189,67 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 推送規則 未定義通送規則 沒有已註冊的推送閘道 - app_id: push_key: app_display_name: session_name: Url: 格式: - 音訊與視訊 說明與關於 - - 註冊代符 - 建議 請在下面編寫您的建議。 在此描述您的建議 感謝,建議已成功傳送 建議傳送失敗 (%s) - 在時間軸中顯示隱藏的活動 - 直接訊息 - 正在等待…… 正在加密縮圖…… 正在傳送縮圖 (%1$s / %2$s) 正在加密檔案…… 正在傳送檔案 (%1$s / %2$s) - 正在下載檔案 %1$s…… 檔案 %1$s 已下載! - (已編輯) - - 訊息編輯 找不到編輯 - 過濾對話…… 找不到您要尋找的東西? 建立新聊天室 傳送新直接訊息 檢視聊天室目錄 - 名稱或 ID (#example:matrix.org) - 啟用滑動以在時間軸上回覆 - 連結已複製到剪貼簿 - 整合管理員 - 未設定整合管理員。 透過 matrix ID 新增 正在建立聊天室…… 找不到結果,在伺服器上使用透過 matrix ID 新增。 開始輸入以取得結果 透過使用者名稱或 ID 過濾…… - 正在加入聊天室…… - 檢視編輯歷史 - 審閱 婉拒 - 要繼續,您必須接受此服務的條款。 - 服務條款 審閱條款 可被其他人探索 使用機器人、橋接、小工具與貼紙包 - 閱讀於 - 撤銷 斷線 未設定身份識別伺服器。 - 因為錯誤設定的伺服器導致通話失敗 請要求您家伺服器 (%1$s) 的管理員設定 TURN 伺服器以讓通話正常運作。 \n \n或者您也可以試用看看位於 %2$s 的公開伺服器,但這可能不太可靠,而且也會將您的 IP 位置與伺服器分享。您也可以在設定中管理這個。 嘗試使用 %s 不要再問我 - 設定電子郵件以供帳號復原,然後就可以讓認識您的人選擇性探索到您。 設定電話,然後就可以讓認識您的人選擇性探索到您。 設定電子郵件以供帳號復原。然後就可以讓認識您的人用電子郵件或電話選擇性探索到您。 @@ -1549,8 +1268,6 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 無背景同步 當應用程式在背景時,您將不會收到訊息通知。 更新設定失敗。 - - 偏好同步間隔 %s \n取決於資源(電池)或裝置狀態(休眠),同步可能會被延遲。 @@ -1560,9 +1277,7 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 工作階段的公開名稱對您通訊的對象可見 您未使用任何身份識別伺服器 未設定身份識別伺服器,這在重設您的密碼時是必要的。 - 看起來您正在嘗試連線到其它家伺服器。您想要登出嗎? - 身份識別伺服器 取消連線到身份識別伺服器 設定身份識別伺服器 @@ -1576,27 +1291,19 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 可探索的電話號碼 我們將會傳送確認電子郵件到 %s 給您,請檢查您的電子郵件並在確認連結上點選 擱置中 - 輸入身份識別伺服器 URL 無法連線到身份識別伺服器 請輸入身份識別伺服器 URL 身份識別伺服器無服務條款 您選擇的身份識別伺服器沒有任何服務條款。僅在您信任服務擁有者時才繼續 文字訊息已傳送給 %s。請輸入其中包含的驗證碼。 - 您目前正在身份識別伺服器 %1$s 上分享電子郵件地址或電話號碼。您將必須重新連線到 %2$s 以停止分享它們。 同意身份識別伺服器 (%s) 的服務條款以允許您被透過電子郵件地址或電話號碼探索。 - 啟用詳細紀錄。 詳細紀錄可以協助開發者在您傳送憤怒搖晃時取得更多紀錄。即使啟用這個設定,應用程式依然不會紀錄訊息內容或任何個人資料。 - - 請在您接受您家伺服器的條款與條件前繼續重試。 - 看起來伺服器回應時間似乎太久了,這可能是不良的網路連線或伺服器錯誤所造成。請稍後再試。 - 傳送附件 - 開啟導航選單 開啟建立聊天室選單 關閉建立聊天室選單…… @@ -1606,16 +1313,13 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 顯示密碼 隱藏密碼 跳到底部 - %1$s、%2$s 與 %3$s 已閱讀 %1$s 與 %2$s 已閱讀 %s 已閱讀 %d 個使用者已閱讀 - 檔案「%1$s」(%2$s) 太大無法上傳。限制為 %3$s。 - 在擷取附件時遇到錯誤。 檔案 聯絡人 @@ -1624,7 +1328,6 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 相簿 貼圖 無法處理分享資料 - 垃圾訊息 不合適 自訂回報…… @@ -1632,33 +1335,26 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 回報此內容的理由 回報 忽略使用者 - 內容已回報 - 此內容已回報。 -\n -\n如果您不想要看到從此使用者而來的更多內容,您可以封鎖他以隱藏他的訊息 + 此內容已回報。 +\n +\n如果您不想要看到從此使用者而來的更多內容,您可以忽略他們以隱藏他們的訊息。 回報為垃圾訊息 - 此內容已被回報為垃圾訊息。 -\n -\n如果您不想要看到從此使用者而來的更多內容,您可以封鎖他以隱藏他的訊息 + 此內容已被回報為垃圾訊息。 +\n +\n如果您不想要看到從此使用者而來的更多內容,您可以忽略他們以隱藏他們的訊息。 回報為不合適 - 此內容已被回報為不合適。 -\n -\n如果您不想要看到從此使用者而來的更多內容,您可以封鎖他以隱藏他的訊息 - + 此內容已被回報為不合適。 +\n +\n如果您不想要看到從此使用者而來的更多內容,您可以忽略他們以隱藏他們的訊息。 Element 需要權限以在磁碟上儲存您的 E2E 金鑰。 \n \n請在下個彈出視窗中允許存取以讓您可以手動匯出您的金鑰。 - 目前沒有網路連線 - Latn - 確認您的密碼 您無法在行動裝置上的 Element 做這件事 需要驗證 - - 整合 使用整合管理員以管理機器人、橋接、小工具與貼紙包。 \n整合管理員可以代表您接收設定資料,調整小工具、傳送聊天室邀請並設定權力等級。 @@ -1673,25 +1369,20 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 重新載入小工具 在瀏覽器中開啟 撤銷我的存取權限 - 您的顯示名稱 您的大頭貼 URL 您的使用者 ID 您的佈景主題 小工具 ID 聊天室 ID - - 此小工具想要使用下列資源: 允許 阻擋所有 使用相機 使用麥克風 讀取 DRM 保護的媒體 - 這不是有效的 Matrix 伺服器位置 忽略使用者 - 所有訊息(吵雜) 所有訊息 僅提及 @@ -1702,22 +1393,16 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 傳送為擾亂者指定的訊息 擾亂者 輸入關鍵字以尋找反應。 - 您未忽略任何使用者 - 長按聊天室以檢視更多選項 - - %1$s 將聊天室設為公開給所有知道連結的人。 %1$s 將聊天室設為僅邀請可進入。 未讀訊息 - 這是您的通訊。您才是所有者。 直接或在群組中與夥伴們聊天 透過加密讓對話保持隱密 擴展並自訂您的體驗 開始 - 選取伺服器 就像電子郵件,帳號也有一個家,不過您還是可以跟任何人交談 在最大的公開伺服器上免費加入數百萬人之中 @@ -1725,7 +1410,6 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 了解更多 其他 自訂與進階設定 - 繼續 連線到 %1$s 連線到 Element Matrix Services @@ -1734,13 +1418,11 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 註冊 登入 以 SSO 繼續 - Element Matrix Services 位置 位置 組織有專業主機 輸入 Modular Element 或您想要使用的伺服器位置 輸入您想要連線的伺服器或 Element 的位置 - 載入頁面時發生錯誤:%1$s (%2$d) 應用程式無法登入此家伺服器。家伺服器支援以下登入類型:%1$s。 \n @@ -1749,58 +1431,46 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 應用程式無法在此家伺服器上建立帳號。 \n \n您想要使用網路客戶端註冊嗎? - 此電子郵件未關聯到任何帳號。 - 在 %1$s 上重設密碼 驗證郵件已傳送到您的收件匣以確認您要設定新密碼。 下一步 電子郵件 新密碼 - 警告! 變更您的密碼將會重設在您所有工作階段中任何的端到端加密金鑰,讓已加密的聊天歷史無法讀取。請在重設您的密碼前從其他工作階段設定金鑰備份或匯出您的聊天室金鑰。 繼續 - 此電子郵件未被連結到任何帳號 - 檢查您的收件匣 驗證電子郵件已傳送至 %1$s。 輕點連結以確認您的新密碼。在您使用了其中包含的連結後,請點擊下方。 我已經驗證了我的電子郵件地址 - 成功! 您的密碼已被重設。 您已登出所有工作階段,且不會再收到推播通知。要重新啟用通知,請在裝置上再次登入。 返回登入 - 警告 您的密碼未變更。 \n \n停止密碼變更流程? - 設定電子郵件地址 設定電子郵件以復原您的帳號。之後您也可以選擇性地讓您認識的人透過您的電子郵件找到您。 電子郵件 電子郵件(選擇性) 下一個 - 設定電話號碼 設定電話號碼以讓您認識的人找到您。 請使用國際格式。 電話號碼 電話號碼(選擇性) 下一個 - 確認電話號碼 我們剛傳送了驗證碼給 %1$s。在下方輸入以驗證是您。 輸入驗證碼 再次傳送 下一個 - 國際電話號碼必須以加號開頭 電話號碼似乎無效。請檢查 - 註冊至 %1$s 使用者名稱或電子郵件 密碼 @@ -1810,26 +1480,21 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 尚未建立您的帳號。 \n \n停止註冊程序? - 選取 matrix.org 選取 Element Matrix Services 選取自訂的家伺服器 請執行 captcha 挑戰 接受條款以繼續 - 請檢查您的電子郵件 我們剛傳送電子郵件給 %1$s。 \n請點擊其中所包含的連結以繼續建立帳號。 輸入的驗證碼不正確。請檢查。 未更新的家伺服器 此家伺服器所執行的版本太舊,所以無法連線。請要求您的家伺服器管理員升級。 - 傳送了太多請求。您可以在 %1$d 秒後重試…… - 檢視由 - 您已登出 這可能有多種原因: \n @@ -1839,7 +1504,6 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 \n \n• 您伺服器的管理員為了安全性讓您的存取無效。 再次登入 - 您已登出 登入 您的家伺服器 (%1$s) 管理員已將您的帳號 %2$s (%3$s) 登出。 @@ -1851,7 +1515,6 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 \n \n如果您已不想使用此裝置,請將其清除,或是登入到其他帳號。 清除所有資料 - 清除資料 清除目前儲存在此裝置上的所有資料嗎? \n再次登入以存取您的帳號資料與訊息。 @@ -1859,12 +1522,9 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 清除資料 使用者 %1$s 目前的工作階段與您提供的使用者 %2$s 憑證。Element 並不支援。 \n請先清除您的資料,然後再以其他帳號登入。 - 您的 matrix.to 連結格式錯誤 描述太短了 - 初始化同步…… - 檢視我的所有工作階段 進階設定 開發者模式 @@ -1876,25 +1536,19 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 設定 目前的工作階段 其他工作階段 - 僅顯示第一個結果,輸入更多字母…… - 快速失敗 在發生非預期的錯誤時,Element 可能更常當機 "將 ¯\\_(ツ)_/¯ 附加到純文字訊息中" - 啟用加密 加密一旦啟用就無法停用。 - 您的電子郵件網域無法在此伺服器上註冊 - 未受信任的登入 它們相符 它們不相符 透過驗證以下出現在他們的畫面上獨一無二的顏文字是否有相同的順序來驗證使用者。 為了得到最佳的安全性,使用其他受信任的通訊方式或面對面來進行。 尋找綠色盾牌以確保使用者受信任。信任所有聊天室中的使用者來確保聊天室是安全的。 - 不安全 以下的其中一項可能會受到威脅: \n @@ -1902,12 +1556,10 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 \n- 您正在驗證的使用者連線的家伺服器 \n- 您的或其他使用者的網際網路連線 \n- 您的或其他使用者的裝置 - 影片。 圖片。 音訊 檔案 - 正在等待…… %s 已取消 您已取消 @@ -1915,25 +1567,17 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 您已接受 已傳送驗證 驗證請求 - - 驗證此工作階段 手動驗證 - - 掃描其他使用者裝置的條碼以安全地相互驗證 掃描他們的條碼 無法掃描 如果您無法面對面進行,請用比較顏文字代替 - 透過比較顏文字驗證 - 透過顏文字驗證 如果您無法掃描上面的條碼,請比較一小段獨一無二的顏文字來驗證。 - QR code 圖片 - 驗證 %s 已驗證 %s 正在等待驗證 %s…… @@ -1955,54 +1599,38 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 上傳 離開聊天室 正在離開聊天室…… - 管理員 板主 自訂 邀請 使用者 - %1$s 中的管理員 %1$s 中的板主 %2$s 中的自訂 (%1$d) - 跳至讀取回條 - Element 無法處理類型為「%1$s」的事件 Element 無法處理類型為「%1$s」的訊息 在彩現 id「%1$s」事件的內容時,Element 遇到問題 - 取消忽略 - 此工作階段無法與您其他的工作階段分享此驗證。 \n驗證將會儲存在本機並在未來版本的應用程式中共享。 - 最近的聊天室 其他聊天室 - 將指定的訊息以彩虹的方式上色後傳送 將指定的表情符號以彩虹的方式上色後傳送 - 時間軸 - 訊息編輯器 - 啟用端到端加密 一旦啟用加密就無法停用。 - 啟用加密? 一旦啟用對聊天室的加密就無法停用。傳送到已加密聊天室的訊息無法被伺服器看見,僅有聊天室的參與者可見。啟用加密可能會讓許多機器人與橋接無法運作。 啟用加密 - 要確定安全,請透過一次性的代碼驗證 %s。 要確定安全,請面對面進行或使用其他方式來通訊。 - 比較獨一無二的顏文字,並確保它們以相同的順序出現。 以其中一方的畫面上顯示的代碼與其他使用者的畫面比較。 與此使用者的訊息是端到端加密的,無法被第三方讀取。 您的新工作階段已被驗證。其已存取您已加密的訊息,其他使用者也會看到其已受信任。 - - 交叉簽署 交叉簽署已啟用 \n私鑰在裝置上。 @@ -2012,52 +1640,36 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 交叉簽署已啟用。 \n金鑰未受信任 交叉簽署未啟用 - - 活躍的工作階段 顯示所有工作階段 管理工作階段 登出此工作階段 - 無可用的密碼學資訊 - 因為您已驗證此工作階段,所以其在安全通訊上是可受信任的: 驗證此工作階段以將其標記為受信任並讓其可以存取已加密的訊息。如果您並未登入此工作階段,您的帳號可能已被盜用: - %d 活躍的工作階段 - 驗證此登入 其他使用者可能不會信任它 全面的安全性 - 使用既有的工作階段來驗證這個,讓它可以存取已加密的訊息。 - - 驗證 已驗證 警告 - 取得工作階段失敗 工作階段 受信任 未受信任 - 因為 %1$s (%2$s) 已驗證此工作階段,所以其在安全通訊上是可受信任的: %1$s (%2$s) 使用新的工作階段登入: 直到此使用者信任此工作階段為止,傳送到該工作階段與從它傳送的訊息都會以警告標記。或者您可以手動進行驗證。 - - 初始化交叉簽署 重設金鑰 - QR code - 就快完成了!%s 有顯示相同的盾牌嗎? - 到伺服器的連線已遺失 使用者名稱 開發者工具 @@ -2072,48 +1684,35 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 建立簡易投票 使用復原通關密語或金鑰 如果您無法存取既有的工作階段的話 - 新登入 - 在儲存空間中找不到秘密 輸入秘密儲存空間通關密語 警告: 您僅能從受信任的裝置存取秘密儲存空間 - 移除…… 您想要傳送此附件到 %1$s 嗎? 使用原始大小傳送圖片 - 確認移除 您確定您想要移除(刪除)此活動嗎?注意,如果您刪除聊天室名稱或變更主題,則可能會撤銷變更。 包含理由 修改原因 - 被使用者刪除的活動,理由:%1$s 由聊天室管理員管理的活動,理由:%1$s - 金鑰已為最新! - Element Android - 金鑰請求 - 解鎖已加密的訊息歷史 - 重新整理 - 新登入。是您嗎? 輕觸即可以審閱並驗證 使用此工作階段來驗證新的,讓它可以存取已加密的訊息。 這不是我 您的帳號可能已被盜用 - 如果您取消,您可能無法在此裝置上讀取已加密的訊息,而其他使用者也不會信任它 如果您取消,您可能無法在您新的裝置上讀取已加密的訊息,而其他使用者也不會信任它 如果您現在取消,您將無法驗證 %1$s (%2$s)。在他們的使用者檔案中重新開始。 - 下列其中一項可能已被盜用: \n \n- 您的密碼 @@ -2122,35 +1721,25 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 \n- 任一裝置正在使用的網際網路連線 \n \n我們建議您立刻在設定中變更您的密碼與復原金鑰。 - 透過設定驗證您的裝置。 驗證已取消 - 復原通關密語 訊息金鑰 帳號密碼 - 設定 %s 生成訊息金鑰 - 確認 %s - 輸入您的 %s 以繼續。 - 保護與解鎖已加密的訊息並信任 %s。 再次輸入您的 %s 以確認。 不要重用您的帳號密碼。 - - 這可能需要數秒,請稍候。 設定復原。 您的復原金鑰 已完成! 把它放在安全的地方 完成 - 使用這個 %1$s 做為安全網以避免您忘記您的 %2$s。 - 正在發佈已建立的識別金鑰 正在從通關密語生成安全金鑰 正在定義 SSSS 預設金鑰 @@ -2158,37 +1747,26 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 正在同步使用者金鑰 正在同步自行簽署金鑰 設定金鑰備份 - - 您的 %2$s 與 %1$s 設定好了。 \n \n請保護它們的安全!如果您遺失所有作用中的工作階段,您將會需要它們來解鎖已加密的訊息並保護資訊。 - 列印並將其存放在安全的地方 將其儲存在 USB 隨身碟或備份磁碟上 將其複製到您的私人雲端儲存空間 - 您不能從行動裝置上做這件事 - 設定訊息密碼可讓您保護並解鎖已加密的訊息並信任。 \n \n如果您不想要設定訊息密碼,請生成訊息金鑰來代替。 設定復原通關密語可讓您保護並解鎖已加密的訊息並信任。 - - 加密已啟用 在此聊天室中的訊息已端到端加密。取得更多資訊並在使用者的個人檔案中驗證他們。 加密未啟用 用於此聊天室的加密未受支援 - %s 建立並設定聊天室。 - 就快完成了!其他裝置有顯示相同的盾牌嗎? 就快完成了!正在等待確認…… 正在等待 %s…… - 匯入金鑰失敗 - 通知設定 訊息包含 @room 在一對一的聊天中已加密的訊息 @@ -2196,24 +1774,17 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 當聊天室升級時 疑難排解 按事件設定通知重要性 - 傳送純文字訊息,不將它們解譯為 markdown - 不正確的使用者名稱及/或密碼。輸入的密碼以空格開頭或結尾,請檢查。 - 訊息…… - 提供加密升級 驗證您自己與其他人以保證聊天安全 - 輸入您的 %s 以繼續 使用檔案 - 輸入 %s 復源通關密語 這不是有效的復原金鑰 請輸入復原金鑰 - 正在檢查備份金鑰 正在檢查備份金鑰 (%s) 正在取得曲線金鑰 @@ -2222,19 +1793,15 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 正在從復原金鑰生成 SSSS 金鑰 正在 SSSS 中儲存金鑰備份秘密 %1$s (%2$s) - 輸入您的金鑰備份通關密語以繼續。 使用您的金鑰備份復原金鑰 不知道您的金鑰備份通關密語,您可以 %s。 金鑰備份復原金鑰 - 避免對應用程式進行螢幕截圖 啟用此設定會新增 FLAG_SECURE 到所有活動。重新啟動應用程式以讓變動生效。 - 媒體檔案已新增至媒體庫中 無法新增媒體檔案到媒體庫中 設定新的帳號密碼…… - 在您的其他裝置上使用最新的 Element、Element Web、Element 桌面版、Element iOS、Element for Android 或其他有交叉簽章功能的 Matrix 客戶端 Element Web \nElement 桌面版 @@ -2249,30 +1816,25 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 選取您的復原金鑰,或是透過打字或從您的剪貼簿貼上來手動輸入 無法使用此復原金鑰解密備份:請驗證您是否輸入了正確的復原金鑰。 存取安全儲存空間失敗 - 透過文字手動驗證 驗證登入 透過顏文字來進行互動驗證 從您的其他工作階段驗證此登入以確認您的身份並授予存取加密訊息的權限。 標記為受信任 - 請選擇使用者名稱。 請選擇密碼。 仔細檢查此連結 連結 %1$s 正在將您帶往其他網站:%2$s。 \n \n您確定您想要繼續嗎? - 我們無法建立您的直接訊息。請檢查您想要邀請的使用者,然後再試一次。 未加密 由未驗證的裝置加密 審閱您從何處登入 驗證您所有的工作階段以確保您的帳號與訊息都安全 驗證正在存取您帳號的新登入:%1$s - %1$s:%2$s %1$s:%2$s %3$s - 新增成員 邀請 正在邀請使用者…… @@ -2283,19 +1845,16 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 邀請已傳送給 %1$s 與其他 %2$d 個人 我們無法邀請使用者。請檢查您想要邀請的使用者並再試一次。 - 訊息已刪除 顯示已移除的訊息 為已移除的訊息顯示佔位符 我們已傳送確認電子郵件給 %s,請先檢查您的電子郵件並點擊確認連結 驗證代碼不正確。 - 媒體 此聊天室中沒有媒體 檔案 %1$s 在 %2$s 此聊天室中沒有檔案 - 或者如果您已有帳號,而且您也知道您 Matrix 的識別符與密碼,您可以使用此方式: 使用我的 Matrix 識別符登入 登入 @@ -2303,13 +1862,10 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 使用者識別符 這不是有效的使用者識別符。預期中的格式:\'@user:homeserver.org\' 找不到有效的家伺服器。請檢查您的識別符 - 飛航模式開啟 - 目前語言 其他可用的語言 正在載入可用的語言…… - 開啟 %s 的條款 從身份識別伺服器 %s 斷線? 此身份識別伺服器太舊了。Element 僅支援 API V2。 @@ -2319,26 +1875,20 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 為了保護您的隱私,Element 僅支援傳送雜湊過的使用者電子郵件與電話號碼。 關聯失敗。 目前沒有此識別符的關聯。 - 您的家伺服器 (%1$s) 建議將 %2$s 用於您的身份識別伺服器 使用 %1$s 或者您可以輸入任何身份識別伺服器 URL 輸入身份識別伺服器 URL 遞交 - 播放 暫停 忽略 - - 複製 成功 - 通知 Element 呼叫失敗 建立即時連線失敗。 \n請要求您家伺服器的管理員設定 TURN 伺服器以讓通話的運作更可靠。 - 選取音效裝置 電話 喇叭 @@ -2349,18 +1899,14 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 關閉 HD 開啟 HD - SSL 錯誤:對方的身份未驗證。 SSL 錯誤。 有效通話 (%s) 回到通話 - 取消邀請 將您自己降級? 您將無法復原此變更,因為您要把自己降級,如果您是聊天室中最後一個有此權限的使用者,那將無法重新取得權限。 降級 - - 忽略使用者 忽略此使用者將會從您分享的聊天室移除他們的訊息。 \n @@ -2378,7 +1924,6 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 封鎖的理由 取消封鎖使用者 取消封鎖使用者將會再次允許他們加入聊天室。 - 安全備份 管理 設定安全備份 @@ -2387,53 +1932,36 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 透過備份加密金鑰到您的伺服器上以防止遺失對加密訊息與資料的存取權。 為您現有的備份生成新的安全金鑰或設定新的安全密語。 這將會取代您目前的金鑰或密語。 - 整合被停用 在設定中啟用「允許整合」以執行此動作。 - %d 封鎖了使用者 - 金鑰已成功匯出 - 檢視 作用中的小工具 - - 復原金鑰已儲存。 - 安全備份 防止遺失對加密訊息與資料的存取權 - 設定安全備份 - - 合併未能在時間軸中解密訊息 在主畫面上新增專用的未讀通知分頁。 - 新增到最愛 從最愛移除 您未做出變更 您讓聊天室對所有知道連結的人公開。 您讓聊天室變為僅邀請可加入。 輸入您想要使用的伺服器地址 - 如果您不知道您的密碼,請返回並重設。 貼圖 - 管理動作 在 %1$s 中的預設 您的伺服器管理員已在私人聊天室與直接訊息中預設停用端到端加密。 輸入只有您知道的安全密語,用於保護您伺服器上的安全祕密。 - 如果您現在取消,您可能會失去對加密訊息與資料的存取權,如果您失去對您登入的存取權的話。 \n \n您也可以在設定中設定安全備份與管理您的金鑰。 - 您建立並設定了聊天室。 - 此帳號已被停用。 - 啟用交叉簽章 無法儲存媒體檔案 設定角色 @@ -2443,9 +1971,7 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 取消麥克風靜音 停止相機 開啟相機 - 設定安全備份 - 安全備份 透過備份加密金鑰到您的伺服器上以防止遺失對加密訊息與資料的存取權。 設定 @@ -2453,22 +1979,17 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 生成安全金鑰並儲存在安全的地方,如密碼管理員或保險櫃。 使用安全密語 輸入只有您知道的密語,並生成金鑰備份。 - 儲存您的安全金鑰 將您的安全金鑰儲存在安全的地方,如密碼管理員或保險櫃。 - 設定安全密語 輸入僅有您知道的安全密語,用於保護在您伺服器上的安全祕密。 安全密語 再次輸入您的安全密語以確認。 - 儲存您的安全金鑰 將您的安全金鑰儲存在安全的地方,如密碼管理員或保險櫃。 - 聊天室設定 主題 您成功變更了聊天室設定 - 您無法存取此訊息 正在等待此訊息,可能需要花一點時間 無法解密 @@ -2477,17 +1998,12 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 您無法存取此訊息,因為您的工作階段未被傳送者所信任 您無法存取此訊息,因為傳送者刻意未傳送金鑰 正在等待加密歷史 - Riot 現在是 Element 了! 我們很高興地我們已變更名稱!您的應用程式是最新的,而您也登入了您的帳號。 知道了 取得更多資訊 - element - - 儲存復原金鑰於 - 從我的電話簿新增 您的電話簿是空的 電話簿 @@ -2495,13 +2011,10 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 正在擷取您的聯絡人…… 您的通訊錄為空 通訊錄 - 撤銷邀請 撤銷對 %1$s 的邀請? - 被 %1$s 封鎖 取消封鎖使用者失敗 - 推送通知已停用 審閱您的設定以啟用推送通知 選擇 PIN 碼以確保安全 @@ -2529,13 +2042,11 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 %1$d/%2$d 金鑰匯入成功。 - 管理整合 無作用中的小工具 已建立聊天室,但因為以下理由而未傳送某些邀請: \n \n%s - %1$s, %2$s 與 %3$d 個其他人已讀取 @@ -2551,27 +2062,77 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意 電話號碼 移除 %s? 確保您已經點擊我們傳送給您的電子郵件中的連結。 - 電子郵件與電話號碼 管理連結到您 Matrix 帳號的電子郵件與電話號碼 - 代碼 請使用國際格式(電話號碼必須以 \'+\' 開頭) 透過確認此登入來驗證您的身份,以及授予存取加密訊息的權限。 抱歉,對於使用單一登入的帳號,還無法執行此動作。 - 無法開啟禁止您進入的聊天室。 找不到此聊天室。請確定它存在。 %d 秒 - 顯示聊天室成員狀態活動 包含邀請/加入/離開/踢除/封鎖事件與大頭貼/顯示名稱變更等。 投票 機器人按鈕 反應:%s 驗證結論 - 連結格式錯誤 - + 您沒有在此聊天室開始通話的權限 + 刪除類型為 %1$s 的帳號資料? +\n +\n小心使用,它可能會導致意料之外的行為。 + 每次打開 Element 時都需要 PIN 碼。 + 未使用 Element 2分鐘後,要求輸入 PIN 碼。 + 2分鐘後要求輸入 PIN 碼 + 在簡易通知中僅顯示未讀訊息數量。 + 顯示如聊天是名稱與訊息內容等詳細資訊。 + 在通知中顯示內容 + PIN 碼是解鎖 Element 的唯一方式。 + 啟用特定裝置的生物識別技術,如指紋與臉部辨識。 + 設定保護 + 使用 PIN 碼與生物識別技術來保護存取權。 + 保護存取權 + + 顯示您現在可以驗證的 %s 個裝置 + + 您將會重新啟動,沒有歷史紀錄,已信任的裝置或已信任的使用者 + 如果您重設了所有東西 + 僅在您沒有其他裝置可以驗證此裝置時才使用這個。 + 重設所有東西 + 忘記或遺失所有復原選項?重設所有東西 + 您已加入。 + %s 已加入。 + 此聊天適中的訊息有端到端加密。 + 離開 + 設定 + 這裡的訊息有端到端加密。 +\n +\n您的訊息已使用鎖來保護,只有您與收件者才有獨特的金鑰來解鎖他們。 + 這裡的訊息沒有端到端加密。 + 此家伺服器正在執行較舊的版本。請要求您的家伺服器管理原升級。您可以繼續,但某些功能可能無法正常運作。 + 您讓此變為僅邀請。 + %1$s 讓此變為僅邀請。 + 在已加密的聊天適中顯示完整歷史紀錄 + %1$s 與 %2$s + %1$s 在 %2$s 與 %3$s + + %d 個邀請 + + 通知已被點擊! + 請點擊通知。如果您沒有看到通知,請檢查系統設定。 + 通知顯示 + 您正在檢視通知!點擊我! + 接收推播失敗。解決方法可能是重新安裝應用程式。 + 應用程式正在接收 PUSH + 應用程式正在等待 PUSH + 測試推播 + 目前尚不支援在已加密的聊天室中搜尋。 + 過濾被封鎖的使用者 + 您沒有開始通話的權限 + 您沒有開始會議通話的權限 + 重設 + 啟用生物識別技術 + \ No newline at end of file diff --git a/vector/src/main/res/values/attrs.xml b/vector/src/main/res/values/attrs.xml index 8c71fb26b2..55a4f9a84e 100644 --- a/vector/src/main/res/values/attrs.xml +++ b/vector/src/main/res/values/attrs.xml @@ -52,6 +52,7 @@ + @@ -59,7 +60,7 @@ - + diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index a5d9bae0ae..ec916d1daf 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -78,6 +78,7 @@ Play Pause Dismiss + Reset @@ -89,7 +90,9 @@ "Due to missing permissions, this action is not possible. You need permission to invite to start a conference in this room You do not have permission to start a conference call in this room + You do not have permission to start a conference call You do not have permission to start a call in this room + You do not have permission to start a call A conference is already in progress! Start video meeting Start audio meeting @@ -620,11 +623,13 @@ Search Filter room members + Filter banned users No results ROOMS MESSAGES PEOPLE FILES + Searching in encrypted rooms is not supported yet. JOIN @@ -661,7 +666,7 @@ Direct Chat Leave Conversation Forget - Add Homescreen Shortcut + Add to Home screen Messages @@ -744,6 +749,15 @@ FCM token successfully registered to HomeServer. Failed to register FCM token to HomeServer:\n%1$s + Test Push + The application is waiting for the PUSH + The application is receiving PUSH + Failed to receive push. Solution could be to reinstall the application. + You are viewing the notification! Click me! + Notification Display + Please click on the notification. If you do not see the notification, please check the system settings. + The notification has been clicked! + Notifications Service Notifications Service is running. Notifications Service is not running.\nTry to restart the application. @@ -1708,6 +1722,7 @@ The suggestion failed to be sent (%s) Show hidden events in timeline + "Show complete history in encrypted rooms" Direct Messages @@ -1735,7 +1750,6 @@ Name or ID (#example:matrix.org) Enable swipe to reply in timeline - Merge failed to decrypt message in timeline Add a dedicated tab for unread notifications on main screen. Link copied to clipboard @@ -1846,11 +1860,11 @@ "IGNORE USER" "Content reported" - "This content was reported.\n\nIf you don't want to see any more content from this user, you can block him to hide his messages" + "This content was reported.\n\nIf you don't want to see any more content from this user, you can ignore them to hide their messages." "Reported as spam" - "This content was reported as spam.\n\nIf you don't want to see any more content from this user, you can block him to hide his messages" + "This content was reported as spam.\n\nIf you don't want to see any more content from this user, you can ignore them to hide their messages." "Reported as inappropriate" - "This content was reported as inappropriate.\n\nIf you don't want to see any more content from this user, you can block him to hide his messages" + "This content was reported as inappropriate.\n\nIf you don't want to see any more content from this user, you can ignore them to hide their messages." Element needs permission to save your E2E keys on disk.\n\nPlease allow access on the next pop-up to be able to export your keys manually. @@ -1881,6 +1895,8 @@ You made the room public to whoever knows the link. %1$s made the room invite only. You made the room invite only. + %1$s made this invite only. + You made this invite only. Unread messages It\'s your conversation. Own it. @@ -2113,12 +2129,15 @@ Waiting for %s… For extra security, verify %s by checking a one-time code on both your devices.\n\nFor maximum security, do this in person. Messages in this room are not end-to-end encrypted. + Messages here are not end-to-end encrypted. Messages in this room are end-to-end encrypted.\n\nYour messages are secured with locks and only you and the recipient have the unique keys to unlock them. + Messages here are end-to-end encrypted.\n\nYour messages are secured with locks and only you and the recipient have the unique keys to unlock them. Security Learn more More Admin Actions Room settings + Settings Notifications "One person" @@ -2126,6 +2145,7 @@ Uploads Leave Room + Leave "Leaving the room…" Admins @@ -2355,11 +2375,14 @@ Encryption enabled Messages in this room are end-to-end encrypted. Learn more & verify users in their profile. + Messages in this room are end-to-end encrypted. Encryption not enabled The encryption used by this room is not supported %s created and configured the room. You created and configured the room. + %s joined. + You joined. Almost there! Is the other device showing the same shield? Almost there! Waiting for confirmation… @@ -2434,6 +2457,15 @@ Select your Recovery Key, or input it manually by typing it or pasting from your clipboard Backup could not be decrypted with this Recovery Key: please verify that you entered the correct Recovery Key. Failed to access secure storage + Forgot or lost all recovery options? Reset everything + Reset everything + Only do this if you have no other device you can verify this device with. + If you reset everything + You will restart with no history, no messages, trusted devices or trusted users + + Show the device you can verify with now + Show %d devices you can verify with now + Unencrypted Encrypted by an unverified device diff --git a/vector/src/main/res/values/styles_login.xml b/vector/src/main/res/values/styles_login.xml index 024a69f64a..8d2e969738 100644 --- a/vector/src/main/res/values/styles_login.xml +++ b/vector/src/main/res/values/styles_login.xml @@ -1,13 +1,6 @@ - - diff --git a/vector/src/main/res/xml/vector_settings_advanced_settings.xml b/vector/src/main/res/xml/vector_settings_advanced_settings.xml index 41255d2d2d..1cb93fcb31 100644 --- a/vector/src/main/res/xml/vector_settings_advanced_settings.xml +++ b/vector/src/main/res/xml/vector_settings_advanced_settings.xml @@ -16,6 +16,12 @@ android:key="SETTINGS_LABS_SHOW_HIDDEN_EVENTS_PREFERENCE_KEY" android:title="@string/settings_labs_show_hidden_events_in_timeline" /> + + - - - - + + + + + + \ No newline at end of file diff --git a/vector/src/test/java/im/vector/app/features/home/room/detail/composer/rainbow/RainbowGeneratorTest.kt b/vector/src/test/java/im/vector/app/features/home/room/detail/composer/rainbow/RainbowGeneratorTest.kt index 0e46d67860..20b041fd1e 100644 --- a/vector/src/test/java/im/vector/app/features/home/room/detail/composer/rainbow/RainbowGeneratorTest.kt +++ b/vector/src/test/java/im/vector/app/features/home/room/detail/composer/rainbow/RainbowGeneratorTest.kt @@ -32,14 +32,14 @@ class RainbowGeneratorTest { @Test fun testAscii1() { - assertEquals("""a""", rainbowGenerator.generate("a")) + assertEquals("""a""", rainbowGenerator.generate("a")) } @Test fun testAscii2() { val expected = """ - a - b + a + b """.trimIndentOneLine() assertEquals(expected, rainbowGenerator.generate("ab")) @@ -48,24 +48,24 @@ class RainbowGeneratorTest { @Test fun testAscii3() { val expected = """ - T - h - i - s + T + h + i + s - i - s + i + s - a + a - r - a - i - n - b - o - w - ! + r + a + i + n + b + o + w + ! """.trimIndentOneLine() assertEquals(expected, rainbowGenerator.generate("This is a rainbow!")) @@ -73,19 +73,19 @@ class RainbowGeneratorTest { @Test fun testEmoji1() { - assertEquals("""🤞""", rainbowGenerator.generate("\uD83E\uDD1E")) // 🤞 + assertEquals("""🤞""", rainbowGenerator.generate("\uD83E\uDD1E")) // 🤞 } @Test fun testEmoji2() { - assertEquals("""🤞""", rainbowGenerator.generate("🤞")) + assertEquals("""🤞""", rainbowGenerator.generate("🤞")) } @Test fun testEmoji3() { val expected = """ - 🤞 - 🙂 + 🤞 + 🙂 """.trimIndentOneLine() assertEquals(expected, rainbowGenerator.generate("🤞🙂")) @@ -94,20 +94,20 @@ class RainbowGeneratorTest { @Test fun testEmojiMix1() { val expected = """ - H - e - l - l - o + H + e + l + l + o - 🤞 + 🤞 - w - o - r - l - d - ! + w + o + r + l + d + ! """.trimIndentOneLine() assertEquals(expected, rainbowGenerator.generate("Hello 🤞 world!")) @@ -116,8 +116,8 @@ class RainbowGeneratorTest { @Test fun testEmojiMix2() { val expected = """ - a - 🤞 + a + 🤞 """.trimIndentOneLine() assertEquals(expected, rainbowGenerator.generate("a🤞")) @@ -126,8 +126,8 @@ class RainbowGeneratorTest { @Test fun testEmojiMix3() { val expected = """ - 🤞 - a + 🤞 + a """.trimIndentOneLine() assertEquals(expected, rainbowGenerator.generate("🤞a")) @@ -135,6 +135,6 @@ class RainbowGeneratorTest { @Test fun testError1() { - assertEquals("\uD83E", rainbowGenerator.generate("\uD83E")) + assertEquals("\uD83E", rainbowGenerator.generate("\uD83E")) } }