diff --git a/.github/workflows/post-pr.yml b/.github/workflows/post-pr.yml
index 9cd33143ad..8fe51eb8d5 100644
--- a/.github/workflows/post-pr.yml
+++ b/.github/workflows/post-pr.yml
@@ -325,5 +325,5 @@ jobs:
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
hookshot_url: ${{ secrets.ELEMENT_ANDROID_HOOKSHOT_URL }}
- text_template: "Post-merge validation of ${{ github.head_ref }} into ${{ github.base_ref }} by ${{ github.event.pull_request.merged_by }} failed: {{#each job_statuses }}{{#with this }}{{#if completed }} {{name}} {{conclusion}} at {{completed_at}}, {{/if}}{{/with}}{{/each}}"
- html_template: "Post-merge validation of ${{ github.head_ref }} into ${{ github.base_ref }} by ${{ github.event.pull_request.merged_by }} failed: {{#each job_statuses }}{{#with this }}{{#if completed }} {{icon conclusion}} {{name}} {{conclusion}} at {{completed_at}} [details]{{/if}}{{/with}}{{/each}}"
+ text_template: "Post-merge validation of ${{ github.head_ref }} into ${{ github.base_ref }} by ${{ github.event.pull_request.merged_by.login }} failed: {{#each job_statuses }}{{#with this }}{{#if completed }} {{name}} {{conclusion}} at {{completed_at}}, {{/if}}{{/with}}{{/each}}"
+ html_template: "Post-merge validation of ${{ github.head_ref }} into ${{ github.base_ref }} by ${{ github.event.pull_request.merged_by.login }} failed: {{#each job_statuses }}{{#with this }}{{#if completed }} {{icon conclusion}} {{name}} {{conclusion}} at {{completed_at}} [details]{{/if}}{{/with}}{{/each}}"
diff --git a/CHANGES.md b/CHANGES.md
index 8e42149545..c8677b1ae4 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,44 @@
+Changes in Element v1.4.16 (2022-05-17)
+=======================================
+
+Features ✨
+----------
+ - Use key backup before requesting keys + refactor & improvement of key request/forward ([#5494](https://github.com/vector-im/element-android/issues/5494))
+ - Screen sharing over WebRTC ([#5911](https://github.com/vector-im/element-android/issues/5911))
+ - Allow using the latest user Avatar and name for all messages in the timeline ([#5932](https://github.com/vector-im/element-android/issues/5932))
+ - Added themed launch icons for Android 13 ([#5936](https://github.com/vector-im/element-android/issues/5936))
+ - Add presence indicator busy and away. ([#6047](https://github.com/vector-im/element-android/issues/6047))
+
+Bugfixes 🐛
+----------
+ - Changed copy and list order in member profile screen. ([#5825](https://github.com/vector-im/element-android/issues/5825))
+ - Fix for audio only being received in one direction after an un-hold during a sip call. ([#5865](https://github.com/vector-im/element-android/issues/5865))
+ - Desynchronized 4S | Megolm backup causing Unusable backup ([#5906](https://github.com/vector-im/element-android/issues/5906))
+ - If animations are disable on the System, chat effects and confetti will be disabled too ([#5941](https://github.com/vector-im/element-android/issues/5941))
+ - Multiple threads improvement (mainly UI) ([#5959](https://github.com/vector-im/element-android/issues/5959))
+
+Improved Documentation 📚
+------------------------
+ - Note public_baseurl requirement in integration tests documentation. ([#5973](https://github.com/vector-im/element-android/issues/5973))
+
+SDK API changes ⚠️
+------------------
+ - - New API to enable/disable key forwarding CryptoService#enableKeyGossiping()
+ - New API to limit room key request only to own devices MXCryptoConfig#limitRoomKeyRequestsToMyDevices
+ - Event Trail API has changed, now using AuditTrail events
+ - New API to manually accept an incoming key request CryptoService#manuallyAcceptRoomKeyRequest() ([#5559](https://github.com/vector-im/element-android/issues/5559))
+ - Small change in the Matrix class: deprecated methods have been removed and the constructor is now public. Also the fun `workerFactory()` has been renamed to `getWorkerFactory()` ([#5887](https://github.com/vector-im/element-android/issues/5887))
+ - Including SSL/TLS error handing when doing WellKnown lookups without a custom HomeServerConnectionConfig ([#5965](https://github.com/vector-im/element-android/issues/5965))
+
+Other changes
+-------------
+ - Improve threads rendering in the main timeline ([#5151](https://github.com/vector-im/element-android/issues/5151))
+ - Reformatted project code ([#5953](https://github.com/vector-im/element-android/issues/5953))
+ - Update check for server-side threads support to match spec. ([#5997](https://github.com/vector-im/element-android/issues/5997))
+ - Setup detekt ([#6038](https://github.com/vector-im/element-android/issues/6038))
+ - Notify the user for each new message ([#46312](https://github.com/vector-im/element-android/issues/46312))
+
+
Changes in Element v1.4.14 (2022-05-05)
=======================================
diff --git a/build.gradle b/build.gradle
index 8553428b49..25ff8b91c0 100644
--- a/build.gradle
+++ b/build.gradle
@@ -27,7 +27,7 @@ buildscript {
classpath 'com.google.gms:google-services:4.3.10'
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.3'
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.5'
- classpath "com.likethesalad.android:stem-plugin:2.0.0"
+ classpath "com.likethesalad.android:stem-plugin:2.1.1"
classpath 'org.owasp:dependency-check-gradle:7.1.0.1'
classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.6.21"
classpath "org.jetbrains.kotlinx:kotlinx-knit:0.4.0"
diff --git a/changelog.d/46312.misc b/changelog.d/46312.misc
deleted file mode 100644
index 5e0112372f..0000000000
--- a/changelog.d/46312.misc
+++ /dev/null
@@ -1 +0,0 @@
-Notify the user for each new message
diff --git a/changelog.d/5151.misc b/changelog.d/5151.misc
deleted file mode 100644
index b785c4229c..0000000000
--- a/changelog.d/5151.misc
+++ /dev/null
@@ -1 +0,0 @@
-Improve threads rendering in the main timeline
diff --git a/changelog.d/5494.feature b/changelog.d/5494.feature
deleted file mode 100644
index 59b8a78a2c..0000000000
--- a/changelog.d/5494.feature
+++ /dev/null
@@ -1 +0,0 @@
-Use key backup before requesting keys + refactor & improvement of key request/forward
\ No newline at end of file
diff --git a/changelog.d/5559.sdk b/changelog.d/5559.sdk
deleted file mode 100644
index 2466fcef48..0000000000
--- a/changelog.d/5559.sdk
+++ /dev/null
@@ -1,4 +0,0 @@
-- New API to enable/disable key forwarding CryptoService#enableKeyGossiping()
-- New API to limit room key request only to own devices MXCryptoConfig#limitRoomKeyRequestsToMyDevices
-- Event Trail API has changed, now using AuditTrail events
-- New API to manually accept an incoming key request CryptoService#manuallyAcceptRoomKeyRequest()
diff --git a/changelog.d/5658.feature b/changelog.d/5658.feature
new file mode 100644
index 0000000000..ba41a03207
--- /dev/null
+++ b/changelog.d/5658.feature
@@ -0,0 +1 @@
+Space explore screen changes: removed space card, added rooms filtering
diff --git a/changelog.d/5689.wip b/changelog.d/5689.wip
new file mode 100644
index 0000000000..ccea1ec541
--- /dev/null
+++ b/changelog.d/5689.wip
@@ -0,0 +1 @@
+[Live location sharing] Update message in timeline during the live
diff --git a/changelog.d/5724.sdk b/changelog.d/5724.sdk
new file mode 100644
index 0000000000..5a0a37fe31
--- /dev/null
+++ b/changelog.d/5724.sdk
@@ -0,0 +1 @@
+- Notifies other devices when a verification request sent from an Android device is accepted.`
diff --git a/changelog.d/5728.misc b/changelog.d/5728.misc
new file mode 100644
index 0000000000..6e463fa76f
--- /dev/null
+++ b/changelog.d/5728.misc
@@ -0,0 +1 @@
+leaving space experience changed to be aligned with iOS
diff --git a/changelog.d/5825.bugfix b/changelog.d/5825.bugfix
deleted file mode 100644
index 77560027ba..0000000000
--- a/changelog.d/5825.bugfix
+++ /dev/null
@@ -1 +0,0 @@
-Changed copy and list order in member profile screen.
\ No newline at end of file
diff --git a/changelog.d/5887.sdk b/changelog.d/5887.sdk
deleted file mode 100644
index 0f128938dd..0000000000
--- a/changelog.d/5887.sdk
+++ /dev/null
@@ -1 +0,0 @@
-Small change in the Matrix class: deprecated methods have been removed and the constructor is now public. Also the fun `workerFactory()` has been renamed to `getWorkerFactory()`
diff --git a/changelog.d/5906.bugfix b/changelog.d/5906.bugfix
deleted file mode 100644
index be1379c6e4..0000000000
--- a/changelog.d/5906.bugfix
+++ /dev/null
@@ -1 +0,0 @@
-Desynchronized 4S | Megolm backup causing Unusable backup
diff --git a/changelog.d/5911.feature b/changelog.d/5911.feature
deleted file mode 100644
index 368a3b4056..0000000000
--- a/changelog.d/5911.feature
+++ /dev/null
@@ -1 +0,0 @@
-Screen sharing over WebRTC
diff --git a/changelog.d/5932.feature b/changelog.d/5932.feature
deleted file mode 100644
index dcfc6615b0..0000000000
--- a/changelog.d/5932.feature
+++ /dev/null
@@ -1 +0,0 @@
-Allow using the latest user Avatar and name for all messages in the timeline
diff --git a/changelog.d/5936.feature b/changelog.d/5936.feature
deleted file mode 100644
index cbf14aaba1..0000000000
--- a/changelog.d/5936.feature
+++ /dev/null
@@ -1 +0,0 @@
-Added themed launch icons for Android 13
\ No newline at end of file
diff --git a/changelog.d/5941.bugfix b/changelog.d/5941.bugfix
deleted file mode 100644
index 0ea17668c6..0000000000
--- a/changelog.d/5941.bugfix
+++ /dev/null
@@ -1 +0,0 @@
-If animations are disable on the System, chat effects and confetti will be disabled too
diff --git a/changelog.d/5953.misc b/changelog.d/5953.misc
deleted file mode 100644
index a3ad5dae93..0000000000
--- a/changelog.d/5953.misc
+++ /dev/null
@@ -1 +0,0 @@
-Reformatted project code
diff --git a/changelog.d/5965.sdk b/changelog.d/5965.sdk
deleted file mode 100644
index 5bb6c3aac4..0000000000
--- a/changelog.d/5965.sdk
+++ /dev/null
@@ -1 +0,0 @@
-Including SSL/TLS error handing when doing WellKnown lookups without a custom HomeServerConnectionConfig
diff --git a/changelog.d/5973.doc b/changelog.d/5973.doc
deleted file mode 100644
index cd3b31dd21..0000000000
--- a/changelog.d/5973.doc
+++ /dev/null
@@ -1 +0,0 @@
-Note public_baseurl requirement in integration tests documentation.
diff --git a/changelog.d/5997.misc b/changelog.d/5997.misc
deleted file mode 100644
index 328f3c0079..0000000000
--- a/changelog.d/5997.misc
+++ /dev/null
@@ -1 +0,0 @@
-Update check for server-side threads support to match spec.
diff --git a/changelog.d/6025.misc b/changelog.d/6025.misc
new file mode 100644
index 0000000000..2900796792
--- /dev/null
+++ b/changelog.d/6025.misc
@@ -0,0 +1 @@
+@Ignore a number of tests that are currently failing in CI.
diff --git a/changelog.d/6032.bugfix b/changelog.d/6032.bugfix
new file mode 100644
index 0000000000..c20d7ddd08
--- /dev/null
+++ b/changelog.d/6032.bugfix
@@ -0,0 +1 @@
+Revert: Use member name instead of room name in DM creation item
diff --git a/changelog.d/6038.misc b/changelog.d/6038.misc
deleted file mode 100644
index 881aae5ca3..0000000000
--- a/changelog.d/6038.misc
+++ /dev/null
@@ -1 +0,0 @@
-Setup detekt
diff --git a/changelog.d/6041.misc b/changelog.d/6041.misc
new file mode 100644
index 0000000000..50378ea3fd
--- /dev/null
+++ b/changelog.d/6041.misc
@@ -0,0 +1 @@
+Remove ShortcutBadger lib and usage (it was dead code)
diff --git a/changelog.d/6047.feature b/changelog.d/6047.feature
deleted file mode 100644
index 59d37e21e9..0000000000
--- a/changelog.d/6047.feature
+++ /dev/null
@@ -1 +0,0 @@
-Add presence indicator busy and away.
diff --git a/changelog.d/6074.bugfix b/changelog.d/6074.bugfix
new file mode 100644
index 0000000000..692dce28d7
--- /dev/null
+++ b/changelog.d/6074.bugfix
@@ -0,0 +1 @@
+Poll refactoring with unit tests
diff --git a/changelog.d/6089.misc b/changelog.d/6089.misc
new file mode 100644
index 0000000000..19b951c1a3
--- /dev/null
+++ b/changelog.d/6089.misc
@@ -0,0 +1 @@
+Test: Ensure calling 'fail()' is not caught by the catch block
diff --git a/changelog.d/6095.bugfix b/changelog.d/6095.bugfix
new file mode 100644
index 0000000000..11110bfa08
--- /dev/null
+++ b/changelog.d/6095.bugfix
@@ -0,0 +1 @@
+Correct .well-known/matrix/client handling for server_names which include ports.
diff --git a/changelog.d/6098.feature b/changelog.d/6098.feature
new file mode 100644
index 0000000000..659da42094
--- /dev/null
+++ b/changelog.d/6098.feature
@@ -0,0 +1 @@
+Labs flag for enabling live location sharing
diff --git a/dependencies_groups.gradle b/dependencies_groups.gradle
index 76869fccf1..59cefe7e89 100644
--- a/dependencies_groups.gradle
+++ b/dependencies_groups.gradle
@@ -141,7 +141,6 @@ ext.groups = [
'jline',
'jp.wasabeef',
'junit',
- 'me.leolin',
'me.saket',
'net.bytebuddy',
'net.java',
diff --git a/fastlane/metadata/android/cs-CZ/changelogs/40104120.txt b/fastlane/metadata/android/cs-CZ/changelogs/40104120.txt
new file mode 100644
index 0000000000..7867646fe5
--- /dev/null
+++ b/fastlane/metadata/android/cs-CZ/changelogs/40104120.txt
@@ -0,0 +1,2 @@
+Hlavní změny v této verzi: Umožňuje uživatelům zobrazovat se offline a přidává zvukový přehrávač pro zvukové přílohy
+Úplný seznam změn: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/cs-CZ/changelogs/40104130.txt b/fastlane/metadata/android/cs-CZ/changelogs/40104130.txt
new file mode 100644
index 0000000000..7867646fe5
--- /dev/null
+++ b/fastlane/metadata/android/cs-CZ/changelogs/40104130.txt
@@ -0,0 +1,2 @@
+Hlavní změny v této verzi: Umožňuje uživatelům zobrazovat se offline a přidává zvukový přehrávač pro zvukové přílohy
+Úplný seznam změn: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/de-DE/changelogs/40104060.txt b/fastlane/metadata/android/de-DE/changelogs/40104060.txt
new file mode 100644
index 0000000000..17cfdd26cc
--- /dev/null
+++ b/fastlane/metadata/android/de-DE/changelogs/40104060.txt
@@ -0,0 +1,2 @@
+Hauptänderungen: Threads sind jetzt schneller, Fehlerbehebungen.
+Alle Änderungen: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/de-DE/changelogs/40104070.txt b/fastlane/metadata/android/de-DE/changelogs/40104070.txt
new file mode 100644
index 0000000000..30da225add
--- /dev/null
+++ b/fastlane/metadata/android/de-DE/changelogs/40104070.txt
@@ -0,0 +1,2 @@
+Änderungen: Fehlerbehebungen
+Alle Änderungen: https://github.com/vector-im/element-android/releases/tag/v1.4.7
diff --git a/fastlane/metadata/android/de-DE/changelogs/40104080.txt b/fastlane/metadata/android/de-DE/changelogs/40104080.txt
new file mode 100644
index 0000000000..902e1d27f7
--- /dev/null
+++ b/fastlane/metadata/android/de-DE/changelogs/40104080.txt
@@ -0,0 +1,2 @@
+Hauptänderungen: Schnellere Threads, Fehlerbehebungen.
+Alle Änderungen: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/de-DE/changelogs/40104100.txt b/fastlane/metadata/android/de-DE/changelogs/40104100.txt
new file mode 100644
index 0000000000..2de5ec1d6a
--- /dev/null
+++ b/fastlane/metadata/android/de-DE/changelogs/40104100.txt
@@ -0,0 +1,2 @@
+Hauptänderungen: Scrollen in Sprachnachrichten, Fehlerbehebungen.
+Alle Änderungen: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/de-DE/changelogs/40104110.txt b/fastlane/metadata/android/de-DE/changelogs/40104110.txt
new file mode 100644
index 0000000000..bde9f04e11
--- /dev/null
+++ b/fastlane/metadata/android/de-DE/changelogs/40104110.txt
@@ -0,0 +1,2 @@
+Änderungen: Fehlerbehebungen und Stabilitätsverbesserungen
+Alle Änderungen: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/de-DE/changelogs/40104120.txt b/fastlane/metadata/android/de-DE/changelogs/40104120.txt
new file mode 100644
index 0000000000..e0ce944874
--- /dev/null
+++ b/fastlane/metadata/android/de-DE/changelogs/40104120.txt
@@ -0,0 +1,2 @@
+Hauptänderungen: Nutzer können ihren Status auf „Offline“ setzen, Gesendete Audiodateien können nun in der App abgespielt werden
+Alle Änderungen: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/de-DE/changelogs/40104130.txt b/fastlane/metadata/android/de-DE/changelogs/40104130.txt
new file mode 100644
index 0000000000..e0ce944874
--- /dev/null
+++ b/fastlane/metadata/android/de-DE/changelogs/40104130.txt
@@ -0,0 +1,2 @@
+Hauptänderungen: Nutzer können ihren Status auf „Offline“ setzen, Gesendete Audiodateien können nun in der App abgespielt werden
+Alle Änderungen: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/en-US/changelogs/40104160.txt b/fastlane/metadata/android/en-US/changelogs/40104160.txt
new file mode 100644
index 0000000000..6e37a4ae7f
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/40104160.txt
@@ -0,0 +1,2 @@
+Main changes in this version: Better management of encrypted messages. Various bug fixes and stability improvements.
+Full changelog: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/et/changelogs/40104120.txt b/fastlane/metadata/android/et/changelogs/40104120.txt
new file mode 100644
index 0000000000..1a7d3ae979
--- /dev/null
+++ b/fastlane/metadata/android/et/changelogs/40104120.txt
@@ -0,0 +1,2 @@
+Põhilised muutused selles versioonis: kasutajate võrguolekud ning helisõnumite esitaja.
+Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/et/changelogs/40104130.txt b/fastlane/metadata/android/et/changelogs/40104130.txt
new file mode 100644
index 0000000000..1a7d3ae979
--- /dev/null
+++ b/fastlane/metadata/android/et/changelogs/40104130.txt
@@ -0,0 +1,2 @@
+Põhilised muutused selles versioonis: kasutajate võrguolekud ning helisõnumite esitaja.
+Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/fa/changelogs/40104120.txt b/fastlane/metadata/android/fa/changelogs/40104120.txt
new file mode 100644
index 0000000000..4f730e52dc
--- /dev/null
+++ b/fastlane/metadata/android/fa/changelogs/40104120.txt
@@ -0,0 +1,2 @@
+تغییرات عمده در این نگارش: اجازه به کاربران برای برونخط ظاهر شدن و افزودن یک پخشکنندهٔ صدا برای پیوستهای صوتی
+گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/fa/changelogs/40104130.txt b/fastlane/metadata/android/fa/changelogs/40104130.txt
new file mode 100644
index 0000000000..4f730e52dc
--- /dev/null
+++ b/fastlane/metadata/android/fa/changelogs/40104130.txt
@@ -0,0 +1,2 @@
+تغییرات عمده در این نگارش: اجازه به کاربران برای برونخط ظاهر شدن و افزودن یک پخشکنندهٔ صدا برای پیوستهای صوتی
+گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/id/changelogs/40104120.txt b/fastlane/metadata/android/id/changelogs/40104120.txt
new file mode 100644
index 0000000000..ce1a4a4d84
--- /dev/null
+++ b/fastlane/metadata/android/id/changelogs/40104120.txt
@@ -0,0 +1,2 @@
+Perubahan utama dalam versi ini: Diperbolehkan pengguna untuk terlihat luring dan ditambahkan sebuah pemain audio untuk lampiran audio
+Catatan perubahan lanjutan: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/id/changelogs/40104130.txt b/fastlane/metadata/android/id/changelogs/40104130.txt
new file mode 100644
index 0000000000..ce1a4a4d84
--- /dev/null
+++ b/fastlane/metadata/android/id/changelogs/40104130.txt
@@ -0,0 +1,2 @@
+Perubahan utama dalam versi ini: Diperbolehkan pengguna untuk terlihat luring dan ditambahkan sebuah pemain audio untuk lampiran audio
+Catatan perubahan lanjutan: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/it-IT/changelogs/40104120.txt b/fastlane/metadata/android/it-IT/changelogs/40104120.txt
new file mode 100644
index 0000000000..fa015ae564
--- /dev/null
+++ b/fastlane/metadata/android/it-IT/changelogs/40104120.txt
@@ -0,0 +1,2 @@
+Modifiche principali in questa versione: consente agli utenti di apparire offline e aggiunge un player audio per gli allegati audio
+Cronologia completa: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/it-IT/changelogs/40104130.txt b/fastlane/metadata/android/it-IT/changelogs/40104130.txt
new file mode 100644
index 0000000000..fa015ae564
--- /dev/null
+++ b/fastlane/metadata/android/it-IT/changelogs/40104130.txt
@@ -0,0 +1,2 @@
+Modifiche principali in questa versione: consente agli utenti di apparire offline e aggiunge un player audio per gli allegati audio
+Cronologia completa: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/lo/changelogs/40100100.txt b/fastlane/metadata/android/lo-LA/changelogs/40100100.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40100100.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40100100.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40100110.txt b/fastlane/metadata/android/lo-LA/changelogs/40100110.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40100110.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40100110.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40100120.txt b/fastlane/metadata/android/lo-LA/changelogs/40100120.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40100120.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40100120.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40100130.txt b/fastlane/metadata/android/lo-LA/changelogs/40100130.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40100130.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40100130.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40100140.txt b/fastlane/metadata/android/lo-LA/changelogs/40100140.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40100140.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40100140.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40100150.txt b/fastlane/metadata/android/lo-LA/changelogs/40100150.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40100150.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40100150.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40100160.txt b/fastlane/metadata/android/lo-LA/changelogs/40100160.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40100160.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40100160.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40100170.txt b/fastlane/metadata/android/lo-LA/changelogs/40100170.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40100170.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40100170.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40101000.txt b/fastlane/metadata/android/lo-LA/changelogs/40101000.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40101000.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40101000.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40101010.txt b/fastlane/metadata/android/lo-LA/changelogs/40101010.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40101010.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40101010.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40101020.txt b/fastlane/metadata/android/lo-LA/changelogs/40101020.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40101020.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40101020.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40101030.txt b/fastlane/metadata/android/lo-LA/changelogs/40101030.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40101030.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40101030.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40101040.txt b/fastlane/metadata/android/lo-LA/changelogs/40101040.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40101040.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40101040.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40101050.txt b/fastlane/metadata/android/lo-LA/changelogs/40101050.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40101050.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40101050.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40101060.txt b/fastlane/metadata/android/lo-LA/changelogs/40101060.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40101060.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40101060.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40101070.txt b/fastlane/metadata/android/lo-LA/changelogs/40101070.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40101070.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40101070.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40101080.txt b/fastlane/metadata/android/lo-LA/changelogs/40101080.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40101080.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40101080.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40101090.txt b/fastlane/metadata/android/lo-LA/changelogs/40101090.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40101090.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40101090.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40101100.txt b/fastlane/metadata/android/lo-LA/changelogs/40101100.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40101100.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40101100.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40101110.txt b/fastlane/metadata/android/lo-LA/changelogs/40101110.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40101110.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40101110.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40101120.txt b/fastlane/metadata/android/lo-LA/changelogs/40101120.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40101120.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40101120.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40101130.txt b/fastlane/metadata/android/lo-LA/changelogs/40101130.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40101130.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40101130.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40101140.txt b/fastlane/metadata/android/lo-LA/changelogs/40101140.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40101140.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40101140.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40101150.txt b/fastlane/metadata/android/lo-LA/changelogs/40101150.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40101150.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40101150.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40101160.txt b/fastlane/metadata/android/lo-LA/changelogs/40101160.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40101160.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40101160.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40102000.txt b/fastlane/metadata/android/lo-LA/changelogs/40102000.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40102000.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40102000.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40102010.txt b/fastlane/metadata/android/lo-LA/changelogs/40102010.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40102010.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40102010.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40103000.txt b/fastlane/metadata/android/lo-LA/changelogs/40103000.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40103000.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40103000.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40103010.txt b/fastlane/metadata/android/lo-LA/changelogs/40103010.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40103010.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40103010.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40103020.txt b/fastlane/metadata/android/lo-LA/changelogs/40103020.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40103020.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40103020.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40103030.txt b/fastlane/metadata/android/lo-LA/changelogs/40103030.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40103030.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40103030.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40103040.txt b/fastlane/metadata/android/lo-LA/changelogs/40103040.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40103040.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40103040.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40103050.txt b/fastlane/metadata/android/lo-LA/changelogs/40103050.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40103050.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40103050.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40103060.txt b/fastlane/metadata/android/lo-LA/changelogs/40103060.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40103060.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40103060.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40103070.txt b/fastlane/metadata/android/lo-LA/changelogs/40103070.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40103070.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40103070.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40103080.txt b/fastlane/metadata/android/lo-LA/changelogs/40103080.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40103080.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40103080.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40103090.txt b/fastlane/metadata/android/lo-LA/changelogs/40103090.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40103090.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40103090.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40103100.txt b/fastlane/metadata/android/lo-LA/changelogs/40103100.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40103100.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40103100.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40103110.txt b/fastlane/metadata/android/lo-LA/changelogs/40103110.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40103110.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40103110.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40103120.txt b/fastlane/metadata/android/lo-LA/changelogs/40103120.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40103120.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40103120.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40103130.txt b/fastlane/metadata/android/lo-LA/changelogs/40103130.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40103130.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40103130.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40103140.txt b/fastlane/metadata/android/lo-LA/changelogs/40103140.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40103140.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40103140.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40103150.txt b/fastlane/metadata/android/lo-LA/changelogs/40103150.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40103150.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40103150.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40103160.txt b/fastlane/metadata/android/lo-LA/changelogs/40103160.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40103160.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40103160.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40103170.txt b/fastlane/metadata/android/lo-LA/changelogs/40103170.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40103170.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40103170.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40103180.txt b/fastlane/metadata/android/lo-LA/changelogs/40103180.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40103180.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40103180.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40104000.txt b/fastlane/metadata/android/lo-LA/changelogs/40104000.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40104000.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40104000.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40104020.txt b/fastlane/metadata/android/lo-LA/changelogs/40104020.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40104020.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40104020.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40104040.txt b/fastlane/metadata/android/lo-LA/changelogs/40104040.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40104040.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40104040.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40104060.txt b/fastlane/metadata/android/lo-LA/changelogs/40104060.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40104060.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40104060.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40104070.txt b/fastlane/metadata/android/lo-LA/changelogs/40104070.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40104070.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40104070.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40104080.txt b/fastlane/metadata/android/lo-LA/changelogs/40104080.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40104080.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40104080.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40104100.txt b/fastlane/metadata/android/lo-LA/changelogs/40104100.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40104100.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40104100.txt
diff --git a/fastlane/metadata/android/lo/changelogs/40104110.txt b/fastlane/metadata/android/lo-LA/changelogs/40104110.txt
similarity index 100%
rename from fastlane/metadata/android/lo/changelogs/40104110.txt
rename to fastlane/metadata/android/lo-LA/changelogs/40104110.txt
diff --git a/fastlane/metadata/android/lo-LA/changelogs/40104120.txt b/fastlane/metadata/android/lo-LA/changelogs/40104120.txt
new file mode 100644
index 0000000000..36c6d678f7
--- /dev/null
+++ b/fastlane/metadata/android/lo-LA/changelogs/40104120.txt
@@ -0,0 +1,2 @@
+ການປ່ຽນແປງຫຼັກໃນສະບັບນີ້: ໃຫ້ຜູ້ໃຊ້ສາມາດສະແດງຕົວເປັນ offline ແລະສາມາດຫຼິ້ນສຽງໄດ້ສຳລັບການແນບສຽງ
+ບັນທຶກການປ່ຽນແປງສະບັບເຕັມ: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/lo-LA/changelogs/40104130.txt b/fastlane/metadata/android/lo-LA/changelogs/40104130.txt
new file mode 100644
index 0000000000..36c6d678f7
--- /dev/null
+++ b/fastlane/metadata/android/lo-LA/changelogs/40104130.txt
@@ -0,0 +1,2 @@
+ການປ່ຽນແປງຫຼັກໃນສະບັບນີ້: ໃຫ້ຜູ້ໃຊ້ສາມາດສະແດງຕົວເປັນ offline ແລະສາມາດຫຼິ້ນສຽງໄດ້ສຳລັບການແນບສຽງ
+ບັນທຶກການປ່ຽນແປງສະບັບເຕັມ: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/lo/full_description.txt b/fastlane/metadata/android/lo-LA/full_description.txt
similarity index 100%
rename from fastlane/metadata/android/lo/full_description.txt
rename to fastlane/metadata/android/lo-LA/full_description.txt
diff --git a/fastlane/metadata/android/lo/short_description.txt b/fastlane/metadata/android/lo-LA/short_description.txt
similarity index 100%
rename from fastlane/metadata/android/lo/short_description.txt
rename to fastlane/metadata/android/lo-LA/short_description.txt
diff --git a/fastlane/metadata/android/lo/title.txt b/fastlane/metadata/android/lo-LA/title.txt
similarity index 100%
rename from fastlane/metadata/android/lo/title.txt
rename to fastlane/metadata/android/lo-LA/title.txt
diff --git a/fastlane/metadata/android/pl-PL/changelogs/40103050.txt b/fastlane/metadata/android/pl-PL/changelogs/40103050.txt
new file mode 100644
index 0000000000..b81d5ef037
--- /dev/null
+++ b/fastlane/metadata/android/pl-PL/changelogs/40103050.txt
@@ -0,0 +1,2 @@
+Główne zmiany w tej wersji: Dodanie obsługi obecności, dla pokoju wiadomości bezpośrednich (uwaga: obecność jest wyłączona na matrix.org). Dodaje ponownie obsługę Androida Auto.
+ Pełna lista zmian: https://github.com/vector-im/element-android/releases/tag/v1.3.5
diff --git a/fastlane/metadata/android/pl-PL/changelogs/40104100.txt b/fastlane/metadata/android/pl-PL/changelogs/40104100.txt
new file mode 100644
index 0000000000..3d1efbc1d2
--- /dev/null
+++ b/fastlane/metadata/android/pl-PL/changelogs/40104100.txt
@@ -0,0 +1,2 @@
+Główne zmiany w tej wersji: Przewijanie w wiadomości głosowej. Różne poprawki błędów i ulepszenia stabilności.
+ Pełna lista zmian: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/pl-PL/changelogs/40104110.txt b/fastlane/metadata/android/pl-PL/changelogs/40104110.txt
new file mode 100644
index 0000000000..6ae86f3c9b
--- /dev/null
+++ b/fastlane/metadata/android/pl-PL/changelogs/40104110.txt
@@ -0,0 +1,2 @@
+Główne zmiany w tej wersji: Różne poprawki błędów i ulepszenia stabilności.
+ Pełna lista zmian: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/pl-PL/changelogs/40104120.txt b/fastlane/metadata/android/pl-PL/changelogs/40104120.txt
new file mode 100644
index 0000000000..bd995ad5e5
--- /dev/null
+++ b/fastlane/metadata/android/pl-PL/changelogs/40104120.txt
@@ -0,0 +1,2 @@
+Główne zmiany w tej wersji: Pozwala użytkownikom pojawiać się w trybie offline i dodaje odtwarzacz audio do załączników audio
+Pełna lista zmian: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/pl-PL/changelogs/40104130.txt b/fastlane/metadata/android/pl-PL/changelogs/40104130.txt
new file mode 100644
index 0000000000..bd995ad5e5
--- /dev/null
+++ b/fastlane/metadata/android/pl-PL/changelogs/40104130.txt
@@ -0,0 +1,2 @@
+Główne zmiany w tej wersji: Pozwala użytkownikom pojawiać się w trybie offline i dodaje odtwarzacz audio do załączników audio
+Pełna lista zmian: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/pl-PL/title.txt b/fastlane/metadata/android/pl-PL/title.txt
index 907f907f99..df4d9b71f4 100644
--- a/fastlane/metadata/android/pl-PL/title.txt
+++ b/fastlane/metadata/android/pl-PL/title.txt
@@ -1 +1 @@
-Element
+Element - komunikator
diff --git a/fastlane/metadata/android/pt-BR/changelogs/40104120.txt b/fastlane/metadata/android/pt-BR/changelogs/40104120.txt
new file mode 100644
index 0000000000..f77d426d99
--- /dev/null
+++ b/fastlane/metadata/android/pt-BR/changelogs/40104120.txt
@@ -0,0 +1,2 @@
+Principais mudanças nesta versão: Permite usuárias(os) aparecer offline e adiciona um tocador de áudio para anexos de áudio
+Changelog completo: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/pt-BR/changelogs/40104130.txt b/fastlane/metadata/android/pt-BR/changelogs/40104130.txt
new file mode 100644
index 0000000000..f77d426d99
--- /dev/null
+++ b/fastlane/metadata/android/pt-BR/changelogs/40104130.txt
@@ -0,0 +1,2 @@
+Principais mudanças nesta versão: Permite usuárias(os) aparecer offline e adiciona um tocador de áudio para anexos de áudio
+Changelog completo: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/sk/changelogs/40104120.txt b/fastlane/metadata/android/sk/changelogs/40104120.txt
new file mode 100644
index 0000000000..2279ddc574
--- /dev/null
+++ b/fastlane/metadata/android/sk/changelogs/40104120.txt
@@ -0,0 +1,2 @@
+Hlavné zmeny v tejto verzii: Umožňuje používateľom zobrazovať sa v režime offline a pridáva zvukový prehrávač pre zvukové prílohy
+Úplný zoznam zmien: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/sk/changelogs/40104130.txt b/fastlane/metadata/android/sk/changelogs/40104130.txt
new file mode 100644
index 0000000000..2279ddc574
--- /dev/null
+++ b/fastlane/metadata/android/sk/changelogs/40104130.txt
@@ -0,0 +1,2 @@
+Hlavné zmeny v tejto verzii: Umožňuje používateľom zobrazovať sa v režime offline a pridáva zvukový prehrávač pre zvukové prílohy
+Úplný zoznam zmien: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/sv-SE/changelogs/40104120.txt b/fastlane/metadata/android/sv-SE/changelogs/40104120.txt
new file mode 100644
index 0000000000..6692768e1e
--- /dev/null
+++ b/fastlane/metadata/android/sv-SE/changelogs/40104120.txt
@@ -0,0 +1,2 @@
+Huvudsakliga ändringar i den här versionen: Låter användare visas offline och lägger till en ljudspelare för ljudbilagor
+Full ändringslogg: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/sv-SE/changelogs/40104130.txt b/fastlane/metadata/android/sv-SE/changelogs/40104130.txt
new file mode 100644
index 0000000000..6692768e1e
--- /dev/null
+++ b/fastlane/metadata/android/sv-SE/changelogs/40104130.txt
@@ -0,0 +1,2 @@
+Huvudsakliga ändringar i den här versionen: Låter användare visas offline och lägger till en ljudspelare för ljudbilagor
+Full ändringslogg: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/uk/changelogs/40104120.txt b/fastlane/metadata/android/uk/changelogs/40104120.txt
new file mode 100644
index 0000000000..aa075bd42e
--- /dev/null
+++ b/fastlane/metadata/android/uk/changelogs/40104120.txt
@@ -0,0 +1,2 @@
+Основні зміни у цій версії: Дозволяє користувачам з’являтися в режимі офлайн та додає аудіопрогравач для аудіовкладень
+Вичерпний перелік змін: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/uk/changelogs/40104130.txt b/fastlane/metadata/android/uk/changelogs/40104130.txt
new file mode 100644
index 0000000000..aa075bd42e
--- /dev/null
+++ b/fastlane/metadata/android/uk/changelogs/40104130.txt
@@ -0,0 +1,2 @@
+Основні зміни у цій версії: Дозволяє користувачам з’являтися в режимі офлайн та додає аудіопрогравач для аудіовкладень
+Вичерпний перелік змін: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/zh-TW/changelogs/40104120.txt b/fastlane/metadata/android/zh-TW/changelogs/40104120.txt
new file mode 100644
index 0000000000..d3d48abab9
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/40104120.txt
@@ -0,0 +1,2 @@
+此版本中的主要變動:允許使用者顯示為離線並為音訊附件新增音訊播放器
+完整的變更紀錄:https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/zh-TW/changelogs/40104130.txt b/fastlane/metadata/android/zh-TW/changelogs/40104130.txt
new file mode 100644
index 0000000000..d3d48abab9
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/40104130.txt
@@ -0,0 +1,2 @@
+此版本中的主要變動:允許使用者顯示為離線並為音訊附件新增音訊播放器
+完整的變更紀錄:https://github.com/vector-im/element-android/releases
diff --git a/library/core-utils/src/main/java/im/vector/lib/core/utils/flow/TimingOperators.kt b/library/core-utils/src/main/java/im/vector/lib/core/utils/flow/TimingOperators.kt
index 2efb439ace..aeb5ae7914 100644
--- a/library/core-utils/src/main/java/im/vector/lib/core/utils/flow/TimingOperators.kt
+++ b/library/core-utils/src/main/java/im/vector/lib/core/utils/flow/TimingOperators.kt
@@ -16,6 +16,7 @@
package im.vector.lib.core.utils.flow
+import android.os.SystemClock
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.Channel
@@ -68,10 +69,10 @@ fun Flow.chunk(durationInMillis: Long): Flow> {
@ExperimentalCoroutinesApi
fun Flow.throttleFirst(windowDuration: Long): Flow = flow {
- var windowStartTime = System.currentTimeMillis()
+ var windowStartTime = SystemClock.elapsedRealtime()
var emitted = false
collect { value ->
- val currentTime = System.currentTimeMillis()
+ val currentTime = SystemClock.elapsedRealtime()
val delta = currentTime - windowStartTime
if (delta >= windowDuration) {
windowStartTime += delta / windowDuration * windowDuration
diff --git a/library/jsonviewer/src/main/java/org/billcarsonfr/jsonviewer/ValueItem.kt b/library/jsonviewer/src/main/java/org/billcarsonfr/jsonviewer/ValueItem.kt
index fac7099b37..66dfcc5dc3 100644
--- a/library/jsonviewer/src/main/java/org/billcarsonfr/jsonviewer/ValueItem.kt
+++ b/library/jsonviewer/src/main/java/org/billcarsonfr/jsonviewer/ValueItem.kt
@@ -18,11 +18,11 @@ package org.billcarsonfr.jsonviewer
import android.content.ClipData
import android.content.ClipboardManager
-import android.content.Context
import android.view.ContextMenu
import android.view.View
import android.widget.LinearLayout
import android.widget.TextView
+import androidx.core.content.getSystemService
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyHolder
import com.airbnb.epoxy.EpoxyModelClass
@@ -77,8 +77,7 @@ internal abstract class ValueItem : EpoxyModelWithHolder() {
) {
if (copyValue != null) {
val menuItem = menu?.add(R.string.copy_value)
- val clipService =
- v?.context?.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager
+ val clipService = v?.context?.getSystemService()
menuItem?.setOnMenuItemClickListener {
clipService?.setPrimaryClip(ClipData.newPlainText("", copyValue))
true
diff --git a/library/ui-styles/src/main/res/values/colors.xml b/library/ui-styles/src/main/res/values/colors.xml
index 2104b49ab5..e72d02f51e 100644
--- a/library/ui-styles/src/main/res/values/colors.xml
+++ b/library/ui-styles/src/main/res/values/colors.xml
@@ -144,4 +144,9 @@
#17191C#FF4B55
+
+
+ @color/palette_white
+ @color/palette_black_950
+
diff --git a/library/ui-styles/src/main/res/values/dimens.xml b/library/ui-styles/src/main/res/values/dimens.xml
index 81d5a77297..70d051b457 100644
--- a/library/ui-styles/src/main/res/values/dimens.xml
+++ b/library/ui-styles/src/main/res/values/dimens.xml
@@ -9,6 +9,7 @@
32dp50dp
+ 20dp16dp32dp
@@ -40,7 +41,7 @@
24dp48dp48dp
- 38dp
+ 34dp56dp
diff --git a/library/ui-styles/src/main/res/values/styles_location.xml b/library/ui-styles/src/main/res/values/styles_location.xml
index 5563d28342..7571265241 100644
--- a/library/ui-styles/src/main/res/values/styles_location.xml
+++ b/library/ui-styles/src/main/res/values/styles_location.xml
@@ -2,10 +2,20 @@
+
+
diff --git a/library/ui-styles/src/main/res/values/theme_dark.xml b/library/ui-styles/src/main/res/values/theme_dark.xml
index eeff039b71..733f7e8eb5 100644
--- a/library/ui-styles/src/main/res/values/theme_dark.xml
+++ b/library/ui-styles/src/main/res/values/theme_dark.xml
@@ -30,6 +30,7 @@
@color/element_system_dark@color/vctr_message_bubble_inbound_dark@color/vctr_message_bubble_outbound_dark
+ @color/vctr_badge_color_border_dark#61708B
diff --git a/library/ui-styles/src/main/res/values/theme_light.xml b/library/ui-styles/src/main/res/values/theme_light.xml
index 0c363b583d..77996c8ce5 100644
--- a/library/ui-styles/src/main/res/values/theme_light.xml
+++ b/library/ui-styles/src/main/res/values/theme_light.xml
@@ -30,6 +30,7 @@
@color/element_background_light@color/vctr_message_bubble_inbound_light@color/vctr_message_bubble_outbound_light
+ @color/vctr_badge_color_border_light#61708B
diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle
index dfa99d0f8a..33798d1357 100644
--- a/matrix-sdk-android/build.gradle
+++ b/matrix-sdk-android/build.gradle
@@ -56,7 +56,7 @@ android {
// that the app's state is completely cleared between tests.
testInstrumentationRunnerArguments clearPackageData: 'true'
- buildConfigField "String", "SDK_VERSION", "\"1.4.16\""
+ buildConfigField "String", "SDK_VERSION", "\"1.4.18\""
buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\""
buildConfigField "String", "GIT_SDK_REVISION_UNIX_DATE", "\"${gitRevisionUnixDate()}\""
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/Util.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/Util.kt
new file mode 100644
index 0000000000..5e2c2ba25f
--- /dev/null
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/Util.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2022 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
+
+import junit.framework.TestCase.fail
+
+/**
+ * Will fail the test if invoking [block] is not throwing a Throwable.
+ *
+ * @param message the failure message, if the block does not throw any Throwable
+ * @param failureBlock a Lambda to be able to do extra check on the thrown Throwable
+ * @param block the block to test
+ */
+internal suspend fun mustFail(
+ message: String = "must fail",
+ failureBlock: ((Throwable) -> Unit)? = null,
+ block: suspend () -> Unit,
+) {
+ val isSuccess = try {
+ block.invoke()
+ true
+ } catch (throwable: Throwable) {
+ failureBlock?.invoke(throwable)
+ false
+ }
+
+ if (isSuccess) {
+ fail(message)
+ }
+}
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 e33e4faea2..96ea99d92f 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
@@ -96,7 +96,7 @@ class CommonTestHelper(context: Context) {
/**
* This methods init the event stream and check for initial sync
*
- * @param session the session to sync
+ * @param session the session to sync
*/
fun syncSession(session: Session, timeout: Long = TestConstants.timeOutMillis * 10) {
val lock = CountDownLatch(1)
@@ -119,7 +119,7 @@ class CommonTestHelper(context: Context) {
/**
* This methods clear the cache and waits for initialSync
*
- * @param session the session to sync
+ * @param session the session to sync
*/
fun clearCacheAndSync(session: Session, timeout: Long = TestConstants.timeOutMillis) {
waitWithLatch(timeout) { latch ->
@@ -142,8 +142,8 @@ class CommonTestHelper(context: Context) {
/**
* Sends text messages in a room
*
- * @param room the room where to send the messages
- * @param message the message to send
+ * @param room the room where to send the messages
+ * @param message the message to send
* @param nbOfMessages the number of time the message will be sent
*/
fun sendTextMessage(room: Room, message: String, nbOfMessages: Int, timeout: Long = TestConstants.timeOutMillis): List {
@@ -207,8 +207,8 @@ class CommonTestHelper(context: Context) {
/**
* Reply in a thread
- * @param room the room where to send the messages
- * @param message the message to send
+ * @param room the room where to send the messages
+ * @param message the message to send
* @param numberOfMessages the number of time the message will be sent
*/
fun replyInThreadMessage(
@@ -232,8 +232,8 @@ class CommonTestHelper(context: Context) {
* Creates a unique account
*
* @param userNamePrefix the user name prefix
- * @param password the password
- * @param testParams test params about the session
+ * @param password the password
+ * @param testParams test params about the session
* @return the session associated with the newly created account
*/
private fun createAccount(userNamePrefix: String,
@@ -251,8 +251,8 @@ class CommonTestHelper(context: Context) {
/**
* Logs into an existing account
*
- * @param userId the userId to log in
- * @param password the password to log in
+ * @param userId the userId to log in
+ * @param password the password to log in
* @param testParams test params about the session
* @return the session associated with the existing account
*/
@@ -267,8 +267,8 @@ class CommonTestHelper(context: Context) {
/**
* Create an account and a dedicated session
*
- * @param userName the account username
- * @param password the password
+ * @param userName the account username
+ * @param password the password
* @param sessionTestParams parameters for the test
*/
private fun createAccountAndSync(userName: String,
@@ -297,7 +297,7 @@ class CommonTestHelper(context: Context) {
val session = (registrationResult as RegistrationResult.Success).session
session.open()
if (sessionTestParams.withInitialSync) {
- syncSession(session, 60_000)
+ syncSession(session, 120_000)
}
return session
}
@@ -305,8 +305,8 @@ class CommonTestHelper(context: Context) {
/**
* Start an account login
*
- * @param userName the account username
- * @param password the password
+ * @param userName the account username
+ * @param password the password
* @param sessionTestParams session test params
*/
private fun logAccountAndSync(userName: String,
@@ -378,7 +378,10 @@ class CommonTestHelper(context: Context) {
* @throws InterruptedException
*/
fun await(latch: CountDownLatch, timeout: Long? = TestConstants.timeOutMillis) {
- assertTrue(latch.await(timeout ?: TestConstants.timeOutMillis, TimeUnit.MILLISECONDS))
+ assertTrue(
+ "Timed out after " + timeout + "ms waiting for something to happen. See stacktrace for cause.",
+ latch.await(timeout ?: TestConstants.timeOutMillis, TimeUnit.MILLISECONDS)
+ )
}
suspend fun retryPeriodicallyWithLatch(latch: CountDownLatch, condition: (() -> Boolean)) {
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 0f79896b2c..89c965c31a 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
@@ -23,7 +23,7 @@ object TestConstants {
const val TESTS_HOME_SERVER_URL = "http://10.0.2.2:8080"
// Time out to use when waiting for server response.
- private const val AWAIT_TIME_OUT_MILLIS = 60_000
+ private const val AWAIT_TIME_OUT_MILLIS = 120_000
// Time out to use when waiting for server response, when the debugger is connected. 10 minutes
private const val AWAIT_TIME_OUT_WITH_DEBUGGER_MILLIS = 10 * 60_000
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrix.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrix.kt
index e663cc1865..5864a801e6 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrix.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrix.kt
@@ -21,7 +21,8 @@ import android.os.Handler
import android.os.Looper
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.work.Configuration
-import androidx.work.WorkManager
+import androidx.work.impl.WorkManagerImpl
+import androidx.work.impl.utils.taskexecutor.WorkManagerTaskExecutor
import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.BuildConfig
import org.matrix.android.sdk.api.MatrixConfiguration
@@ -66,7 +67,12 @@ internal class TestMatrix(context: Context, matrixConfiguration: MatrixConfigura
.setExecutor(Executors.newCachedThreadPool())
.setWorkerFactory(matrixWorkerFactory)
.build()
- WorkManager.initialize(appContext, configuration)
+ val delegate = WorkManagerImpl(
+ context,
+ configuration,
+ WorkManagerTaskExecutor(configuration.taskExecutor)
+ )
+ WorkManagerImpl.setDelegate(delegate)
uiHandler.post {
ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver)
}
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 e823aa39a1..cd6c146f03 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
@@ -22,9 +22,11 @@ import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertNull
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.matrix.android.sdk.InstrumentedTest
+import org.matrix.android.sdk.common.RetryTestRule
import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
import org.matrix.android.sdk.internal.util.time.DefaultClock
@@ -37,6 +39,8 @@ private const val DUMMY_DEVICE_KEY = "DeviceKey"
@RunWith(AndroidJUnit4::class)
class CryptoStoreTest : InstrumentedTest {
+ @get:Rule val rule = RetryTestRule(3)
+
private val cryptoStoreHelper = CryptoStoreHelper()
private val clock = DefaultClock()
diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeSanityTests.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeSanityTests.kt
index ebe4c5ff6f..552936971f 100644
--- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeSanityTests.kt
+++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeSanityTests.kt
@@ -23,6 +23,7 @@ import org.amshove.kluent.fail
import org.amshove.kluent.internal.assertEquals
import org.junit.Assert
import org.junit.FixMethodOrder
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@@ -57,9 +58,11 @@ import org.matrix.android.sdk.api.session.room.send.SendState
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.common.RetryTestRule
import org.matrix.android.sdk.common.SessionTestParams
import org.matrix.android.sdk.common.TestConstants
import org.matrix.android.sdk.common.TestMatrixCallback
+import org.matrix.android.sdk.mustFail
import java.util.concurrent.CountDownLatch
@RunWith(JUnit4::class)
@@ -67,6 +70,8 @@ import java.util.concurrent.CountDownLatch
@LargeTest
class E2eeSanityTests : InstrumentedTest {
+ @get:Rule val rule = RetryTestRule(3)
+
/**
* Simple test that create an e2ee room.
* Some new members are added, and a message is sent.
@@ -521,10 +526,8 @@ class E2eeSanityTests : InstrumentedTest {
// Confirm we can decrypt one but not the other
testHelper.runBlockingTest {
- try {
+ mustFail(message = "Should not be able to decrypt event") {
newBobSession.cryptoService().decryptEvent(firstEventNewBobPov.root, "")
- fail("Should not be able to decrypt event")
- } catch (_: MXCryptoError) {
}
}
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 a37626dc20..abcf1714b8 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
@@ -25,7 +25,6 @@ import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Assert.fail
import org.junit.FixMethodOrder
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -143,7 +142,6 @@ class XSigningTest : InstrumentedTest {
}
@Test
- @Ignore("This test will be ignored until it is fixed")
fun test_CrossSigningTestAliceTrustBobNewDevice() {
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
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 2e4fd62822..895f95aeac 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
@@ -21,11 +21,11 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import junit.framework.TestCase.assertNotNull
import junit.framework.TestCase.assertTrue
-import junit.framework.TestCase.fail
import org.amshove.kluent.internal.assertEquals
import org.junit.Assert
import org.junit.Assert.assertNull
import org.junit.FixMethodOrder
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -43,14 +43,18 @@ import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
import org.matrix.android.sdk.common.CommonTestHelper
import org.matrix.android.sdk.common.CryptoTestHelper
+import org.matrix.android.sdk.common.RetryTestRule
import org.matrix.android.sdk.common.SessionTestParams
import org.matrix.android.sdk.common.TestConstants
+import org.matrix.android.sdk.mustFail
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
@LargeTest
class KeyShareTests : InstrumentedTest {
+ @get:Rule val rule = RetryTestRule(3)
+
@Test
fun test_DoNotSelfShareIfNotTrusted() {
val commonTestHelper = CommonTestHelper(context())
@@ -91,12 +95,10 @@ class KeyShareTests : InstrumentedTest {
assertNotNull(receivedEvent)
assert(receivedEvent!!.isEncrypted())
- try {
- commonTestHelper.runBlockingTest {
+ commonTestHelper.runBlockingTest {
+ mustFail {
aliceSession2.cryptoService().decryptEvent(receivedEvent.root, "foo")
}
- fail("should fail")
- } catch (failure: Throwable) {
}
val outgoingRequestsBefore = aliceSession2.cryptoService().getOutgoingRoomKeyRequests()
@@ -164,12 +166,10 @@ class KeyShareTests : InstrumentedTest {
}
}
- try {
- commonTestHelper.runBlockingTest {
+ commonTestHelper.runBlockingTest {
+ mustFail {
aliceSession2.cryptoService().decryptEvent(receivedEvent.root, "foo")
}
- fail("should fail")
- } catch (failure: Throwable) {
}
// Mark the device as trusted
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 cb31a2232f..13133b726c 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
@@ -21,6 +21,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import org.junit.Assert
import org.junit.FixMethodOrder
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -38,14 +39,18 @@ import org.matrix.android.sdk.api.session.room.getTimelineEvent
import org.matrix.android.sdk.common.CommonTestHelper
import org.matrix.android.sdk.common.CryptoTestHelper
import org.matrix.android.sdk.common.MockOkHttpInterceptor
+import org.matrix.android.sdk.common.RetryTestRule
import org.matrix.android.sdk.common.SessionTestParams
import org.matrix.android.sdk.common.TestConstants
+import org.matrix.android.sdk.mustFail
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
@LargeTest
class WithHeldTests : InstrumentedTest {
+ @get:Rule val rule = RetryTestRule(3)
+
@Test
fun test_WithHeldUnverifiedReason() {
val testHelper = CommonTestHelper(context())
@@ -92,17 +97,19 @@ class WithHeldTests : InstrumentedTest {
// =============================
// Bob should not be able to decrypt because the keys is withheld
- try {
- // .. might need to wait a bit for stability?
- testHelper.runBlockingTest {
+ // .. might need to wait a bit for stability?
+ testHelper.runBlockingTest {
+ mustFail(
+ message = "This session should not be able to decrypt",
+ failureBlock = { failure ->
+ val type = (failure as MXCryptoError.Base).errorType
+ val technicalMessage = failure.technicalMessage
+ Assert.assertEquals("Error should be withheld", MXCryptoError.ErrorType.KEYS_WITHHELD, type)
+ Assert.assertEquals("Cause should be unverified", WithHeldCode.UNVERIFIED.value, technicalMessage)
+ }
+ ) {
bobUnverifiedSession.cryptoService().decryptEvent(eventBobPOV.root, "")
}
- Assert.fail("This session should not be able to decrypt")
- } catch (failure: Throwable) {
- val type = (failure as MXCryptoError.Base).errorType
- val technicalMessage = failure.technicalMessage
- Assert.assertEquals("Error should be withheld", MXCryptoError.ErrorType.KEYS_WITHHELD, type)
- Assert.assertEquals("Cause should be unverified", WithHeldCode.UNAUTHORISED.value, technicalMessage)
}
// Let's see if the reply we got from bob first session is unverified
@@ -133,17 +140,18 @@ class WithHeldTests : InstrumentedTest {
}
// Previous message should still be undecryptable (partially withheld session)
- try {
- // .. might need to wait a bit for stability?
- testHelper.runBlockingTest {
+ // .. might need to wait a bit for stability?
+ testHelper.runBlockingTest {
+ mustFail(
+ message = "This session should not be able to decrypt",
+ failureBlock = { failure ->
+ val type = (failure as MXCryptoError.Base).errorType
+ val technicalMessage = failure.technicalMessage
+ Assert.assertEquals("Error should be withheld", MXCryptoError.ErrorType.KEYS_WITHHELD, type)
+ Assert.assertEquals("Cause should be unverified", WithHeldCode.UNVERIFIED.value, technicalMessage)
+ }) {
bobUnverifiedSession.cryptoService().decryptEvent(eventBobPOV.root, "")
}
- Assert.fail("This session should not be able to decrypt")
- } catch (failure: Throwable) {
- val type = (failure as MXCryptoError.Base).errorType
- val technicalMessage = failure.technicalMessage
- Assert.assertEquals("Error should be withheld", MXCryptoError.ErrorType.KEYS_WITHHELD, type)
- Assert.assertEquals("Cause should be unverified", WithHeldCode.UNAUTHORISED.value, technicalMessage)
}
testHelper.signOutAndClose(aliceSession)
@@ -186,17 +194,18 @@ class WithHeldTests : InstrumentedTest {
// Previous message should still be undecryptable (partially withheld session)
val eventBobPOV = bobSession.getRoom(testData.roomId)?.getTimelineEvent(eventId)
- try {
- // .. might need to wait a bit for stability?
- testHelper.runBlockingTest {
+ // .. might need to wait a bit for stability?
+ testHelper.runBlockingTest {
+ mustFail(
+ message = "This session should not be able to decrypt",
+ failureBlock = { failure ->
+ val type = (failure as MXCryptoError.Base).errorType
+ val technicalMessage = failure.technicalMessage
+ Assert.assertEquals("Error should be withheld", MXCryptoError.ErrorType.KEYS_WITHHELD, type)
+ Assert.assertEquals("Cause should be unverified", WithHeldCode.NO_OLM.value, technicalMessage)
+ }) {
bobSession.cryptoService().decryptEvent(eventBobPOV!!.root, "")
}
- Assert.fail("This session should not be able to decrypt")
- } catch (failure: Throwable) {
- val type = (failure as MXCryptoError.Base).errorType
- val technicalMessage = failure.technicalMessage
- Assert.assertEquals("Error should be withheld", MXCryptoError.ErrorType.KEYS_WITHHELD, type)
- Assert.assertEquals("Cause should be unverified", WithHeldCode.NO_OLM.value, technicalMessage)
}
// Ensure that alice has marked the session to be shared with bob
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 df3b2ffe27..ceebc3cd01 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
@@ -27,11 +27,13 @@ import org.matrix.android.sdk.api.auth.UIABaseAuth
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.auth.UserPasswordAuth
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
+import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
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.common.CommonTestHelper
import org.matrix.android.sdk.common.CryptoTestHelper
+import org.matrix.android.sdk.common.SessionTestParams
import org.matrix.android.sdk.common.TestConstants
import java.util.concurrent.CountDownLatch
import kotlin.coroutines.Continuation
@@ -252,4 +254,48 @@ class VerificationTest : InstrumentedTest {
cryptoTestData.cleanUp(testHelper)
}
+
+ @Test
+ fun test_selfVerificationAcceptedCancelsItForOtherSessions() {
+ val defaultSessionParams = SessionTestParams(true)
+ val testHelper = CommonTestHelper(context())
+
+ val aliceSessionToVerify = testHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams)
+ val aliceSessionThatVerifies = testHelper.logIntoAccount(aliceSessionToVerify.myUserId, TestConstants.PASSWORD, defaultSessionParams)
+ val aliceSessionThatReceivesCanceledEvent = testHelper.logIntoAccount(aliceSessionToVerify.myUserId, TestConstants.PASSWORD, defaultSessionParams)
+
+ val verificationMethods = listOf(VerificationMethod.SAS, VerificationMethod.QR_CODE_SCAN, VerificationMethod.QR_CODE_SHOW)
+
+ val serviceOfVerified = aliceSessionToVerify.cryptoService().verificationService()
+ val serviceOfVerifier = aliceSessionThatVerifies.cryptoService().verificationService()
+ val serviceOfUserWhoReceivesCancellation = aliceSessionThatReceivesCanceledEvent.cryptoService().verificationService()
+
+ serviceOfVerifier.addListener(object : VerificationService.Listener {
+ override fun verificationRequestCreated(pr: PendingVerificationRequest) {
+ // Accept verification request
+ serviceOfVerifier.readyPendingVerification(
+ verificationMethods,
+ pr.otherUserId,
+ pr.transactionId!!,
+ )
+ }
+ })
+
+ serviceOfVerified.requestKeyVerification(
+ methods = verificationMethods,
+ otherUserId = aliceSessionToVerify.myUserId,
+ otherDevices = listOfNotNull(aliceSessionThatVerifies.sessionParams.deviceId, aliceSessionThatReceivesCanceledEvent.sessionParams.deviceId),
+ )
+
+ testHelper.waitWithLatch { latch ->
+ testHelper.retryPeriodicallyWithLatch(latch) {
+ val requests = serviceOfUserWhoReceivesCancellation.getExistingVerificationRequests(aliceSessionToVerify.myUserId)
+ requests.any { it.cancelConclusion == CancelCode.AcceptedByAnotherDevice }
+ }
+ }
+
+ testHelper.signOutAndClose(aliceSessionToVerify)
+ testHelper.signOutAndClose(aliceSessionThatVerifies)
+ testHelper.signOutAndClose(aliceSessionThatReceivesCanceledEvent)
+ }
}
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 d5b4a07fc0..e407c1b42d 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
@@ -140,9 +140,24 @@ class TimelineForwardPaginationTest : InstrumentedTest {
aliceTimeline.hasMoreToLoad(Timeline.Direction.BACKWARDS).shouldBeFalse()
assertEquals(EventType.STATE_ROOM_CREATE, snapshot.lastOrNull()?.root?.getClearType())
- // 6 for room creation item (backward pagination), 1 for the context, and 50 for the forward pagination
- // 6 + 1 + 50
- assertEquals(57, snapshot.size)
+
+ // We explicitly test all the types we expect here, as we expect 51 messages and "some" state events
+ // But state events can change over time. So this acts as a kinda documentation of what we expect and
+ // provides a good error message if it doesn't match
+
+ val snapshotTypes = mutableMapOf()
+ snapshot.groupingBy { it -> it.root.type }.eachCountTo(snapshotTypes)
+ // Some state events on room creation
+ assertEquals("m.room.name", 1, snapshotTypes.remove("m.room.name"))
+ assertEquals("m.room.guest_access", 1, snapshotTypes.remove("m.room.guest_access"))
+ assertEquals("m.room.history_visibility", 1, snapshotTypes.remove("m.room.history_visibility"))
+ assertEquals("m.room.join_rules", 1, snapshotTypes.remove("m.room.join_rules"))
+ assertEquals("m.room.power_levels", 1, snapshotTypes.remove("m.room.power_levels"))
+ assertEquals("m.room.create", 1, snapshotTypes.remove("m.room.create"))
+ assertEquals("m.room.member", 1, snapshotTypes.remove("m.room.member"))
+ // 50 from pagination + 1 context
+ assertEquals("m.room.message", 51, snapshotTypes.remove("m.room.message"))
+ assertEquals("Additional events found in timeline", setOf(), snapshotTypes.keys)
}
// Alice paginates once again FORWARD for 50 events
@@ -152,8 +167,8 @@ class TimelineForwardPaginationTest : InstrumentedTest {
val snapshot = runBlocking {
aliceTimeline.awaitPaginate(Timeline.Direction.FORWARDS, 50)
}
- // 6 for room creation item (backward pagination),and numberOfMessagesToSend (all the message of the room)
- snapshot.size == 6 + numberOfMessagesToSend &&
+ // 7 for room creation item (backward pagination),and numberOfMessagesToSend (all the message of the room)
+ snapshot.size == 7 + numberOfMessagesToSend &&
snapshot.checkSendOrder(message, numberOfMessagesToSend, 0)
// The timeline is fully loaded
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 6e5fed8df9..1a36adec44 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
@@ -74,8 +74,12 @@ class TimelinePreviousLastForwardTest : InstrumentedTest {
Timber.w(" event ${it.root}")
}
- // Ok, we have the 8 first messages of the initial sync (room creation and bob invite and join events)
- snapshot.size == 8
+ // Ok, we have the 9 first messages of the initial sync (room creation and bob invite and join events)
+ // create
+ // join alice
+ // power_levels, join_rules, history_visibility, guest_access, name
+ // invite, join bob
+ snapshot.size == 9
}
bobTimeline.addListener(eventsListener)
@@ -192,7 +196,7 @@ class TimelinePreviousLastForwardTest : InstrumentedTest {
Timber.w(" event ${it.root}")
}
- snapshot.size == 44 // 8 + 1 + 35
+ snapshot.size == 45 // 9 + 1 + 35
}
bobTimeline.addListener(eventsListener)
@@ -220,8 +224,8 @@ class TimelinePreviousLastForwardTest : InstrumentedTest {
// Bob can see the first event of the room (so Back pagination has worked)
snapshot.lastOrNull()?.root?.getClearType() == EventType.STATE_ROOM_CREATE &&
- // 8 for room creation item 60 message from Alice
- snapshot.size == 68 && // 8 + 60
+ // 9 for room creation item 60 message from Alice
+ snapshot.size == 69 && // 9 + 60U
snapshot.checkSendOrder(secondMessage, 30, 0) &&
snapshot.checkSendOrder(firstMessage, 30, 30)
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt
index 867e066e60..82f39806c0 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt
@@ -177,7 +177,7 @@ object MatrixPatterns {
* - "@alice:domain.org".getDomain() will return "domain.org"
* - "@bob:domain.org:3455".getDomain() will return "domain.org:3455"
*/
- fun String.getDomain(): String {
+ fun String.getServerName(): String {
if (BuildConfig.DEBUG && !isUserId(this)) {
// They are some invalid userId localpart in the wild, but the domain part should be there anyway
Timber.w("Not a valid user ID: $this")
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/data/HomeServerConnectionConfig.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/data/HomeServerConnectionConfig.kt
index c2c1f043bb..c3f0221bb8 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/data/HomeServerConnectionConfig.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/data/HomeServerConnectionConfig.kt
@@ -195,7 +195,7 @@ data class HomeServerConnectionConfig(
* - https://www.ssi.gouv.fr/uploads/2017/07/anssi-guide-recommandations_de_securite_relatives_a_tls-v1.2.pdf
* - https://developer.android.com/reference/javax/net/ssl/SSLEngine
*
- * @param tlsLimitations true to use Tls limitations
+ * @param tlsLimitations true to use Tls limitations
* @param enableCompatibilityMode set to true for Android < 20
* @return this builder
*/
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/content/ContentUrlResolver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/content/ContentUrlResolver.kt
index e59e676ed9..20f977e86e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/content/ContentUrlResolver.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/content/ContentUrlResolver.kt
@@ -36,7 +36,7 @@ interface ContentUrlResolver {
/**
* Get the actual URL for accessing the full-size image of a Matrix media content URI.
*
- * @param contentUrl the Matrix media content URI (in the form of "mxc://...").
+ * @param contentUrl the Matrix media content URI (in the form of "mxc://...").
* @return the URL to access the described resource, or null if the url is invalid.
*/
fun resolveFullSize(contentUrl: String?): String?
@@ -44,7 +44,7 @@ interface ContentUrlResolver {
/**
* Get the ResolvedMethod to download a URL.
*
- * @param contentUrl the Matrix media content URI (in the form of "mxc://...").
+ * @param contentUrl the Matrix media content URI (in the form of "mxc://...").
* @param elementToDecrypt Encryption data may be required if you use a content scanner
* @return the Method to access resource, or null if invalid
*/
@@ -54,9 +54,9 @@ interface ContentUrlResolver {
* Get the actual URL for accessing the thumbnail image of a given Matrix media content URI.
*
* @param contentUrl the Matrix media content URI (in the form of "mxc://...").
- * @param width the desired width
- * @param height the desired height
- * @param method the desired method (METHOD_CROP or METHOD_SCALE)
+ * @param width the desired width
+ * @param height the desired height
+ * @param method the desired method (METHOD_CROP or METHOD_SCALE)
* @return the URL to access the described resource, or null if the url is invalid.
*/
fun resolveThumbnail(contentUrl: String?, width: Int, height: Int, method: ThumbnailMethod): String?
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/contentscanner/ContentScannerService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/contentscanner/ContentScannerService.kt
index 7a85a89058..22250628d5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/contentscanner/ContentScannerService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/contentscanner/ContentScannerService.kt
@@ -33,7 +33,7 @@ interface ContentScannerService {
/**
* Get the current public curve25519 key that the AV server is advertising.
- * @param callback on success callback containing the server public key
+ * @param forceDownload true to force the SDK to download again the server public key
*/
suspend fun getServerPublicKey(forceDownload: Boolean = false): String?
suspend fun getScanResultForAttachment(mxcUrl: String, fileInfo: ElementToDecrypt? = null): ScanStatusInfo
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/keysbackup/KeysBackupService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/keysbackup/KeysBackupService.kt
index 0d40490c3e..9029c7f8a3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/keysbackup/KeysBackupService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/keysbackup/KeysBackupService.kt
@@ -34,7 +34,7 @@ interface KeysBackupService {
* Create a new keys backup version and enable it, using the information return from [prepareKeysBackupVersion].
*
* @param keysBackupCreationInfo the info object from [prepareKeysBackupVersion].
- * @param callback Asynchronous callback
+ * @param callback Asynchronous callback
*/
fun createKeysBackupVersion(keysBackupCreationInfo: MegolmBackupCreationInfo,
callback: MatrixCallback)
@@ -122,7 +122,7 @@ interface KeysBackupService {
* Delete a keys backup version. It will delete all backed up keys on the server, and the backup itself.
* If we are backing up to this version. Backup will be stopped.
*
- * @param version the backup version to delete.
+ * @param version the backup version to delete.
* @param callback Asynchronous callback
*/
fun deleteBackup(version: String,
@@ -173,12 +173,12 @@ interface KeysBackupService {
/**
* Restore a backup with a recovery key from a given backup version stored on the homeserver.
*
- * @param keysVersionResult the backup version to restore from.
- * @param recoveryKey the recovery key to decrypt the retrieved backup.
- * @param roomId the id of the room to get backup data from.
- * @param sessionId the id of the session to restore.
+ * @param keysVersionResult the backup version to restore from.
+ * @param recoveryKey the recovery key to decrypt the retrieved backup.
+ * @param roomId the id of the room to get backup data from.
+ * @param sessionId the id of the session to restore.
* @param stepProgressListener the step progress listener
- * @param callback Callback. It provides the number of found keys and the number of successfully imported keys.
+ * @param callback Callback. It provides the number of found keys and the number of successfully imported keys.
*/
fun restoreKeysWithRecoveryKey(keysVersionResult: KeysVersionResult,
recoveryKey: String, roomId: String?,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/IncomingRoomKeyRequest.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/IncomingRoomKeyRequest.kt
index 0c19d275cc..4ff196dd07 100755
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/IncomingRoomKeyRequest.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/IncomingRoomKeyRequest.kt
@@ -48,8 +48,7 @@ data class IncomingRoomKeyRequest(
/**
* Factory.
*
- * @param event the event
- * @param currentTimeMillis the current time in milliseconds
+ * @param trail the AuditTrail data
*/
fun fromEvent(trail: AuditTrail): IncomingRoomKeyRequest? {
return trail
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/MXUsersDevicesMap.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/MXUsersDevicesMap.kt
index 744fe74d0d..736ae6b318 100755
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/MXUsersDevicesMap.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/MXUsersDevicesMap.kt
@@ -46,8 +46,8 @@ class MXUsersDevicesMap {
/**
* Provides the object for a device id and a user Id.
*
+ * @param userId the user id
* @param deviceId the device id
- * @param userId the object id
* @return the object
*/
fun getObject(userId: String?, deviceId: String?): E? {
@@ -59,9 +59,9 @@ class MXUsersDevicesMap {
/**
* Set an object for a dedicated user Id and device Id.
*
- * @param userId the user Id
+ * @param userId the user Id
* @param deviceId the device id
- * @param o the object to set
+ * @param o the object to set
*/
fun setObject(userId: String?, deviceId: String?, o: E?) {
if (null != o && userId?.isNotBlank() == true && deviceId?.isNotBlank() == true) {
@@ -73,8 +73,8 @@ class MXUsersDevicesMap {
/**
* Defines the objects map for a user Id.
*
+ * @param userId the user id
* @param objectsPerDevices the objects maps
- * @param userId the user id
*/
fun setObjects(userId: String?, objectsPerDevices: Map?) {
if (!userId.isNullOrBlank()) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/CancelCode.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/CancelCode.kt
index 5a025f37e1..e4716d7794 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/CancelCode.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/CancelCode.kt
@@ -28,7 +28,8 @@ enum class CancelCode(val value: String, val humanReadable: String) {
MismatchedKeys("m.key_mismatch", "Key mismatch"),
UserError("m.user_error", "User error"),
MismatchedUser("m.user_mismatch", "User mismatch"),
- QrCodeInvalid("m.qr_code.invalid", "Invalid QR code")
+ QrCodeInvalid("m.qr_code.invalid", "Invalid QR code"),
+ AcceptedByAnotherDevice("m.accepted", "Verification request accepted by another device")
}
fun safeValueOf(code: String?): CancelCode {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt
index 16bdbd3432..7124d8a1a3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt
@@ -26,6 +26,7 @@ import org.matrix.android.sdk.api.session.crypto.model.OlmDecryptionResult
import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent
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.MessageBeaconLocationDataContent
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
import org.matrix.android.sdk.api.session.room.model.message.MessageStickerContent
@@ -375,11 +376,11 @@ fun Event.getRelationContent(): RelationDefaultContent? {
content.toModel()?.relatesTo
} else {
content.toModel()?.relatesTo ?: run {
- // Special case to handle stickers, while there is only a local msgtype for stickers
- if (getClearType() == EventType.STICKER) {
- getClearContent().toModel()?.relatesTo
- } else {
- null
+ // Special cases when there is only a local msgtype for some event types
+ when (getClearType()) {
+ EventType.STICKER -> getClearContent().toModel()?.relatesTo
+ in EventType.BEACON_LOCATION_DATA -> getClearContent().toModel()?.relatesTo
+ else -> null
}
}
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/file/FileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/file/FileService.kt
index 84a9990826..a7c81136e3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/file/FileService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/file/FileService.kt
@@ -33,7 +33,7 @@ interface FileService {
/**
* The original file is in cache, but the decrypted files can be deleted for security reason.
* To decrypt the file again, call [downloadFile], the encrypted file will not be downloaded again
- * @param decryptedFileInCache true if the decrypted file is available. Always true for clear files.
+ * @property decryptedFileInCache true if the decrypted file is available. Always true for clear files.
*/
data class InCache(val decryptedFileInCache: Boolean) : FileState()
object Downloading : FileState()
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityService.kt
index c03b42e6c8..2fb35d38e3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityService.kt
@@ -74,6 +74,7 @@ interface IdentityService {
/**
* Submit the code that the identity server has sent to the user (in email or SMS).
* Once successful, you will have to call [finalizeBindThreePid]
+ * @param threePid the three pid
* @param code the code sent to the user
*/
suspend fun submitValidationToken(threePid: ThreePid, code: String)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/integrationmanager/IntegrationManagerService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/integrationmanager/IntegrationManagerService.kt
index 60af93888e..5b15a0cb13 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/integrationmanager/IntegrationManagerService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/integrationmanager/IntegrationManagerService.kt
@@ -99,6 +99,7 @@ interface IntegrationManagerService {
* Offers to allow or disallow a native widget domain.
* @param widgetType the widget type to check for
* @param domain the domain to check for
+ * @param allowed true or false
*/
suspend fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/MatrixLinkify.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/MatrixLinkify.kt
index c5d919407a..c428e40203 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/MatrixLinkify.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/MatrixLinkify.kt
@@ -29,6 +29,7 @@ object MatrixLinkify {
* Find the matrix spans i.e matrix id , user id ... to display them as URL.
*
* @param spannable the text in which the matrix items has to be clickable.
+ * @param callback listener to be notified when the span is clicked
*/
@Suppress("UNUSED_PARAMETER")
fun addLinks(spannable: Spannable, callback: MatrixPermalinkSpan.Callback?): Boolean {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/MatrixPermalinkSpan.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/MatrixPermalinkSpan.kt
index 2f8f5f99a5..48b30dfa21 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/MatrixPermalinkSpan.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/MatrixPermalinkSpan.kt
@@ -22,8 +22,8 @@ import org.matrix.android.sdk.api.session.permalinks.MatrixPermalinkSpan.Callbac
/**
* This MatrixPermalinkSpan is a clickable span which use a [Callback] to communicate back.
- * @param url the permalink url tied to the span
- * @param callback the callback to use.
+ * @property url the permalink url tied to the span
+ * @property callback the callback to use.
*/
class MatrixPermalinkSpan(private val url: String,
private val callback: Callback? = null) : ClickableSpan() {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkService.kt
index b49b80df09..1788bf7bd2 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkService.kt
@@ -60,6 +60,7 @@ interface PermalinkService {
* Creates a permalink for a roomId, including the via parameters.
*
* @param roomId the room id
+ * @param viaServers the via parameter
* @param forceMatrixTo whether we should force using matrix.to base URL
*
* @return the permalink, or null in case of error
@@ -70,7 +71,7 @@ interface PermalinkService {
* Creates a permalink for an event. If you have an event you can use [createPermalink]
* Ex: "https://matrix.to/#/!nbzmcXAqpxBXjAdgoX:matrix.org/$1531497316352799BevdV:matrix.org?via=matrix.org"
*
- * @param roomId the id of the room
+ * @param roomId the id of the room
* @param eventId the id of the event
* @param forceMatrixTo whether we should force using matrix.to base URL
*
@@ -90,7 +91,7 @@ interface PermalinkService {
* Creates a HTML or Markdown mention span template. Can be used to replace a mention with a permalink to mentioned user.
* Ex: "%2\$s" or "[%2\$s](https://matrix.to/#/%1\$s)"
*
- * @param type: type of template to create
+ * @param type type of template to create
* @param forceMatrixTo whether we should force using matrix.to base URL
*
* @return the created template
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt
index 5f9857eb2f..5cb7857021 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt
@@ -47,12 +47,12 @@ interface PushersService {
* Add a new Email pusher.
* Ref: https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-pushers-set
*
- * @param email The email address to send notifications to.
- * @param lang The preferred language for receiving notifications (e.g. "en" or "en-US").
- * @param emailBranding The branding placeholder to include in the email communications.
- * @param appDisplayName A human readable string that will allow the user to identify what application owns this pusher.
+ * @param email The email address to send notifications to.
+ * @param lang The preferred language for receiving notifications (e.g. "en" or "en-US").
+ * @param emailBranding The branding placeholder to include in the email communications.
+ * @param appDisplayName A human readable string that will allow the user to identify what application owns this pusher.
* @param deviceDisplayName A human readable string that will allow the user to identify what device owns this pusher.
- * @param append If true, the homeserver should add another pusher with the given pushkey and App ID in addition
+ * @param append If true, the homeserver should add another pusher with the given pushkey and App ID in addition
* to any others with different user IDs. Otherwise, the homeserver must remove any other pushers
* with the same App ID and pushkey for different users. Typically We always want to append for
* email pushers since we don't want to stop other accounts notifying to the same email address.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushrules/PushRuleService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushrules/PushRuleService.kt
index bc4860be11..7ffbc89559 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushrules/PushRuleService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushrules/PushRuleService.kt
@@ -34,10 +34,11 @@ interface PushRuleService {
/**
* Enables/Disables a push rule and updates the actions if necessary.
+ * @param kind the rule kind
+ * @param ruleId the rule id
* @param enable Enables/Disables the rule
* @param actions Actions to update if not null
*/
-
suspend fun updatePushRuleActions(kind: RuleKind, ruleId: String, enable: Boolean, actions: List?)
suspend fun removePushRule(kind: RuleKind, ruleId: String)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushrules/rest/RuleSet.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushrules/rest/RuleSet.kt
index 5bf42b8252..9498ed002c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushrules/rest/RuleSet.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushrules/rest/RuleSet.kt
@@ -67,7 +67,7 @@ data class RuleSet(
/**
* Find a rule from its rule Id.
*
- * @param rules the rules list.
+ * @param rules the rules list.
* @param ruleId the rule Id.
* @return the bing rule if it exists, else null.
*/
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/crypto/RoomCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/crypto/RoomCryptoService.kt
index 6967e0c455..6064643820 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/crypto/RoomCryptoService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/crypto/RoomCryptoService.kt
@@ -28,7 +28,8 @@ interface RoomCryptoService {
/**
* Enable encryption of the room.
- * @param Use force to ensure that this algorithm will be used. Otherwise this call
+ * @param algorithm the algorithm to set, default to [MXCRYPTO_ALGORITHM_MEGOLM]
+ * @param force Use force to ensure that this algorithm will be used. Otherwise this call
* will throw if encryption is already setup or if the algorithm is not supported. Only to
* be used by admins to fix misconfigured encryption.
*/
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/relation/RelationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/relation/RelationService.kt
index 0d094b835b..02c597ee63 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/relation/RelationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/relation/RelationService.kt
@@ -71,8 +71,8 @@ interface RelationService {
/**
* Edit a poll.
- * @param pollType indicates open or closed polls
* @param targetEvent The poll event to edit
+ * @param pollType indicates open or closed polls
* @param question The edited question
* @param options The edited options
*/
@@ -84,7 +84,9 @@ interface RelationService {
/**
* Edit a text message body. Limited to "m.text" contentType.
* @param targetEvent The event to edit
+ * @param msgType the message type
* @param newBodyText The edited body
+ * @param newBodyAutoMarkdown true to parse markdown on the new body
* @param compatibilityBodyText The text that will appear on clients that don't support yet edition
*/
fun editTextMessage(targetEvent: TimelineEvent,
@@ -153,8 +155,8 @@ interface RelationService {
* @param rootThreadEventId the root thread eventId
* @param replyInThreadText the reply text
* @param msgType the message type: MessageType.MSGTYPE_TEXT (default) or MessageType.MSGTYPE_EMOTE
- * @param formattedText The formatted body using MessageType#FORMAT_MATRIX_HTML
* @param autoMarkdown If true, the SDK will generate a formatted HTML message from the body text if markdown syntax is present
+ * @param formattedText The formatted body using MessageType#FORMAT_MATRIX_HTML
* @param eventReplied the event referenced by the reply within a thread
*/
fun replyInThread(rootThreadEventId: String,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/PowerLevelsHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/PowerLevelsHelper.kt
index 165a912b7f..36993074aa 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/PowerLevelsHelper.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/powerlevels/PowerLevelsHelper.kt
@@ -58,7 +58,7 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) {
/**
* Tell if an user can send an event of a certain type.
*
- * @param userId the id of the user to check for.
+ * @param userId the id of the user to check for.
* @param isState true if the event is a state event (ie. state key is not null)
* @param eventType the event type to check for
* @return true if the user can send this type of event
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/read/ReadService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/read/ReadService.kt
index 036628c02f..dac1a1a773 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/read/ReadService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/read/ReadService.kt
@@ -71,7 +71,7 @@ interface ReadService {
/**
* Returns a live list of read receipts for a given event.
- * @param eventId: the event
+ * @param eventId the event
*/
fun getEventReadReceiptsLive(eventId: String): LiveData>
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt
index 4bb8abef8a..c2e3ded2fa 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt
@@ -62,6 +62,7 @@ interface SendService {
* @param quotedEvent The event to which we will quote it's content.
* @param text the text message to send
* @param autoMarkdown If true, the SDK will generate a formatted HTML message from the body text if markdown syntax is present
+ * @param rootThreadEventId when this param is not null, the message will be sent in this specific thread
* @return a [Cancelable]
*/
fun sendQuotedTextMessage(quotedEvent: TimelineEvent, text: String, autoMarkdown: Boolean, rootThreadEventId: String? = null): Cancelable
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt
index f6b56128d3..c79171f156 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt
@@ -90,23 +90,29 @@ interface StateService {
/**
* Get a state event of the room.
+ * @param eventType An eventType.
+ * @param stateKey the query which will be done on the stateKey
*/
fun getStateEvent(eventType: String, stateKey: QueryStringValue = QueryStringValue.NoCondition): Event?
/**
* Get a live state event of the room.
+ * @param eventType An eventType.
+ * @param stateKey the query which will be done on the stateKey
*/
fun getStateEventLive(eventType: String, stateKey: QueryStringValue = QueryStringValue.NoCondition): LiveData>
/**
* Get state events of the room.
* @param eventTypes Set of eventType. If empty, all state events will be returned
+ * @param stateKey the query which will be done on the stateKey
*/
fun getStateEvents(eventTypes: Set, stateKey: QueryStringValue = QueryStringValue.NoCondition): List
/**
* Get live state events of the room.
* @param eventTypes Set of eventType to observe. If empty, all state events will be observed
+ * @param stateKey the query which will be done on the stateKey
*/
fun getStateEventsLive(eventTypes: Set, stateKey: QueryStringValue = QueryStringValue.NoCondition): LiveData>
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt
index b87bc25435..d4ade9b5b9 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt
@@ -30,6 +30,7 @@ import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary
import org.matrix.android.sdk.api.session.room.model.ReadReceipt
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent
+import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
import org.matrix.android.sdk.api.session.room.model.message.MessageStickerContent
@@ -89,6 +90,7 @@ data class TimelineEvent(
/**
* Get the metadata associated with a key.
+ * @param T type to cast the metadata to
* @param key the key to get the metadata
* @return the metadata
*/
@@ -140,6 +142,7 @@ fun TimelineEvent.getLastMessageContent(): MessageContent? {
EventType.STICKER -> root.getClearContent().toModel()
in EventType.POLL_START -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel()
in EventType.STATE_ROOM_BEACON_INFO -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel()
+ in EventType.BEACON_LOCATION_DATA -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel()
else -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel()
}
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/securestorage/SharedSecretStorageService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/securestorage/SharedSecretStorageService.kt
index 528e071966..e3a9860523 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/securestorage/SharedSecretStorageService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/securestorage/SharedSecretStorageService.kt
@@ -92,7 +92,7 @@ interface SharedSecretStorageService {
* Clients MUST ensure that the key is trusted before using it to encrypt secrets.
*
* @param name The name of the secret
- * @param secret The secret contents.
+ * @param secretBase64 The secret contents.
* @param keys The list of (ID,privateKey) of the keys to use to encrypt the secret.
*/
suspend fun storeSecret(name: String, secretBase64: String, keys: List)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt
index 8f16b3b9c3..38e55664d2 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt
@@ -44,8 +44,8 @@ interface SpaceService {
roomAliasLocalPart: String? = null): String
/**
- * Get a space from a roomId.
- * @param spaceId the roomId to look for.
+ * Get a space from a spaceId.
+ * @param spaceId the spaceId to look for.
* @return a space with spaceId or null if room type is not space
*/
fun getSpace(spaceId: String): Space?
@@ -54,21 +54,24 @@ interface SpaceService {
* Try to resolve (peek) rooms and subspace in this space.
* Use this call get preview of children of this space, particularly useful to get a
* preview of rooms that you did not join yet.
+ * @param spaceId the spaceId to look for.
*/
suspend fun peekSpace(spaceId: String): SpacePeekResult
/**
* Get's information of a space by querying the server.
+ *
+ * @param spaceId the spaceId to look for.
* @param suggestedOnly If true, return only child events and rooms where the m.space.child event has suggested: true.
* @param limit a client-defined limit to the maximum number of rooms to return per page. Must be a non-negative integer.
- * @param from: Optional. Pagination token given to retrieve the next set of rooms. Note that if a pagination token is provided,
+ * @param from Optional. Pagination token given to retrieve the next set of rooms. Note that if a pagination token is provided,
* then the parameters given for suggested_only and max_depth must be the same.
+ * @param knownStateList when paginating, pass back the m.space.child state events
*/
suspend fun querySpaceChildren(spaceId: String,
suggestedOnly: Boolean? = null,
limit: Int? = null,
from: String? = null,
- // when paginating, pass back the m.space.child state events
knownStateList: List? = null): SpaceHierarchyData
/**
@@ -98,7 +101,10 @@ interface SpaceService {
/**
* Let this room declare that it has a parent.
+ * @param childRoomId the space to set as a child
+ * @param parentSpaceId the parentId which will be set
* @param canonical true if it should be the main parent of this room
+ * @param viaServers list of candidate servers that can be used to set the parent
* In practice, well behaved rooms should only have one canonical parent, but given this is not enforced:
* if multiple are present the client should select the one with the lowest room ID, as determined via a lexicographic utf-8 ordering.
*/
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/widgets/WidgetPostAPIMediator.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/widgets/WidgetPostAPIMediator.kt
index edb49f4797..0c224ff17c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/widgets/WidgetPostAPIMediator.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/widgets/WidgetPostAPIMediator.kt
@@ -49,7 +49,7 @@ interface WidgetPostAPIMediator {
/**
* Send a boolean response.
*
- * @param response the response
+ * @param response the response
* @param eventData the modular data
*/
fun sendBoolResponse(response: Boolean, eventData: JsonDict)
@@ -57,7 +57,7 @@ interface WidgetPostAPIMediator {
/**
* Send an integer response.
*
- * @param response the response
+ * @param response the response
* @param eventData the modular data
*/
fun sendIntegerResponse(response: Int, eventData: JsonDict)
@@ -65,8 +65,9 @@ interface WidgetPostAPIMediator {
/**
* Send an object response.
*
- * @param klass the class of the response
- * @param response the response
+ * @param T the generic type
+ * @param type the type of the response
+ * @param response the response
* @param eventData the modular data
*/
fun sendObjectResponse(type: Type, response: T?, eventData: JsonDict)
@@ -81,7 +82,7 @@ interface WidgetPostAPIMediator {
/**
* Send an error.
*
- * @param message the error message
+ * @param message the error message
* @param eventData the modular data
*/
fun sendError(message: String, eventData: JsonDict)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/widgets/WidgetService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/widgets/WidgetService.kt
index b06f8f7bc6..8ad6500d25 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/widgets/WidgetService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/widgets/WidgetService.kt
@@ -111,8 +111,8 @@ interface WidgetService {
/**
* Deactivate a widget in a room. It makes sure you have the rights to handle this.
*
- * @param roomId: the room where you want to deactivate the widget.
- * @param widgetId: the widget to deactivate.
+ * @param roomId the room where you want to deactivate the widget.
+ * @param widgetId the widget to deactivate.
*/
suspend fun destroyRoomWidget(roomId: String, widgetId: String)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
index f1cfe3fee5..02dfce04b5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
@@ -20,7 +20,7 @@ import android.net.Uri
import dagger.Lazy
import okhttp3.OkHttpClient
import org.matrix.android.sdk.api.MatrixPatterns
-import org.matrix.android.sdk.api.MatrixPatterns.getDomain
+import org.matrix.android.sdk.api.MatrixPatterns.getServerName
import org.matrix.android.sdk.api.auth.AuthenticationService
import org.matrix.android.sdk.api.auth.data.Credentials
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
@@ -381,7 +381,7 @@ internal class DefaultAuthenticationService @Inject constructor(
return getWellknownTask.execute(
GetWellknownTask.Params(
- domain = matrixId.getDomain(),
+ domain = matrixId.getServerName().substringBeforeLast(":"),
homeServerConnectionConfig = homeServerConnectionConfig.orWellKnownDefaults()
)
)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CryptoSessionInfoProvider.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CryptoSessionInfoProvider.kt
index b3e9eab988..eee1ee70aa 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CryptoSessionInfoProvider.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CryptoSessionInfoProvider.kt
@@ -46,6 +46,7 @@ internal class CryptoSessionInfoProvider @Inject constructor(
}
/**
+ * @param roomId the room Id
* @param allActive if true return joined as well as invited, if false, only joined
*/
fun getRoomUserIds(roomId: String, allActive: Boolean): List {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt
index 11fa93dbe0..824478f1d3 100755
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt
@@ -508,7 +508,7 @@ internal class DefaultCryptoService @Inject constructor(
/**
* Provides the device information for a user id and a device Id.
*
- * @param userId the user id
+ * @param userId the user id
* @param deviceId the device id
*/
override fun getDeviceInfo(userId: String, deviceId: String?): CryptoDeviceInfo? {
@@ -538,7 +538,7 @@ internal class DefaultCryptoService @Inject constructor(
/**
* Set the devices as known.
*
- * @param devices the devices. Note that the verified member of the devices in this list will not be updated by this method.
+ * @param devices the devices. Note that the verified member of the devices in this list will not be updated by this method.
* @param callback the asynchronous callback
*/
override fun setDevicesKnown(devices: List, callback: MatrixCallback?) {
@@ -576,8 +576,8 @@ internal class DefaultCryptoService @Inject constructor(
* Update the blocked/verified state of the given device.
*
* @param trustLevel the new trust level
- * @param userId the owner of the device
- * @param deviceId the unique identifier for the device.
+ * @param userId the owner of the device
+ * @param deviceId the unique identifier for the device.
*/
override fun setDeviceVerification(trustLevel: DeviceTrustLevel, userId: String, deviceId: String) {
setDeviceVerificationAction.handle(trustLevel, userId, deviceId)
@@ -586,10 +586,10 @@ internal class DefaultCryptoService @Inject constructor(
/**
* Configure a room to use encryption.
*
- * @param roomId the room id to enable encryption in.
- * @param algorithm the encryption config for the room.
+ * @param roomId the room id to enable encryption in.
+ * @param algorithm the encryption config for the room.
* @param inhibitDeviceQuery true to suppress device list query for users in the room (for now)
- * @param membersId list of members to start tracking their devices
+ * @param membersId list of members to start tracking their devices
* @return true if the operation succeeds.
*/
private suspend fun setEncryptionInRoom(roomId: String,
@@ -687,9 +687,9 @@ internal class DefaultCryptoService @Inject constructor(
* Encrypt an event content according to the configuration of the room.
*
* @param eventContent the content of the event.
- * @param eventType the type of the event.
- * @param roomId the room identifier the event will be sent.
- * @param callback the asynchronous callback
+ * @param eventType the type of the event.
+ * @param roomId the room identifier the event will be sent.
+ * @param callback the asynchronous callback
*/
override fun encryptEventContent(eventContent: Content,
eventType: String,
@@ -742,7 +742,7 @@ internal class DefaultCryptoService @Inject constructor(
/**
* Decrypt an event.
*
- * @param event the raw event.
+ * @param event the raw event.
* @param timeline the id of the timeline where the event is decrypted. It is used to prevent replay attack.
* @return the MXEventDecryptionResult data, or throw in case of error
*/
@@ -754,7 +754,7 @@ internal class DefaultCryptoService @Inject constructor(
/**
* Decrypt an event asynchronously.
*
- * @param event the raw event.
+ * @param event the raw event.
* @param timeline the id of the timeline where the event is decrypted. It is used to prevent replay attack.
* @param callback the callback to return data or null
*/
@@ -765,7 +765,7 @@ internal class DefaultCryptoService @Inject constructor(
/**
* Decrypt an event.
*
- * @param event the raw event.
+ * @param event the raw event.
* @param timeline the id of the timeline where the event is decrypted. It is used to prevent replay attack.
* @return the MXEventDecryptionResult data, or null in case of error
*/
@@ -905,6 +905,7 @@ internal class DefaultCryptoService @Inject constructor(
/**
* Handle an m.room.encryption event.
*
+ * @param roomId the room Id
* @param event the encryption event.
*/
private fun onRoomEncryptionEvent(roomId: String, event: Event) {
@@ -928,6 +929,7 @@ internal class DefaultCryptoService @Inject constructor(
/**
* Handle a change in the membership state of a member of a room.
*
+ * @param roomId the room Id
* @param event the membership event causing the change
*/
private fun onRoomMembershipEvent(roomId: String, event: Event) {
@@ -996,7 +998,7 @@ internal class DefaultCryptoService @Inject constructor(
/**
* Export the crypto keys.
*
- * @param password the password
+ * @param password the password
* @param anIterationCount the encryption iteration count (0 means no encryption)
*/
private suspend fun exportRoomKeys(password: String, anIterationCount: Int): ByteArray {
@@ -1015,8 +1017,8 @@ internal class DefaultCryptoService @Inject constructor(
/**
* Import the room keys.
*
- * @param roomKeysAsArray the room keys as array.
- * @param password the password
+ * @param roomKeysAsArray the room keys as array.
+ * @param password the password
* @param progressListener the progress listener
* @return the result ImportRoomKeysResult
*/
@@ -1066,7 +1068,7 @@ internal class DefaultCryptoService @Inject constructor(
* A success means there is no unknown devices.
* If there are some unknown devices, a MXCryptoError.UnknownDevice exception is triggered.
*
- * @param userIds the user ids list
+ * @param userIds the user ids list
* @param callback the asynchronous callback.
*/
fun checkUnknownDevices(userIds: List, callback: MatrixCallback) {
@@ -1089,7 +1091,7 @@ internal class DefaultCryptoService @Inject constructor(
* If false, it can still be overridden per-room.
* If true, it overrides the per-room settings.
*
- * @param block true to unilaterally blacklist all
+ * @param block true to unilaterally blacklist all
*/
override fun setGlobalBlacklistUnverifiedDevices(block: Boolean) {
cryptoStore.setGlobalBlacklistUnverifiedDevices(block)
@@ -1129,8 +1131,8 @@ internal class DefaultCryptoService @Inject constructor(
/**
* Manages the room black-listing for unverified devices.
*
- * @param roomId the room id
- * @param add true to add the room id to the list, false to remove it.
+ * @param roomId the room id
+ * @param add true to add the room id to the list, false to remove it.
*/
private fun setRoomBlacklistUnverifiedDevices(roomId: String, add: Boolean) {
val roomIds = cryptoStore.getRoomsListBlacklistUnverifiedDevices().toMutableList()
@@ -1149,7 +1151,7 @@ internal class DefaultCryptoService @Inject constructor(
/**
* Add this room to the ones which don't encrypt messages to unverified devices.
*
- * @param roomId the room id
+ * @param roomId the room id
*/
override fun setRoomBlacklistUnverifiedDevices(roomId: String) {
setRoomBlacklistUnverifiedDevices(roomId, true)
@@ -1158,7 +1160,7 @@ internal class DefaultCryptoService @Inject constructor(
/**
* Remove this room to the ones which don't encrypt messages to unverified devices.
*
- * @param roomId the room id
+ * @param roomId the room id
*/
override fun setRoomUnBlacklistUnverifiedDevices(roomId: String) {
setRoomBlacklistUnverifiedDevices(roomId, false)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DeviceListManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DeviceListManager.kt
index cd4e2a6d52..18b815b3d8 100755
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DeviceListManager.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DeviceListManager.kt
@@ -170,7 +170,7 @@ internal class DeviceListManager @Inject constructor(
* Update the devices list statuses.
*
* @param changed the user ids list which have new devices
- * @param left the user ids list which left a room
+ * @param left the user ids list which left a room
*/
fun handleDeviceListsChanges(changed: Collection, left: Collection) {
Timber.v("## CRYPTO: handleDeviceListsChanges changed: ${changed.logLimit()} / left: ${left.logLimit()}")
@@ -223,7 +223,7 @@ internal class DeviceListManager @Inject constructor(
/**
* The keys download succeeded.
*
- * @param userIds the userIds list
+ * @param userIds the userIds list
* @param failures the failure map.
*/
private fun onKeysDownloadSucceed(userIds: List, failures: Map>?): MXUsersDevicesMap {
@@ -276,7 +276,7 @@ internal class DeviceListManager @Inject constructor(
* Download the device keys for a list of users and stores the keys in the MXStore.
* It must be called in getEncryptingThreadHandler() thread.
*
- * @param userIds The users to fetch.
+ * @param userIds The users to fetch.
* @param forceDownload Always download the keys even if cached.
*/
suspend fun downloadKeys(userIds: List?, forceDownload: Boolean): MXUsersDevicesMap {
@@ -421,9 +421,9 @@ internal class DeviceListManager @Inject constructor(
* Validate device keys.
* This method must called on getEncryptingThreadHandler() thread.
*
- * @param deviceKeys the device keys to validate.
- * @param userId the id of the user of the device.
- * @param deviceId the id of the device.
+ * @param deviceKeys the device keys to validate.
+ * @param userId the id of the user of the device.
+ * @param deviceId the id of the device.
* @param previouslyStoredDeviceKeys the device keys we received before for this device
* @return true if succeeds
*/
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt
index d6f881211c..cb61bbe1de 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt
@@ -72,7 +72,7 @@ internal class EventDecryptor @Inject constructor(
/**
* Decrypt an event.
*
- * @param event the raw event.
+ * @param event the raw event.
* @param timeline the id of the timeline where the event is decrypted. It is used to prevent replay attack.
* @return the MXEventDecryptionResult data, or throw in case of error
*/
@@ -84,7 +84,7 @@ internal class EventDecryptor @Inject constructor(
/**
* Decrypt an event asynchronously.
*
- * @param event the raw event.
+ * @param event the raw event.
* @param timeline the id of the timeline where the event is decrypted. It is used to prevent replay attack.
* @param callback the callback to return data or null
*/
@@ -100,7 +100,7 @@ internal class EventDecryptor @Inject constructor(
/**
* Decrypt an event.
*
- * @param event the raw event.
+ * @param event the raw event.
* @param timeline the id of the timeline where the event is decrypted. It is used to prevent replay attack.
* @return the MXEventDecryptionResult data, or null in case of error
*/
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXMegolmExportEncryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXMegolmExportEncryption.kt
index e4a0f0376e..e0d6c25d70 100755
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXMegolmExportEncryption.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXMegolmExportEncryption.kt
@@ -68,7 +68,7 @@ internal object MXMegolmExportEncryption {
/**
* Decrypt a megolm key file.
*
- * @param data the data to decrypt
+ * @param data the data to decrypt
* @param password the password.
* @return the decrypted output.
* @throws Exception the failure reason
@@ -138,9 +138,9 @@ internal object MXMegolmExportEncryption {
/**
* Encrypt a string into the megolm export format.
*
- * @param data the data to encrypt.
- * @param password the password
- * @param kdf_rounds the iteration count
+ * @param data the data to encrypt.
+ * @param password the password
+ * @param kdfRounds the iteration count
* @return the encrypted data
* @throws Exception the failure reason
*/
@@ -304,9 +304,9 @@ internal object MXMegolmExportEncryption {
/**
* Derive the AES and HMAC-SHA-256 keys for the file.
*
- * @param salt salt for pbkdf
+ * @param salt salt for pbkdf
* @param iterations number of pbkdf iterations
- * @param password password
+ * @param password password
* @return the derived keys
*/
@Throws(Exception::class)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt
index 779113a7bc..37d9a9f567 100755
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt
@@ -261,7 +261,7 @@ internal class MXOlmDevice @Inject constructor(
* The new session will be stored in the MXStore.
*
* @param theirIdentityKey the remote user's Curve25519 identity key
- * @param theirOneTimeKey the remote user's one-time Curve25519 key
+ * @param theirOneTimeKey the remote user's one-time Curve25519 key
* @return the session id for the outbound session.
*/
fun createOutboundSession(theirIdentityKey: String, theirOneTimeKey: String): String? {
@@ -300,8 +300,8 @@ internal class MXOlmDevice @Inject constructor(
* Generate a new inbound session, given an incoming message.
*
* @param theirDeviceIdentityKey the remote user's Curve25519 identity key.
- * @param messageType the message_type field from the received message (must be 0).
- * @param ciphertext base64-encoded body from the received message.
+ * @param messageType the message_type field from the received message (must be 0).
+ * @param ciphertext base64-encoded body from the received message.
* @return {{payload: string, session_id: string}} decrypted payload, and session id of new session.
*/
fun createInboundSession(theirDeviceIdentityKey: String, messageType: Int, ciphertext: String): Map? {
@@ -395,8 +395,8 @@ internal class MXOlmDevice @Inject constructor(
* Encrypt an outgoing message using an existing session.
*
* @param theirDeviceIdentityKey the Curve25519 identity key for the remote device.
- * @param sessionId the id of the active session
- * @param payloadString the payload to be encrypted and sent
+ * @param sessionId the id of the active session
+ * @param payloadString the payload to be encrypted and sent
* @return the cipher text
*/
suspend fun encryptMessage(theirDeviceIdentityKey: String, sessionId: String, payloadString: String): Map? {
@@ -428,10 +428,10 @@ internal class MXOlmDevice @Inject constructor(
/**
* Decrypt an incoming message using an existing session.
*
- * @param ciphertext the base64-encoded body from the received message.
- * @param messageType message_type field from the received message.
+ * @param ciphertext the base64-encoded body from the received message.
+ * @param messageType message_type field from the received message.
+ * @param sessionId the id of the active session.
* @param theirDeviceIdentityKey the Curve25519 identity key for the remote device.
- * @param sessionId the id of the active session.
* @return the decrypted payload.
*/
@kotlin.jvm.Throws
@@ -461,9 +461,9 @@ internal class MXOlmDevice @Inject constructor(
* Determine if an incoming messages is a prekey message matching an existing session.
*
* @param theirDeviceIdentityKey the Curve25519 identity key for the remote device.
- * @param sessionId the id of the active session.
- * @param messageType message_type field from the received message.
- * @param ciphertext the base64-encoded body from the received message.
+ * @param sessionId the id of the active session.
+ * @param messageType message_type field from the received message.
+ * @param ciphertext the base64-encoded body from the received message.
* @return YES if the received message is a prekey message which matchesthe given session.
*/
fun matchesSession(theirDeviceIdentityKey: String, sessionId: String, messageType: Int, ciphertext: String): Boolean {
@@ -564,7 +564,7 @@ internal class MXOlmDevice @Inject constructor(
/**
* Encrypt an outgoing message with an outbound group session.
*
- * @param sessionId the id of the outbound group session.
+ * @param sessionId the id of the outbound group session.
* @param payloadString the payload to be encrypted and sent.
* @return ciphertext
*/
@@ -591,13 +591,13 @@ internal class MXOlmDevice @Inject constructor(
/**
* Add an inbound group session to the session store.
*
- * @param sessionId the session identifier.
- * @param sessionKey base64-encoded secret key.
- * @param roomId the id of the room in which this session will be used.
- * @param senderKey the base64-encoded curve25519 key of the sender.
+ * @param sessionId the session identifier.
+ * @param sessionKey base64-encoded secret key.
+ * @param roomId the id of the room in which this session will be used.
+ * @param senderKey the base64-encoded curve25519 key of the sender.
* @param forwardingCurve25519KeyChain Devices involved in forwarding this session to us.
- * @param keysClaimed Other keys the sender claims.
- * @param exportFormat true if the megolm keys are in export format
+ * @param keysClaimed Other keys the sender claims.
+ * @param exportFormat true if the megolm keys are in export format
* @return true if the operation succeeds.
*/
fun addInboundGroupSession(sessionId: String,
@@ -840,9 +840,9 @@ internal class MXOlmDevice @Inject constructor(
/**
* Verify an ed25519 signature on a JSON object.
*
- * @param key the ed25519 key.
+ * @param key the ed25519 key.
* @param jsonDictionary the JSON object which was signed.
- * @param signature the base64-encoded signature to be checked.
+ * @param signature the base64-encoded signature to be checked.
* @throws Exception the exception
*/
@Throws(Exception::class)
@@ -865,7 +865,7 @@ internal class MXOlmDevice @Inject constructor(
* Search an OlmSession.
*
* @param theirDeviceIdentityKey the device key
- * @param sessionId the session Id
+ * @param sessionId the session Id
* @return the olm session
*/
private fun getSessionForDevice(theirDeviceIdentityKey: String, sessionId: String): OlmSessionWrapper? {
@@ -879,9 +879,9 @@ internal class MXOlmDevice @Inject constructor(
* Extract an InboundGroupSession from the session store and do some check.
* inboundGroupSessionWithIdError describes the failure reason.
*
- * @param roomId the room where the session is used.
* @param sessionId the session identifier.
* @param senderKey the base64-encoded curve25519 key of the sender.
+ * @param roomId the room where the session is used.
* @return the inbound group session.
*/
fun getInboundGroupSession(sessionId: String?, senderKey: String?, roomId: String?): InboundGroupSessionHolder {
@@ -911,7 +911,7 @@ internal class MXOlmDevice @Inject constructor(
/**
* Determine if we have the keys for a given megolm session.
*
- * @param roomId room in which the message was received
+ * @param roomId room in which the message was received
* @param senderKey base64-encoded curve25519 key of the sender
* @param sessionId session identifier
* @return true if the unbound session keys are known.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OlmSessionStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OlmSessionStore.kt
index fe280416ea..4401a07192 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OlmSessionStore.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OlmSessionStore.kt
@@ -39,7 +39,7 @@ internal class OlmSessionStore @Inject constructor(private val store: IMXCryptoS
* Store a session between our own device and another device.
* This will be called after the session has been created but also every time it has been used
* in order to persist the correct state for next run
- * @param olmSessionWrapper the end-to-end session.
+ * @param olmSessionWrapper the end-to-end session.
* @param deviceKey the public key of the other device.
*/
@Synchronized
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/RoomDecryptorProvider.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/RoomDecryptorProvider.kt
index c2f494b4b3..a80bafbe79 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/RoomDecryptorProvider.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/RoomDecryptorProvider.kt
@@ -49,7 +49,7 @@ internal class RoomDecryptorProvider @Inject constructor(
* If we already have a decryptor for the given room and algorithm, return
* it. Otherwise try to instantiate it.
*
- * @param roomId the room id
+ * @param roomId the room id
* @param algorithm the crypto algorithm
* @return the decryptor
* // TODO Create another method for the case of roomId is null
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt
index fc211537a6..4c5720daf2 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt
@@ -29,7 +29,7 @@ internal class EnsureOlmSessionsForUsersAction @Inject constructor(private val o
/**
* Try to make sure we have established olm sessions for the given users.
- * @param users a list of user ids.
+ * @param users a list of user ids.
*/
suspend fun handle(users: List): MXUsersDevicesMap {
Timber.v("## ensureOlmSessionsForUsers() : ensureOlmSessionsForUsers $users")
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/MegolmSessionDataImporter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/MegolmSessionDataImporter.kt
index 22c4e59b18..67d73c21ed 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/MegolmSessionDataImporter.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/MegolmSessionDataImporter.kt
@@ -45,8 +45,8 @@ internal class MegolmSessionDataImporter @Inject constructor(private val olmDevi
* Must be call on the crypto coroutine thread
*
* @param megolmSessionsData megolm sessions.
- * @param fromBackup true if the imported keys are already backed up on the server.
- * @param progressListener the progress listener
+ * @param fromBackup true if the imported keys are already backed up on the server.
+ * @param progressListener the progress listener
* @return import room keys result
*/
@WorkerThread
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/MessageEncrypter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/MessageEncrypter.kt
index 9bbbab4992..919e38c391 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/MessageEncrypter.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/MessageEncrypter.kt
@@ -42,7 +42,7 @@ internal class MessageEncrypter @Inject constructor(
* This method must be called from the getCryptoHandler() thread.
*
* @param payloadFields fields to include in the encrypted payload.
- * @param deviceInfos list of device infos to encrypt for.
+ * @param deviceInfos list of device infos to encrypt for.
* @return the content for an m.room.encrypted event.
*/
suspend fun encryptMessage(payloadFields: Content, deviceInfos: List): EncryptedMessage {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXDecrypting.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXDecrypting.kt
index 34006ecfde..6847a46369 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXDecrypting.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXDecrypting.kt
@@ -29,7 +29,7 @@ internal interface IMXDecrypting {
/**
* Decrypt an event.
*
- * @param event the raw event.
+ * @param event the raw event.
* @param timeline the id of the timeline where the event is decrypted. It is used to prevent replay attack.
* @return the decryption information, or an error
*/
@@ -40,6 +40,7 @@ internal interface IMXDecrypting {
* Handle a key event.
*
* @param event the key event.
+ * @param defaultKeysBackupService the keys backup service
*/
fun onRoomKeyEvent(event: Event, defaultKeysBackupService: DefaultKeysBackupService) {}
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXEncrypting.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXEncrypting.kt
index 1d84120208..73ce5a5004 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXEncrypting.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXEncrypting.kt
@@ -27,8 +27,8 @@ internal interface IMXEncrypting {
* Encrypt an event content according to the configuration of the room.
*
* @param eventContent the content of the event.
- * @param eventType the type of the event.
- * @param userIds the room members the event will be sent to.
+ * @param eventType the type of the event.
+ * @param userIds the room members the event will be sent to.
* @return the encrypted content
*/
suspend fun encryptEventContent(eventContent: Content, eventType: String, userIds: List): Content
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXGroupEncryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXGroupEncryption.kt
index 6f488def0a..8cf01f1972 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXGroupEncryption.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXGroupEncryption.kt
@@ -38,7 +38,7 @@ internal interface IMXGroupEncryption {
* Re-shares a session key with devices if the key has already been
* sent to them.
*
- * @param sessionId The id of the outbound session to share.
+ * @param groupSessionId The id of the outbound session to share.
* @param userId The id of the user who owns the target device.
* @param deviceId The id of the target device.
* @param senderKey The key of the originating device for the session.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt
index c7f8acbff3..722462bf0e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt
@@ -177,6 +177,7 @@ internal class MXMegolmDecryption(
* Handle a key event.
*
* @param event the key event.
+ * @param defaultKeysBackupService the keys backup service
*/
override fun onRoomKeyEvent(event: Event, defaultKeysBackupService: DefaultKeysBackupService) {
Timber.tag(loggerTag.value).v("onRoomKeyEvent()")
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt
index 79e907945f..8b4e9df607 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt
@@ -198,7 +198,7 @@ internal class MXMegolmEncryption(
/**
* Share the device key to a list of users.
*
- * @param session the session info
+ * @param session the session info
* @param devicesByUsers the devices map
*/
private suspend fun shareKey(session: MXOutboundSessionInfo,
@@ -227,7 +227,7 @@ internal class MXMegolmEncryption(
/**
* Share the device keys of a an user.
*
- * @param session the session info
+ * @param session the session info
* @param devicesByUser the devices map
*/
private suspend fun shareUserDevicesKey(session: MXOutboundSessionInfo,
@@ -387,7 +387,7 @@ internal class MXMegolmEncryption(
* Get the list of devices which can encrypt data to.
* This method must be called in getDecryptingThreadHandler() thread.
*
- * @param userIds the user ids whose devices must be checked.
+ * @param userIds the user ids whose devices must be checked.
*/
private suspend fun getDevicesInRoom(userIds: List): DeviceInRoomInfo {
// We are happy to use a cached version here: we assume that if we already
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/olm/MXOlmDecryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/olm/MXOlmDecryption.kt
index 1e66fe84c9..23c8f0e905 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/olm/MXOlmDecryption.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/olm/MXOlmDecryption.kt
@@ -180,8 +180,8 @@ internal class MXOlmDecryption(
/**
* Attempt to decrypt an Olm message.
*
+ * @param message message object, with 'type' and 'body' fields.
* @param theirDeviceIdentityKey the Curve25519 identity key of the sender.
- * @param message message object, with 'type' and 'body' fields.
* @return payload, if decrypted successfully.
*/
private suspend fun decryptMessage(message: JsonDict, theirDeviceIdentityKey: String): String? {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/olm/MXOlmEncryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/olm/MXOlmEncryption.kt
index 3c9706abe1..bde1d65093 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/olm/MXOlmEncryption.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/olm/MXOlmEncryption.kt
@@ -70,7 +70,7 @@ internal class MXOlmEncryption(
/**
* Ensure that the session.
*
- * @param users the user ids list
+ * @param users the user ids list
*/
private suspend fun ensureSession(users: List) {
deviceListManager.downloadKeys(users, false)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/api/CryptoApi.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/api/CryptoApi.kt
index f21f5e05e1..f5ead35933 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/api/CryptoApi.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/api/CryptoApi.kt
@@ -103,7 +103,7 @@ internal interface CryptoApi {
* Claim one-time keys.
* Doc: https://matrix.org/docs/spec/client_server/r0.4.0.html#post-matrix-client-r0-keys-claim
*
- * @param params the params.
+ * @param body the Json body.
*/
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "keys/claim")
suspend fun claimOneTimeKeysForUsersDevices(@Body body: KeysClaimBody): KeysClaimResponse
@@ -112,9 +112,9 @@ internal interface CryptoApi {
* Send an event to a specific list of devices
* Doc: https://matrix.org/docs/spec/client_server/r0.4.0.html#put-matrix-client-r0-sendtodevice-eventtype-txnid
*
- * @param eventType the type of event to send
+ * @param eventType the type of event to send
* @param transactionId the transaction ID for this event
- * @param body the body
+ * @param body the body
*/
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "sendToDevice/{eventType}/{txnId}")
suspend fun sendToDevice(@Path("eventType") eventType: String,
@@ -126,7 +126,7 @@ internal interface CryptoApi {
* Doc: https://matrix.org/docs/spec/client_server/r0.4.0.html#delete-matrix-client-r0-devices-deviceid
*
* @param deviceId the device id
- * @param params the deletion parameters
+ * @param params the deletion parameters
*/
@HTTP(path = NetworkConstants.URI_API_PREFIX_PATH_R0 + "devices/{device_id}", method = "DELETE", hasBody = true)
suspend fun deleteDevice(@Path("device_id") deviceId: String,
@@ -137,7 +137,7 @@ internal interface CryptoApi {
* Doc: https://matrix.org/docs/spec/client_server/r0.4.0.html#put-matrix-client-r0-devices-deviceid
*
* @param deviceId the device id
- * @param params the params
+ * @param params the params
*/
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "devices/{device_id}")
suspend fun updateDeviceInfo(@Path("device_id") deviceId: String,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/attachments/MXEncryptedAttachments.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/attachments/MXEncryptedAttachments.kt
index b4cbd15109..7ff08cd127 100755
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/attachments/MXEncryptedAttachments.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/attachments/MXEncryptedAttachments.kt
@@ -159,6 +159,7 @@ internal object MXEncryptedAttachments {
* Encrypt an attachment stream.
* DO NOT USE for big files, it will load all in memory
* @param attachmentStream the attachment stream. Will be closed after this method call.
+ * @param clock a clock to retrieve current time
* @return the encryption file info
*/
fun encryptAttachment(attachmentStream: InputStream, clock: Clock): EncryptionResult {
@@ -231,7 +232,8 @@ internal object MXEncryptedAttachments {
*
* @param attachmentStream the attachment stream. Will be closed after this method call.
* @param elementToDecrypt the elementToDecrypt info
- * @param outputStream the outputStream where the decrypted attachment will be write.
+ * @param outputStream the outputStream where the decrypted attachment will be write.
+ * @param clock a clock to retrieve current time
* @return true in case of success, false in case of error
*/
fun decryptAttachment(attachmentStream: InputStream?,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt
index 5ea4695da2..813adf7459 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt
@@ -1105,6 +1105,7 @@ internal class DefaultKeysBackupService @Inject constructor(
*
* @param password the password.
* @param keysBackupData the backup and its auth data.
+ * @param progressListener listener to track progress
*
* @return the recovery key if successful, null in other cases
*/
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupPassword.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupPassword.kt
index d5bab33180..f821fdcf6d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupPassword.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupPassword.kt
@@ -44,6 +44,7 @@ internal data class GeneratePrivateKeyResult(
* Compute a private key from a password.
*
* @param password the password to use.
+ * @param progressListener a listener to track progress
*
* @return a {privateKey, salt, iterations} tuple.
*/
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/api/RoomKeysApi.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/api/RoomKeysApi.kt
index ea23be5923..d9c63b46ab 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/api/RoomKeysApi.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/api/RoomKeysApi.kt
@@ -60,19 +60,19 @@ internal interface RoomKeysApi {
* Get information about the given version.
* If not supported by the server, an error is returned: {"errcode":"M_NOT_FOUND","error":"No backup found"}
*
- * @param version version
+ * @param version version
*/
@GET(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "room_keys/version/{version}")
suspend fun getKeysBackupVersion(@Path("version") version: String): KeysVersionResult
/**
* Update information about the given version.
- * @param version version
+ * @param version version
* @param updateKeysBackupVersionBody the body
*/
@PUT(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "room_keys/version/{version}")
suspend fun updateKeysBackupVersion(@Path("version") version: String,
- @Body keysBackupVersionBody: UpdateKeysBackupVersionBody)
+ @Body updateKeysBackupVersionBody: UpdateKeysBackupVersionBody)
/* ==========================================================================================
* Storing keys
@@ -87,9 +87,9 @@ internal interface RoomKeysApi {
* flag (true is better than false), then by the first_message_index (a lower number is better), and finally by
* forwarded_count (a lower number is better).
*
- * @param roomId the room id
- * @param sessionId the session id
- * @param version the version of the backup
+ * @param roomId the room id
+ * @param sessionId the session id
+ * @param version the version of the backup
* @param keyBackupData the data to send
*/
@PUT(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "room_keys/keys/{roomId}/{sessionId}")
@@ -101,8 +101,8 @@ internal interface RoomKeysApi {
/**
* Store several keys for the given room, using the given backup version.
*
- * @param roomId the room id
- * @param version the version of the backup
+ * @param roomId the room id
+ * @param version the version of the backup
* @param roomKeysBackupData the data to send
*/
@PUT(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "room_keys/keys/{roomId}")
@@ -113,7 +113,7 @@ internal interface RoomKeysApi {
/**
* Store several keys, using the given backup version.
*
- * @param version the version of the backup
+ * @param version the version of the backup
* @param keysBackupData the data to send
*/
@PUT(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "room_keys/keys")
@@ -127,9 +127,9 @@ internal interface RoomKeysApi {
/**
* Retrieve the key for the given session in the given room from the backup.
*
- * @param roomId the room id
+ * @param roomId the room id
* @param sessionId the session id
- * @param version the version of the backup, or empty String to retrieve the last version
+ * @param version the version of the backup, or empty String to retrieve the last version
*/
@GET(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "room_keys/keys/{roomId}/{sessionId}")
suspend fun getRoomSessionData(@Path("roomId") roomId: String,
@@ -139,8 +139,8 @@ internal interface RoomKeysApi {
/**
* Retrieve all the keys for the given room from the backup.
*
- * @param roomId the room id
- * @param version the version of the backup, or empty String to retrieve the last version
+ * @param roomId the room id
+ * @param version the version of the backup, or empty String to retrieve the last version
*/
@GET(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "room_keys/keys/{roomId}")
suspend fun getRoomSessionsData(@Path("roomId") roomId: String,
@@ -149,7 +149,7 @@ internal interface RoomKeysApi {
/**
* Retrieve all the keys from the backup.
*
- * @param version the version of the backup, or empty String to retrieve the last version
+ * @param version the version of the backup, or empty String to retrieve the last version
*/
@GET(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "room_keys/keys")
suspend fun getSessionsData(@Query("version") version: String): KeysBackupData
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/MXKey.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/MXKey.kt
index 6bfa56ae8d..6b747d19f2 100755
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/MXKey.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/MXKey.kt
@@ -59,7 +59,7 @@ internal data class MXKey(
/**
* Returns a signature for an user Id and a signkey.
*
- * @param userId the user id
+ * @param userId the user id
* @param signkey the sign key
* @return the signature
*/
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt
index 480009dbce..9b1c785059 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt
@@ -164,16 +164,14 @@ internal interface IMXCryptoStore {
/**
* Store the end to end account for the logged-in user.
- *
- * @param account the account to save
*/
fun saveOlmAccount()
/**
* Retrieve a device for a user.
*
+ * @param userId the user's id.
* @param deviceId the device id.
- * @param userId the user's id.
* @return the device
*/
fun getUserDevice(userId: String, deviceId: String): CryptoDeviceInfo?
@@ -189,7 +187,7 @@ internal interface IMXCryptoStore {
/**
* Store the known devices for a user.
*
- * @param userId The user's id.
+ * @param userId The user's id.
* @param devices A map from device id to 'MXDevice' object for the device.
*/
fun storeUserDevices(userId: String, devices: Map?)
@@ -225,7 +223,7 @@ internal interface IMXCryptoStore {
/**
* Store the crypto algorithm for a room.
*
- * @param roomId the id of the room.
+ * @param roomId the id of the room.
* @param algorithm the algorithm.
*/
fun storeRoomAlgorithm(roomId: String, algorithm: String?)
@@ -253,7 +251,7 @@ internal interface IMXCryptoStore {
/**
* Store a session between the logged-in user and another device.
*
- * @param olmSessionWrapper the end-to-end session.
+ * @param olmSessionWrapper the end-to-end session.
* @param deviceKey the public key of the other device.
*/
fun storeSession(olmSessionWrapper: OlmSessionWrapper, deviceKey: String)
@@ -331,7 +329,7 @@ internal interface IMXCryptoStore {
/**
* Mark inbound group sessions as backed up on the user homeserver.
*
- * @param sessions the sessions
+ * @param olmInboundGroupSessionWrappers the sessions
*/
fun markBackupDoneForInboundGroupSessions(olmInboundGroupSessionWrappers: List)
@@ -361,7 +359,7 @@ internal interface IMXCryptoStore {
/**
* Get the tracking status of a specified userId devices.
*
- * @param userId the user id
+ * @param userId the user id
* @param defaultValue the default value
* @return the tracking status
*/
@@ -380,7 +378,9 @@ internal interface IMXCryptoStore {
/**
* Look for an existing outgoing room key request, and if none is found, add a new one.
*
- * @param request the request
+ * @param requestBody the request
+ * @param recipients list of recipients
+ * @param fromIndex start index
* @return either the same instance as passed in, or the existing one.
*/
fun getOrAddOutgoingRoomKeyRequest(requestBody: RoomKeyRequestBody, recipients: Map>, fromIndex: Int): OutgoingKeyRequest
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tools/HkdfSha256.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tools/HkdfSha256.kt
index 4ab7e0e30c..04fb6c4858 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tools/HkdfSha256.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tools/HkdfSha256.kt
@@ -35,7 +35,7 @@ internal object HkdfSha256 {
/**
* HkdfSha256-Extract(salt, IKM) -> PRK.
*
- * @param salt optional salt value (a non-secret random value);
+ * @param salt optional salt value (a non-secret random value);
* if not provided, it is set to a string of HashLen (size in octets) zeros.
* @param ikm input keying material
*/
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultVerificationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultVerificationService.kt
index 6da674d6e4..af48283767 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultVerificationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultVerificationService.kt
@@ -939,9 +939,25 @@ internal class DefaultVerificationService @Inject constructor(
updatePendingRequest(
existingRequest.copy(
- readyInfo = readyReq
+ readyInfo = readyReq
)
)
+
+ notifyOthersOfAcceptance(readyReq.transactionId, readyReq.fromDevice)
+ }
+
+ /**
+ * Gets a list of device ids excluding the current one.
+ */
+ private fun getMyOtherDeviceIds(): List = cryptoStore.getUserDevices(userId)?.keys?.filter { it != deviceId }.orEmpty()
+
+ /**
+ * Notifies other devices that the current verification transaction is being handled by [acceptedByDeviceId].
+ */
+ private fun notifyOthersOfAcceptance(transactionId: String, acceptedByDeviceId: String) {
+ val deviceIds = getMyOtherDeviceIds().filter { it != acceptedByDeviceId }
+ val transport = verificationTransportToDeviceFactory.createTransport(null)
+ transport.cancelTransaction(transactionId, userId, deviceIds, CancelCode.AcceptedByAnotherDevice)
}
private fun createQrCodeData(requestId: String?, otherUserId: String, otherDeviceId: String?): QrCodeData? {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransport.kt
index c12aea9d52..69dec12ef3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransport.kt
@@ -35,6 +35,11 @@ internal interface VerificationTransport {
onDone: (() -> Unit)?)
/**
+ * @param supportedMethods list of supported method by this client
+ * @param localId a local Id
+ * @param otherUserId the user id to send the verification request to
+ * @param roomId a room Id to use to send verification message
+ * @param toDevices list of device Ids
* @param callback will be called with eventId and ValidVerificationInfoRequest in case of success
*/
fun sendVerificationRequest(supportedMethods: List,
@@ -49,6 +54,11 @@ internal interface VerificationTransport {
otherUserDeviceId: String?,
code: CancelCode)
+ fun cancelTransaction(transactionId: String,
+ otherUserId: String,
+ otherUserDeviceIds: List,
+ code: CancelCode)
+
fun done(transactionId: String,
onDone: (() -> Unit)?)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportRoomMessage.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportRoomMessage.kt
index e32828af23..03df849d22 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportRoomMessage.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportRoomMessage.kt
@@ -160,6 +160,9 @@ internal class VerificationTransportRoomMessage(
}
}
+ override fun cancelTransaction(transactionId: String, otherUserId: String, otherUserDeviceIds: List, code: CancelCode) =
+ cancelTransaction(transactionId, otherUserId, null, code)
+
override fun done(transactionId: String,
onDone: (() -> Unit)?) {
Timber.d("## SAS sending done for $transactionId")
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportToDevice.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportToDevice.kt
index bc24ef2966..974adf3888 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportToDevice.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationTransportToDevice.kt
@@ -193,6 +193,27 @@ internal class VerificationTransportToDevice(
.executeBy(taskExecutor)
}
+ override fun cancelTransaction(transactionId: String, otherUserId: String, otherUserDeviceIds: List, code: CancelCode) {
+ Timber.d("## SAS canceling transaction $transactionId for reason $code")
+ val cancelMessage = KeyVerificationCancel.create(transactionId, code)
+ val contentMap = MXUsersDevicesMap()
+ val messages = otherUserDeviceIds.associateWith { cancelMessage }
+ contentMap.setObjects(otherUserId, messages)
+ sendToDeviceTask
+ .configureWith(SendToDeviceTask.Params(EventType.KEY_VERIFICATION_CANCEL, contentMap)) {
+ this.callback = object : MatrixCallback {
+ override fun onSuccess(data: Unit) {
+ Timber.v("## SAS verification [$transactionId] canceled for reason ${code.value}")
+ }
+
+ override fun onFailure(failure: Throwable) {
+ Timber.e(failure, "## SAS verification [$transactionId] failed to cancel.")
+ }
+ }
+ }
+ .executeBy(taskExecutor)
+ }
+
override fun createAccept(tid: String,
keyAgreementProtocol: String,
hash: String,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ThreadEventsHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ThreadEventsHelper.kt
index db3647c3fa..5db859bce2 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ThreadEventsHelper.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ThreadEventsHelper.kt
@@ -96,7 +96,9 @@ internal fun EventEntity.markEventAsRoot(
/**
* Count the number of threads for the provided root thread eventId, and finds the latest event message.
* Note: Redactions are handled by RedactionEventProcessor.
+ * @param realm the realm database
* @param rootThreadEventId The root eventId that will find the number of threads
+ * @param chunkEntity the chunk entity
* @return A ThreadSummary containing the counted threads and the latest event message
*/
internal fun EventEntity.threadSummaryInThread(realm: Realm, rootThreadEventId: String, chunkEntity: ChunkEntity?): Summary {
@@ -184,6 +186,7 @@ private fun findLatestSortedChunkEvent(chunk: ChunkEntity, rootThreadEventId: St
/**
* Find all TimelineEventEntity that are root threads for the specified room.
+ * @param realm the realm instance
* @param roomId The room that all stored root threads will be returned
*/
internal fun TimelineEventEntity.Companion.findAllThreadsForRoomId(realm: Realm, roomId: String): RealmQuery =
@@ -218,6 +221,7 @@ internal fun List.mapEventsWithEdition(realm: Realm, roomId: Stri
/**
* Returns a list of all the marked unread threads that exists for the specified room.
+ * @param realm the realm instance
* @param roomId The roomId that the user is currently in
*/
internal fun TimelineEventEntity.Companion.findAllLocalThreadNotificationsForRoomId(realm: Realm, roomId: String): RealmQuery =
@@ -232,6 +236,7 @@ internal fun TimelineEventEntity.Companion.findAllLocalThreadNotificationsForRoo
/**
* Returns whether or not the given user is participating in a current thread.
+ * @param realm the realm instance
* @param roomId the room that the thread exists
* @param rootThreadEventId the thread that the search will be done
* @param senderId the user that will try to find participation
@@ -247,6 +252,7 @@ internal fun TimelineEventEntity.Companion.isUserParticipatingInThread(realm: Re
/**
* Returns whether or not the given user is mentioned in a current thread.
+ * @param realm the realm instance
* @param roomId the room that the thread exists
* @param rootThreadEventId the thread that the search will be done
* @param userId the user that will try to find if there is a mention
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ThreadSummaryHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ThreadSummaryHelper.kt
index 3bf574c207..5b4fe287cb 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ThreadSummaryHelper.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ThreadSummaryHelper.kt
@@ -303,6 +303,7 @@ private fun getLatestEvent(rootThreadEvent: Event): Event? {
/**
* Find all ThreadSummaryEntity for the specified roomId, sorted by origin server.
* note: Sorting cannot be provided by server, so we have to use that unstable property.
+ * @param realm the realm instance
* @param roomId The id of the room
*/
internal fun ThreadSummaryEntity.Companion.findAllThreadsForRoomId(realm: Realm, roomId: String): RealmQuery =
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/legacy/riot/HomeServerConnectionConfig.java b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/legacy/riot/HomeServerConnectionConfig.java
index a1b46f6c09..b2bb852cd1 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/legacy/riot/HomeServerConnectionConfig.java
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/legacy/riot/HomeServerConnectionConfig.java
@@ -612,7 +612,7 @@ public class HomeServerConnectionConfig {
* - https://www.ssi.gouv.fr/uploads/2017/02/security-recommendations-for-tls_v1.1.pdf
* - https://developer.android.com/reference/javax/net/ssl/SSLEngine
*
- * @param tlsLimitations true to use Tls limitations
+ * @param tlsLimitations true to use Tls limitations
* @param enableCompatibilityMode set to true for Android < 20
* @return this builder
*/
@@ -649,7 +649,7 @@ public class HomeServerConnectionConfig {
/**
* @param proxyHostname Proxy Hostname
- * @param proxyPort Proxy Port
+ * @param proxyPort Proxy Port
* @return this builder
*/
public Builder withProxy(@Nullable String proxyHostname, int proxyPort) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/Request.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/Request.kt
index 695e7525af..87a98e03f6 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/Request.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/Request.kt
@@ -32,6 +32,7 @@ import java.io.IOException
* Execute a request from the requestBlock and handle some of the Exception it could generate
* Ref: https://github.com/matrix-org/matrix-js-sdk/blob/develop/src/scheduler.js#L138-L175
*
+ * @param DATA type of data return by the [requestBlock]
* @param globalErrorReceiver will be use to notify error such as invalid token error. See [GlobalError]
* @param canRetry if set to true, the request will be executed again in case of error, after a delay
* @param maxDelayBeforeRetry the max delay to wait before a retry. Note that in the case of a 429, if the provided delay exceeds this value, the error will
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/parsing/RuntimeJsonAdapterFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/parsing/RuntimeJsonAdapterFactory.kt
index 40d174ee2d..dd41b9f6fc 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/parsing/RuntimeJsonAdapterFactory.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/parsing/RuntimeJsonAdapterFactory.kt
@@ -119,9 +119,11 @@ internal class RuntimeJsonAdapterFactory(
companion object {
/**
+ * @param T the generic type to pass to [RuntimeJsonAdapterFactory]
* @param baseType The base type for which this factory will create adapters. Cannot be Object.
* @param labelKey The key in the JSON object whose value determines the type to which to map the
* JSON object.
+ * @param fallbackType alternative Type to try in case of the serialization fails
*/
@CheckReturnValue
fun of(baseType: Class, labelKey: String, fallbackType: Class): RuntimeJsonAdapterFactory {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ssl/CertUtil.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ssl/CertUtil.kt
index 2ef40fe2a3..e5659fd76b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ssl/CertUtil.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ssl/CertUtil.kt
@@ -94,6 +94,7 @@ internal object CertUtil {
* Convert the fingerprint to an hexa string.
*
* @param fingerprint the fingerprint
+ * @param sep the separator character, default to space
* @return the hexa string.
*/
fun fingerprintToHexString(fingerprint: ByteArray, sep: Char = ' '): String {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ssl/PinnedTrustManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ssl/PinnedTrustManager.kt
index ccae5ad14f..539570cdd9 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ssl/PinnedTrustManager.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ssl/PinnedTrustManager.kt
@@ -24,11 +24,9 @@ import javax.net.ssl.X509TrustManager
/**
* Implements a TrustManager that checks Certificates against an explicit list of known
* fingerprints.
- */
-
-/**
- * @param fingerprints Not empty array of SHA256 cert fingerprints
- * @param defaultTrustManager Optional trust manager to fall back on if cert does not match
+ *
+ * @property fingerprints Not empty array of SHA256 cert fingerprints
+ * @property defaultTrustManager Optional trust manager to fall back on if cert does not match
* any of the fingerprints. Can be null.
*/
internal class PinnedTrustManager(private val fingerprints: List,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ssl/PinnedTrustManagerApi24.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ssl/PinnedTrustManagerApi24.kt
index 574f1ef81d..191bb90a67 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ssl/PinnedTrustManagerApi24.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ssl/PinnedTrustManagerApi24.kt
@@ -28,11 +28,9 @@ import javax.net.ssl.X509ExtendedTrustManager
/**
* Implements a TrustManager that checks Certificates against an explicit list of known
* fingerprints.
- */
-
-/**
- * @param fingerprints An array of SHA256 cert fingerprints
- * @param defaultTrustManager Optional trust manager to fall back on if cert does not match
+ *
+ * @property fingerprints An array of SHA256 cert fingerprints
+ * @property defaultTrustManager Optional trust manager to fall back on if cert does not match
* any of the fingerprints. Can be null.
*/
@RequiresApi(Build.VERSION_CODES.N)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt
index 7ceb89e892..9208ff219b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt
@@ -87,8 +87,8 @@ import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationMan
import org.matrix.android.sdk.internal.session.openid.DefaultOpenIdService
import org.matrix.android.sdk.internal.session.permalinks.DefaultPermalinkService
import org.matrix.android.sdk.internal.session.room.EventRelationsAggregationProcessor
-import org.matrix.android.sdk.internal.session.room.aggregation.livelocation.DefaultLiveLocationAggregationProcessor
-import org.matrix.android.sdk.internal.session.room.aggregation.livelocation.LiveLocationAggregationProcessor
+import org.matrix.android.sdk.internal.session.room.aggregation.poll.DefaultPollAggregationProcessor
+import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollAggregationProcessor
import org.matrix.android.sdk.internal.session.room.create.RoomCreateEventProcessor
import org.matrix.android.sdk.internal.session.room.prune.RedactionEventProcessor
import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor
@@ -389,5 +389,5 @@ internal abstract class SessionModule {
abstract fun bindEventSenderProcessor(processor: EventSenderProcessorCoroutine): EventSenderProcessor
@Binds
- abstract fun bindLiveLocationAggregationProcessor(processor: DefaultLiveLocationAggregationProcessor): LiveLocationAggregationProcessor
+ abstract fun bindPollAggregationProcessor(processor: DefaultPollAggregationProcessor): PollAggregationProcessor
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/directory/DirectoryAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/directory/DirectoryAPI.kt
index 19b9130fc4..0db6812609 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/directory/DirectoryAPI.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/directory/DirectoryAPI.kt
@@ -55,6 +55,7 @@ internal interface DirectoryAPI {
/**
* Add alias to the room.
* @param roomAlias the room alias.
+ * @param body the Json body
*/
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/room/{roomAlias}")
suspend fun addRoomAlias(@Path("roomAlias") roomAlias: String,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/FilterApi.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/FilterApi.kt
index dab801360f..d1df77d14a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/FilterApi.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/FilterApi.kt
@@ -28,7 +28,7 @@ internal interface FilterApi {
* Upload FilterBody to get a filter_id which can be used for /sync requests.
*
* @param userId the user id
- * @param body the Json representation of a FilterBody object
+ * @param body the Json representation of a FilterBody object
*/
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "user/{userId}/filter")
suspend fun uploadFilter(@Path("userId") userId: String,
@@ -37,7 +37,7 @@ internal interface FilterApi {
/**
* Gets a filter with a given filterId from the homeserver.
*
- * @param userId the user id
+ * @param userId the user id
* @param filterId the filterID
* @return Filter
*/
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/FilterUtil.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/FilterUtil.kt
index 562fea88b6..2017a86c39 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/FilterUtil.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/FilterUtil.kt
@@ -25,7 +25,7 @@ internal object FilterUtil {
* FIXME New expected filter:
* "{\"room\": {\"ephemeral\": {\"notTypes\": [\"m.typing\"]}}, \"presence\":{\"notTypes\": [\"*\"]}}"
*
- * @param filterBody filterBody to patch
+ * @param filterBody filterBody to patch
* @param useDataSaveMode true to enable data save mode
*/
/*
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
index e9097e4d03..b1a518724c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
@@ -17,7 +17,7 @@
package org.matrix.android.sdk.internal.session.homeserver
import com.zhuinden.monarchy.Monarchy
-import org.matrix.android.sdk.api.MatrixPatterns.getDomain
+import org.matrix.android.sdk.api.MatrixPatterns.getServerName
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
import org.matrix.android.sdk.api.auth.wellknown.WellknownResult
import org.matrix.android.sdk.api.extensions.orFalse
@@ -93,10 +93,14 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
}
}.getOrNull()
+ // Domain may include a port (eg, matrix.org:8080)
+ // Per https://spec.matrix.org/latest/client-server-api/#well-known-uri we should extract the hostname from the server name
+ // So we take everything before the last : as the domain for the well-known task.
+ // NB: This is not always the same endpoint as capabilities / mediaConfig uses.
val wellknownResult = runCatching {
getWellknownTask.execute(
GetWellknownTask.Params(
- domain = userId.getDomain(),
+ domain = userId.getServerName().substringBeforeLast(":"),
homeServerConnectionConfig = homeServerConnectionConfig
)
)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/openid/OpenIdAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/openid/OpenIdAPI.kt
index eb8c841d57..c3caaefdec 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/openid/OpenIdAPI.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/openid/OpenIdAPI.kt
@@ -32,6 +32,7 @@ internal interface OpenIdAPI {
* Ref: https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-user-userid-openid-request-token
*
* @param userId the user id
+ * @param body an empty json body
*/
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "user/{userId}/openid/request_token")
suspend fun openIdToken(@Path("userId") userId: String,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/permalinks/ViaParameterFinder.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/permalinks/ViaParameterFinder.kt
index 0f667c65df..edc45fe945 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/permalinks/ViaParameterFinder.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/permalinks/ViaParameterFinder.kt
@@ -16,7 +16,7 @@
package org.matrix.android.sdk.internal.session.permalinks
-import org.matrix.android.sdk.api.MatrixPatterns.getDomain
+import org.matrix.android.sdk.api.MatrixPatterns.getServerName
import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toModel
@@ -55,9 +55,9 @@ internal class ViaParameterFinder @Inject constructor(
}
fun computeViaParams(userId: String, roomId: String, max: Int): List {
- val userHomeserver = userId.getDomain()
+ val userHomeserver = userId.getServerName()
return getUserIdsOfJoinedMembers(roomId)
- .map { it.getDomain() }
+ .map { it.getServerName() }
.groupBy { it }
.mapValues { it.value.size }
.toMutableMap()
@@ -92,7 +92,7 @@ internal class ViaParameterFinder @Inject constructor(
.orEmpty()
.toSet()
- return userThatCanInvite.map { it.getDomain() }
+ return userThatCanInvite.map { it.getServerName() }
.groupBy { it }
.mapValues { it.value.size }
.toMutableMap()
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/PushRulesApi.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/PushRulesApi.kt
index 40b4ee269a..fbae04a1f1 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/PushRulesApi.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/PushRulesApi.kt
@@ -33,9 +33,9 @@ internal interface PushRulesApi {
/**
* Update the ruleID enable status.
*
- * @param kind the notification kind (sender, room...)
+ * @param kind the notification kind (sender, room...)
* @param ruleId the ruleId
- * @param enable the new enable status
+ * @param enabledBody the new enable status
*/
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "pushrules/global/{kind}/{ruleId}/enabled")
suspend fun updateEnableRuleStatus(@Path("kind") kind: String,
@@ -46,8 +46,8 @@ internal interface PushRulesApi {
* Update the ruleID action.
* Ref: https://matrix.org/docs/spec/client_server/latest#put-matrix-client-r0-pushrules-scope-kind-ruleid-actions
*
- * @param kind the notification kind (sender, room...)
- * @param ruleId the ruleId
+ * @param kind the notification kind (sender, room...)
+ * @param ruleId the ruleId
* @param actions the actions
*/
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "pushrules/global/{kind}/{ruleId}/actions")
@@ -58,7 +58,7 @@ internal interface PushRulesApi {
/**
* Delete a rule.
*
- * @param kind the notification kind (sender, room...)
+ * @param kind the notification kind (sender, room...)
* @param ruleId the ruleId
*/
@DELETE(NetworkConstants.URI_API_PREFIX_PATH_R0 + "pushrules/global/{kind}/{ruleId}")
@@ -68,9 +68,9 @@ internal interface PushRulesApi {
/**
* Add the ruleID enable status.
*
- * @param kind the notification kind (sender, room...)
+ * @param kind the notification kind (sender, room...)
* @param ruleId the ruleId.
- * @param rule the rule to add.
+ * @param rule the rule to add.
*/
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "pushrules/global/{kind}/{ruleId}")
suspend fun addRule(@Path("kind") kind: String,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt
index 16a63a9a96..3efeef7688 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt
@@ -16,7 +16,6 @@
package org.matrix.android.sdk.internal.session.room
import io.realm.Realm
-import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.crypto.verification.VerificationState
import org.matrix.android.sdk.api.session.events.model.AggregatedAnnotation
@@ -28,23 +27,16 @@ import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventCon
import org.matrix.android.sdk.api.session.events.model.getRelationContent
import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.events.model.toModel
-import org.matrix.android.sdk.api.session.room.getTimelineEvent
-import org.matrix.android.sdk.api.session.room.model.PollSummaryContent
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
import org.matrix.android.sdk.api.session.room.model.ReferencesAggregatedContent
-import org.matrix.android.sdk.api.session.room.model.VoteInfo
-import org.matrix.android.sdk.api.session.room.model.VoteSummary
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
-import org.matrix.android.sdk.api.session.room.model.message.MessageEndPollContent
import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
import org.matrix.android.sdk.api.session.room.model.message.MessagePollResponseContent
import org.matrix.android.sdk.api.session.room.model.message.MessageRelationContent
import org.matrix.android.sdk.api.session.room.model.relation.ReactionContent
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
-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.SessionManager
import org.matrix.android.sdk.internal.crypto.verification.toState
import org.matrix.android.sdk.internal.database.helper.findRootThreadEvent
@@ -55,7 +47,6 @@ import org.matrix.android.sdk.internal.database.model.EditionOfEvent
import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntity
import org.matrix.android.sdk.internal.database.model.EventEntity
import org.matrix.android.sdk.internal.database.model.EventInsertType
-import org.matrix.android.sdk.internal.database.model.PollResponseAggregatedSummaryEntity
import org.matrix.android.sdk.internal.database.model.ReactionAggregatedSummaryEntity
import org.matrix.android.sdk.internal.database.model.ReactionAggregatedSummaryEntityFields
import org.matrix.android.sdk.internal.database.model.ReferencesAggregatedSummaryEntity
@@ -68,6 +59,7 @@ import org.matrix.android.sdk.internal.di.SessionId
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor
import org.matrix.android.sdk.internal.session.room.aggregation.livelocation.LiveLocationAggregationProcessor
+import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollAggregationProcessor
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber
@@ -79,6 +71,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
@SessionId private val sessionId: String,
private val sessionManager: SessionManager,
private val liveLocationAggregationProcessor: LiveLocationAggregationProcessor,
+ private val pollAggregationProcessor: PollAggregationProcessor,
private val clock: Clock,
) : EventInsertLiveProcessor {
@@ -162,9 +155,8 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
// A replace!
handleReplace(realm, event, it, roomId, isLocalEcho, encryptedEventContent.relatesTo.eventId)
} else if (event.getClearType() in EventType.POLL_RESPONSE) {
- event.getClearContent().toModel(catchError = true)?.let { pollResponseContent ->
- Timber.v("###RESPONSE in room $roomId for event ${event.eventId}")
- handleResponse(realm, event, pollResponseContent, roomId, isLocalEcho, encryptedEventContent.relatesTo.eventId)
+ sessionManager.getSessionComponent(sessionId)?.session()?.let { session ->
+ pollAggregationProcessor.handlePollResponseEvent(session, realm, event)
}
}
}
@@ -184,18 +176,20 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
}
in EventType.POLL_RESPONSE -> {
event.getClearContent().toModel(catchError = true)?.let {
- handleResponse(realm, event, it, roomId, isLocalEcho, event.getRelationContent()?.eventId)
+ sessionManager.getSessionComponent(sessionId)?.session()?.let { session ->
+ pollAggregationProcessor.handlePollResponseEvent(session, realm, event)
+ }
}
}
in EventType.POLL_END -> {
- event.content.toModel(catchError = true)?.let {
- handleEndPoll(realm, event, it, roomId, isLocalEcho)
+ sessionManager.getSessionComponent(sessionId)?.session()?.let { session ->
+ getPowerLevelsHelper(event.roomId)?.let {
+ pollAggregationProcessor.handlePollEndEvent(session, it, realm, event)
+ }
}
}
in EventType.BEACON_LOCATION_DATA -> {
- event.getClearContent().toModel(catchError = true)?.let {
- liveLocationAggregationProcessor.handleBeaconLocationData(realm, event, it, roomId, isLocalEcho)
- }
+ handleBeaconLocationData(event, realm, roomId, isLocalEcho)
}
}
} else if (encryptedEventContent?.relatesTo?.type == RelationType.ANNOTATION) {
@@ -247,12 +241,16 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
}
in EventType.POLL_RESPONSE -> {
event.content.toModel(catchError = true)?.let {
- handleResponse(realm, event, it, roomId, isLocalEcho)
+ sessionManager.getSessionComponent(sessionId)?.session()?.let { session ->
+ pollAggregationProcessor.handlePollResponseEvent(session, realm, event)
+ }
}
}
in EventType.POLL_END -> {
- event.content.toModel(catchError = true)?.let {
- handleEndPoll(realm, event, it, roomId, isLocalEcho)
+ sessionManager.getSessionComponent(sessionId)?.session()?.let { session ->
+ getPowerLevelsHelper(event.roomId)?.let {
+ pollAggregationProcessor.handlePollEndEvent(session, it, realm, event)
+ }
}
}
in EventType.STATE_ROOM_BEACON_INFO -> {
@@ -260,6 +258,9 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
liveLocationAggregationProcessor.handleBeaconInfo(realm, event, it, roomId, isLocalEcho)
}
}
+ in EventType.BEACON_LOCATION_DATA -> {
+ handleBeaconLocationData(event, realm, roomId, isLocalEcho)
+ }
else -> Timber.v("UnHandled event ${event.eventId}")
}
} catch (t: Throwable) {
@@ -317,22 +318,6 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
return
}
- ContentMapper
- .map(eventAnnotationsSummaryEntity.pollResponseSummary?.aggregatedContent)
- ?.toModel()
- ?.let { existingPollSummaryContent ->
- eventAnnotationsSummaryEntity.pollResponseSummary?.aggregatedContent = ContentMapper.map(
- PollSummaryContent(
- myVote = existingPollSummaryContent.myVote,
- votes = emptyList(),
- votesSummary = emptyMap(),
- totalVotes = 0,
- winnerVoteCount = 0,
- )
- .toContent()
- )
- }
-
val txId = event.unsignedData?.transactionId
// is it a remote echo?
if (!isLocalEcho && existingSummary.editions.any { it.eventId == txId }) {
@@ -362,6 +347,10 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
}
}
+ if (event.getClearType() in EventType.POLL_START) {
+ pollAggregationProcessor.handlePollStartEvent(realm, event)
+ }
+
if (!isLocalEcho) {
val replaceEvent = TimelineEventEntity
.where(realm, roomId, eventId)
@@ -375,6 +364,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
* Check if the edition is on the latest thread event, and update it accordingly.
* @param editedEvent The event that will be changed
* @param replaceEvent The new event
+ * @param editions list of edition of event
*/
private fun handleThreadSummaryEdition(editedEvent: EventEntity?,
replaceEvent: TimelineEventEntity?,
@@ -391,173 +381,10 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
}
}
- private fun handleResponse(realm: Realm,
- event: Event,
- content: MessagePollResponseContent,
- roomId: String,
- isLocalEcho: Boolean,
- relatedEventId: String? = null) {
- val eventId = event.eventId ?: return
- val senderId = event.senderId ?: return
- val targetEventId = relatedEventId ?: content.relatesTo?.eventId ?: return
- val eventTimestamp = event.originServerTs ?: return
-
- val targetPollContent = getPollContent(roomId, targetEventId) ?: return
-
- // ok, this is a poll response
- var existing = EventAnnotationsSummaryEntity.where(realm, roomId, targetEventId).findFirst()
- if (existing == null) {
- Timber.v("## POLL creating new relation summary for $targetEventId")
- existing = EventAnnotationsSummaryEntity.create(realm, roomId, targetEventId)
- }
-
- // we have it
- val existingPollSummary = existing.pollResponseSummary
- ?: realm.createObject(PollResponseAggregatedSummaryEntity::class.java).also {
- existing.pollResponseSummary = it
- }
-
- val closedTime = existingPollSummary.closedTime
- if (closedTime != null && eventTimestamp > closedTime) {
- Timber.v("## POLL is closed ignore event poll:$targetEventId, event :${event.eventId}")
- return
- }
-
- val currentModel = ContentMapper.map(existingPollSummary.aggregatedContent).toModel()
-
- if (existingPollSummary.sourceEvents.contains(eventId)) {
- // ignore this event, we already know it (??)
- Timber.v("## POLL ignoring event for summary, it's known eventId:$eventId")
- return
- }
- val txId = event.unsignedData?.transactionId
- // is it a remote echo?
- if (!isLocalEcho && existingPollSummary.sourceLocalEchoEvents.contains(txId)) {
- // ok it has already been managed
- Timber.v("## POLL Receiving remote echo of response eventId:$eventId")
- existingPollSummary.sourceLocalEchoEvents.remove(txId)
- existingPollSummary.sourceEvents.add(event.eventId)
- return
- }
-
- val option = content.getBestResponse()?.answers?.first() ?: return Unit.also {
- Timber.d("## POLL Ignoring malformed response no option eventId:$eventId content: ${event.content}")
- }
-
- // Check if this option is in available options
- if (!targetPollContent.getBestPollCreationInfo()?.answers?.map { it.id }?.contains(option).orFalse()) {
- Timber.v("## POLL $targetEventId doesn't contain option $option")
- return
- }
-
- val votes = currentModel?.votes.orEmpty().toMutableList()
-
- var myVote: String? = null
- val existingVoteIndex = votes.indexOfFirst { it.userId == senderId }
- if (existingVoteIndex != -1) {
- // Is the vote newer?
- val existingVote = votes[existingVoteIndex]
- if (existingVote.voteTimestamp < eventTimestamp) {
- // Take the new one
- votes[existingVoteIndex] = VoteInfo(senderId, option, eventTimestamp)
- if (userId == senderId) {
- myVote = option
- }
- Timber.v("## POLL adding vote $option for user $senderId in poll :$targetEventId ")
- } else {
- Timber.v("## POLL Ignoring vote (older than known one) eventId:$eventId ")
- }
- } else {
- votes.add(VoteInfo(senderId, option, eventTimestamp))
- if (userId == senderId) {
- myVote = option
- }
- Timber.v("## POLL adding vote $option for user $senderId in poll :$targetEventId ")
- }
-
- // Precompute the percentage of votes for all options
- val totalVotes = votes.size
- val newVotesSummary = votes
- .groupBy({ it.option }, { it.userId })
- .mapValues {
- VoteSummary(
- total = it.value.size,
- percentage = if (totalVotes == 0 && it.value.isEmpty()) 0.0 else it.value.size.toDouble() / totalVotes
- )
- }
- val newWinnerVoteCount = newVotesSummary.maxOf { it.value.total }
-
- if (isLocalEcho) {
- existingPollSummary.sourceLocalEchoEvents.add(eventId)
- } else {
- existingPollSummary.sourceEvents.add(eventId)
- }
-
- val newSumModel = PollSummaryContent(
- myVote = myVote,
- votes = votes,
- votesSummary = newVotesSummary,
- totalVotes = totalVotes,
- winnerVoteCount = newWinnerVoteCount
- )
-
- existingPollSummary.aggregatedContent = ContentMapper.map(newSumModel.toContent())
- }
-
- private fun handleEndPoll(realm: Realm,
- event: Event,
- content: MessageEndPollContent,
- roomId: String,
- isLocalEcho: Boolean) {
- val pollEventId = content.relatesTo?.eventId ?: return
- val pollOwnerId = getPollEvent(roomId, pollEventId)?.root?.senderId
- val isPollOwner = pollOwnerId == event.senderId
- val powerLevelsHelper = stateEventDataSource.getStateEvent(roomId, EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition)
+ private fun getPowerLevelsHelper(roomId: String): PowerLevelsHelper? {
+ return stateEventDataSource.getStateEvent(roomId, EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition)
?.content?.toModel()
?.let { PowerLevelsHelper(it) }
-
- if (!isPollOwner && !powerLevelsHelper?.isUserAbleToRedact(event.senderId ?: "").orFalse()) {
- Timber.v("## Received poll.end event $pollEventId but user ${event.senderId} doesn't have enough power level in room $roomId")
- return
- }
-
- var existingPoll = EventAnnotationsSummaryEntity.where(realm, roomId, pollEventId).findFirst()
- if (existingPoll == null) {
- Timber.v("## POLL creating new relation summary for $pollEventId")
- existingPoll = EventAnnotationsSummaryEntity.create(realm, roomId, pollEventId)
- }
-
- // we have it
- val existingPollSummary = existingPoll.pollResponseSummary
- ?: realm.createObject(PollResponseAggregatedSummaryEntity::class.java).also {
- existingPoll.pollResponseSummary = it
- }
-
- val txId = event.unsignedData?.transactionId
- existingPollSummary.closedTime = event.originServerTs
-
- // is it a remote echo?
- if (!isLocalEcho && existingPollSummary.sourceLocalEchoEvents.contains(txId)) {
- // ok it has already been managed
- Timber.v("## POLL Receiving remote echo of response eventId:$pollEventId")
- existingPollSummary.sourceLocalEchoEvents.remove(txId)
- existingPollSummary.sourceEvents.add(event.eventId)
- }
- }
-
- private fun getPollEvent(roomId: String, eventId: String): TimelineEvent? {
- val session = sessionManager.getSessionComponent(sessionId)?.session()
- return session?.roomService()?.getRoom(roomId)?.getTimelineEvent(eventId) ?: return null.also {
- Timber.v("## POLL target poll event $eventId not found in room $roomId")
- }
- }
-
- private fun getPollContent(roomId: String, eventId: String): MessagePollContent? {
- val pollEvent = getPollEvent(roomId, eventId) ?: return null
-
- return pollEvent.getLastMessageContent() as? MessagePollContent ?: return null.also {
- Timber.v("## POLL target poll event $eventId content is malformed")
- }
}
private fun handleInitialAggregatedRelations(realm: Realm,
@@ -756,4 +583,17 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
verifSummary.sourceEvents.add(event.eventId)
}
}
+
+ private fun handleBeaconLocationData(event: Event, realm: Realm, roomId: String, isLocalEcho: Boolean) {
+ event.getClearContent().toModel(catchError = true)?.let {
+ liveLocationAggregationProcessor.handleBeaconLocationData(
+ realm,
+ event,
+ it,
+ roomId,
+ event.getRelationContent()?.eventId,
+ isLocalEcho
+ )
+ }
+ }
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt
index 72f56ddf68..ba7f4cf5ad 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt
@@ -78,9 +78,9 @@ internal interface RoomAPI {
* Get a list of messages starting from a reference.
*
* @param roomId the room id
- * @param from the token identifying where to start. Required.
- * @param dir The direction to return messages from. Required.
- * @param limit the maximum number of messages to retrieve. Optional.
+ * @param from the token identifying where to start. Required.
+ * @param dir The direction to return messages from. Required.
+ * @param limit the maximum number of messages to retrieve. Optional.
* @param filter A JSON RoomEventFilter to filter returned events with. Optional.
*/
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/messages")
@@ -94,9 +94,9 @@ internal interface RoomAPI {
/**
* Get all members of a room.
*
- * @param roomId the room id where to get the members
- * @param syncToken the sync token (optional)
- * @param membership to include only one type of membership (optional)
+ * @param roomId the room id where to get the members
+ * @param syncToken the sync token (optional)
+ * @param membership to include only one type of membership (optional)
* @param notMembership to exclude one type of membership (optional)
*/
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/members")
@@ -109,10 +109,10 @@ internal interface RoomAPI {
/**
* Send an event to a room.
*
- * @param txId the transaction Id
- * @param roomId the room id
+ * @param txId the transaction Id
+ * @param roomId the room id
* @param eventType the event type
- * @param content the event content
+ * @param content the event content
*/
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/send/{eventType}/{txId}")
suspend fun send(@Path("txId") txId: String,
@@ -124,10 +124,10 @@ internal interface RoomAPI {
/**
* Get the context surrounding an event.
*
- * @param roomId the room id
+ * @param roomId the room id
* @param eventId the event Id
- * @param limit the maximum number of messages to retrieve
- * @param filter A JSON RoomEventFilter to filter returned events with. Optional.
+ * @param limit the maximum number of messages to retrieve
+ * @param filter A JSON RoomEventFilter to filter returned events with. Optional.
*/
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/context/{eventId}")
suspend fun getContextOfEvent(@Path("roomId") roomId: String,
@@ -138,7 +138,7 @@ internal interface RoomAPI {
/**
* Retrieve an event from its room id / events id.
*
- * @param roomId the room id
+ * @param roomId the room id
* @param eventId the event Id
*/
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/event/{eventId}")
@@ -148,7 +148,7 @@ internal interface RoomAPI {
/**
* Send read markers.
*
- * @param roomId the room id
+ * @param roomId the room id
* @param markers the read markers
*/
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/read_markers")
@@ -169,7 +169,7 @@ internal interface RoomAPI {
* Ref: https://matrix.org/docs/spec/client_server/r0.4.0.html#post-matrix-client-r0-rooms-roomid-invite
*
* @param roomId the room id
- * @param body a object that just contains a user id
+ * @param body a object that just contains a user id
*/
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/invite")
suspend fun invite(@Path("roomId") roomId: String,
@@ -179,6 +179,7 @@ internal interface RoomAPI {
* Invite a user to a room, using a ThreePid
* Ref: https://matrix.org/docs/spec/client_server/r0.6.1#id101
* @param roomId Required. The room identifier (not alias) to which to invite the user.
+ * @param body the Json body
*/
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/invite")
suspend fun invite3pid(@Path("roomId") roomId: String,
@@ -187,9 +188,9 @@ internal interface RoomAPI {
/**
* Send a generic state event.
*
- * @param roomId the room id.
+ * @param roomId the room id.
* @param stateEventType the state event type
- * @param params the request parameters
+ * @param params the request parameters
*/
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/state/{state_event_type}")
suspend fun sendStateEvent(@Path("roomId") roomId: String,
@@ -200,10 +201,10 @@ internal interface RoomAPI {
/**
* Send a generic state event.
*
- * @param roomId the room id.
+ * @param roomId the room id.
* @param stateEventType the state event type
- * @param stateKey the state keys
- * @param params the request parameters
+ * @param stateKey the state keys
+ * @param params the request parameters
*/
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/state/{state_event_type}/{state_key}")
suspend fun sendStateEvent(@Path("roomId") roomId: String,
@@ -221,8 +222,13 @@ internal interface RoomAPI {
/**
* Paginate relations for event based in normal topological order.
+ * @param roomId the room Id
+ * @param eventId the event Id
* @param relationType filter for this relation type
* @param eventType filter for this event type
+ * @param from from token
+ * @param to to token
+ * @param limit max number of Event to retrieve
*/
@GET(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "rooms/{roomId}/relations/{eventId}/{relationType}/{eventType}")
suspend fun getRelations(@Path("roomId") roomId: String,
@@ -236,7 +242,13 @@ internal interface RoomAPI {
/**
* Paginate relations for thread events based in normal topological order.
+ *
+ * @param roomId the room Id
+ * @param eventId the event Id
* @param relationType filter for this relation type
+ * @param from from token
+ * @param to to token
+ * @param limit max number of Event to retrieve
*/
@GET(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "rooms/{roomId}/relations/{eventId}/{relationType}")
suspend fun getThreadsRelations(@Path("roomId") roomId: String,
@@ -262,7 +274,7 @@ internal interface RoomAPI {
/**
* Leave the given room.
*
- * @param roomId the room id
+ * @param roomId the room id
* @param params the request body
*/
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/leave")
@@ -272,7 +284,7 @@ internal interface RoomAPI {
/**
* Ban a user from the given room.
*
- * @param roomId the room id
+ * @param roomId the room id
* @param userIdAndReason the banned user object (userId and reason for ban)
*/
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/ban")
@@ -282,7 +294,7 @@ internal interface RoomAPI {
/**
* unban a user from the given room.
*
- * @param roomId the room id
+ * @param roomId the room id
* @param userIdAndReason the unbanned user object (userId and reason for unban)
*/
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/unban")
@@ -292,7 +304,7 @@ internal interface RoomAPI {
/**
* Kick a user from the given room.
*
- * @param roomId the room id
+ * @param roomId the room id
* @param userIdAndReason the kicked user object (userId and reason for kicking)
*/
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/kick")
@@ -304,10 +316,10 @@ internal interface RoomAPI {
* This cannot be undone.
* Users may redact their own events, and any user with a power level greater than or equal to the redact power level of the room may redact events there.
*
- * @param txId the transaction Id
- * @param roomId the room id
- * @param eventId the event to delete
- * @param reason json containing reason key {"reason": "Indecent material"}
+ * @param txId the transaction Id
+ * @param roomId the room id
+ * @param eventId the event to delete
+ * @param reason json containing reason key {"reason": "Indecent material"}
*/
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/redact/{eventId}/{txnId}")
suspend fun redactEvent(
@@ -320,9 +332,9 @@ internal interface RoomAPI {
/**
* Reports an event as inappropriate to the server, which may then notify the appropriate people.
*
- * @param roomId the room id
+ * @param roomId the room id
* @param eventId the event to report content
- * @param body body containing score and reason
+ * @param body body containing score and reason
*/
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/report/{eventId}")
suspend fun reportContent(@Path("roomId") roomId: String,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAvatarResolver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAvatarResolver.kt
index 29a303475b..c3d55b267a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAvatarResolver.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAvatarResolver.kt
@@ -35,7 +35,7 @@ internal class RoomAvatarResolver @Inject constructor(@UserId private val userId
/**
* Compute the room avatar url.
- * @param realm: the current instance of realm
+ * @param realm the current instance of realm
* @param roomId the roomId of the room to resolve avatar
* @return the room avatar url, can be a fallback to a room member avatar or null
*/
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/DefaultLiveLocationAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/DefaultLiveLocationAggregationProcessor.kt
deleted file mode 100644
index 997e31a109..0000000000
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/DefaultLiveLocationAggregationProcessor.kt
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright 2022 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.session.room.aggregation.livelocation
-
-import io.realm.Realm
-import org.matrix.android.sdk.api.extensions.orTrue
-import org.matrix.android.sdk.api.session.events.model.Event
-import org.matrix.android.sdk.api.session.events.model.toContent
-import org.matrix.android.sdk.api.session.events.model.toModel
-import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent
-import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent
-import org.matrix.android.sdk.internal.database.mapper.ContentMapper
-import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity
-import org.matrix.android.sdk.internal.database.query.getOrCreate
-import timber.log.Timber
-import javax.inject.Inject
-
-internal class DefaultLiveLocationAggregationProcessor @Inject constructor() : LiveLocationAggregationProcessor {
-
- override fun handleBeaconInfo(realm: Realm, event: Event, content: MessageBeaconInfoContent, roomId: String, isLocalEcho: Boolean) {
- if (event.senderId.isNullOrEmpty() || isLocalEcho) {
- return
- }
-
- val targetEventId = if (content.isLive.orTrue()) {
- event.eventId
- } else {
- // when live is set to false, we use the id of the event that should have been replaced
- event.unsignedData?.replacesState
- }
-
- if (targetEventId.isNullOrEmpty()) {
- Timber.w("no target event id found for the beacon content")
- return
- }
-
- val aggregatedSummary = LiveLocationShareAggregatedSummaryEntity.getOrCreate(
- realm = realm,
- roomId = roomId,
- eventId = targetEventId
- )
-
- Timber.d("updating summary of id=$targetEventId with isLive=${content.isLive}")
-
- aggregatedSummary.endOfLiveTimestampMillis = content.getBestTimestampMillis()?.let { it + (content.timeout ?: 0) }
- aggregatedSummary.isActive = content.isLive
- }
-
- override fun handleBeaconLocationData(realm: Realm, event: Event, content: MessageBeaconLocationDataContent, roomId: String, isLocalEcho: Boolean) {
- if (event.senderId.isNullOrEmpty() || isLocalEcho) {
- return
- }
-
- val targetEventId = content.relatesTo?.eventId
-
- if (targetEventId.isNullOrEmpty()) {
- Timber.w("no target event id found for the live location content")
- return
- }
-
- val aggregatedSummary = LiveLocationShareAggregatedSummaryEntity.getOrCreate(
- realm = realm,
- roomId = roomId,
- eventId = targetEventId
- )
- val updatedLocationTimestamp = content.getBestTimestampMillis() ?: 0
- val currentLocationTimestamp = ContentMapper
- .map(aggregatedSummary.lastLocationContent)
- .toModel()
- ?.getBestTimestampMillis()
- ?: 0
-
- if (updatedLocationTimestamp.isMoreRecentThan(currentLocationTimestamp)) {
- Timber.d("updating last location of the summary of id=$targetEventId")
- aggregatedSummary.lastLocationContent = ContentMapper.map(content.toContent())
- }
- }
-
- private fun Long.isMoreRecentThan(timestamp: Long) = this > timestamp
-}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt
index c0be96f83d..76b7a4ec8e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt
@@ -17,24 +17,83 @@
package org.matrix.android.sdk.internal.session.room.aggregation.livelocation
import io.realm.Realm
+import org.matrix.android.sdk.api.extensions.orTrue
import org.matrix.android.sdk.api.session.events.model.Event
+import org.matrix.android.sdk.api.session.events.model.toContent
+import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent
+import org.matrix.android.sdk.internal.database.mapper.ContentMapper
+import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity
+import org.matrix.android.sdk.internal.database.query.getOrCreate
+import timber.log.Timber
+import javax.inject.Inject
-internal interface LiveLocationAggregationProcessor {
- fun handleBeaconInfo(
- realm: Realm,
- event: Event,
- content: MessageBeaconInfoContent,
- roomId: String,
- isLocalEcho: Boolean,
- )
+internal class LiveLocationAggregationProcessor @Inject constructor() {
+
+ fun handleBeaconInfo(realm: Realm, event: Event, content: MessageBeaconInfoContent, roomId: String, isLocalEcho: Boolean) {
+ if (event.senderId.isNullOrEmpty() || isLocalEcho) {
+ return
+ }
+
+ val targetEventId = if (content.isLive.orTrue()) {
+ event.eventId
+ } else {
+ // when live is set to false, we use the id of the event that should have been replaced
+ event.unsignedData?.replacesState
+ }
+
+ if (targetEventId.isNullOrEmpty()) {
+ Timber.w("no target event id found for the beacon content")
+ return
+ }
+
+ val aggregatedSummary = LiveLocationShareAggregatedSummaryEntity.getOrCreate(
+ realm = realm,
+ roomId = roomId,
+ eventId = targetEventId
+ )
+
+ Timber.d("updating summary of id=$targetEventId with isLive=${content.isLive}")
+
+ aggregatedSummary.endOfLiveTimestampMillis = content.getBestTimestampMillis()?.let { it + (content.timeout ?: 0) }
+ aggregatedSummary.isActive = content.isLive
+ }
fun handleBeaconLocationData(
realm: Realm,
event: Event,
content: MessageBeaconLocationDataContent,
roomId: String,
- isLocalEcho: Boolean,
- )
+ relatedEventId: String?,
+ isLocalEcho: Boolean
+ ) {
+ if (event.senderId.isNullOrEmpty() || isLocalEcho) {
+ return
+ }
+
+ if (relatedEventId.isNullOrEmpty()) {
+ Timber.w("no related event id found for the live location content")
+ return
+ }
+
+ val aggregatedSummary = LiveLocationShareAggregatedSummaryEntity.getOrCreate(
+ realm = realm,
+ roomId = roomId,
+ eventId = relatedEventId
+ )
+ val updatedLocationTimestamp = content.getBestTimestampMillis() ?: 0
+ val currentLocationTimestamp = ContentMapper
+ .map(aggregatedSummary.lastLocationContent)
+ .toModel()
+ ?.getBestTimestampMillis()
+ ?: 0
+
+ if (updatedLocationTimestamp.isMoreRecentThan(currentLocationTimestamp)) {
+ Timber.d("updating last location of the summary of id=$relatedEventId")
+ aggregatedSummary.lastLocationContent = ContentMapper.map(content.toContent())
+ }
+ }
+
+ private fun Long.isMoreRecentThan(timestamp: Long) = this > timestamp
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessor.kt
new file mode 100644
index 0000000000..d4b414aaea
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessor.kt
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2022 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.session.room.aggregation.poll
+
+import io.realm.Realm
+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.Event
+import org.matrix.android.sdk.api.session.events.model.LocalEcho
+import org.matrix.android.sdk.api.session.events.model.RelationType
+import org.matrix.android.sdk.api.session.events.model.getRelationContent
+import org.matrix.android.sdk.api.session.events.model.toContent
+import org.matrix.android.sdk.api.session.events.model.toModel
+import org.matrix.android.sdk.api.session.room.getTimelineEvent
+import org.matrix.android.sdk.api.session.room.model.PollSummaryContent
+import org.matrix.android.sdk.api.session.room.model.VoteInfo
+import org.matrix.android.sdk.api.session.room.model.VoteSummary
+import org.matrix.android.sdk.api.session.room.model.message.MessageEndPollContent
+import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
+import org.matrix.android.sdk.api.session.room.model.message.MessagePollResponseContent
+import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
+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.database.mapper.ContentMapper
+import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntity
+import org.matrix.android.sdk.internal.database.model.PollResponseAggregatedSummaryEntity
+import org.matrix.android.sdk.internal.database.query.create
+import org.matrix.android.sdk.internal.database.query.getOrCreate
+import org.matrix.android.sdk.internal.database.query.where
+import javax.inject.Inject
+
+class DefaultPollAggregationProcessor @Inject constructor() : PollAggregationProcessor {
+
+ override fun handlePollStartEvent(realm: Realm, event: Event): Boolean {
+ val content = event.getClearContent()?.toModel()
+ if (content?.relatesTo?.type != RelationType.REPLACE) {
+ return false
+ }
+
+ val roomId = event.roomId ?: return false
+ val targetEventId = content.relatesTo.eventId ?: return false
+
+ EventAnnotationsSummaryEntity.getOrCreate(realm, roomId, targetEventId).let { eventAnnotationsSummaryEntity ->
+ ContentMapper
+ .map(eventAnnotationsSummaryEntity.pollResponseSummary?.aggregatedContent)
+ ?.toModel()
+ ?.let { existingPollSummaryContent ->
+ eventAnnotationsSummaryEntity.pollResponseSummary?.aggregatedContent = ContentMapper.map(
+ PollSummaryContent(
+ myVote = existingPollSummaryContent.myVote,
+ votes = emptyList(),
+ votesSummary = emptyMap(),
+ totalVotes = 0,
+ winnerVoteCount = 0,
+ )
+ .toContent()
+ )
+ }
+ }
+ return true
+ }
+
+ override fun handlePollResponseEvent(session: Session, realm: Realm, event: Event): Boolean {
+ val content = event.getClearContent()?.toModel() ?: return false
+ val roomId = event.roomId ?: return false
+ val senderId = event.senderId ?: return false
+ val targetEventId = (event.getRelationContent() ?: content.relatesTo)?.eventId ?: return false
+ val targetPollContent = getPollContent(session, roomId, targetEventId) ?: return false
+
+ val annotationsSummaryEntity = getAnnotationsSummaryEntity(realm, roomId, targetEventId)
+ val aggregatedPollSummaryEntity = getAggregatedPollSummaryEntity(realm, annotationsSummaryEntity)
+
+ val closedTime = aggregatedPollSummaryEntity.closedTime
+ val responseTime = event.originServerTs ?: return false
+ if (closedTime != null && responseTime > closedTime) {
+ return false
+ }
+
+ if (aggregatedPollSummaryEntity.sourceEvents.contains(event.eventId)) {
+ return false
+ }
+
+ val txId = event.unsignedData?.transactionId
+ val isLocalEcho = LocalEcho.isLocalEchoId(event.eventId ?: "")
+ if (!isLocalEcho && aggregatedPollSummaryEntity.sourceLocalEchoEvents.contains(txId)) {
+ aggregatedPollSummaryEntity.sourceLocalEchoEvents.remove(txId)
+ aggregatedPollSummaryEntity.sourceEvents.add(event.eventId)
+ return false
+ }
+
+ val vote = content.getBestResponse()?.answers?.first() ?: return false
+ if (!targetPollContent.getBestPollCreationInfo()?.answers?.map { it.id }?.contains(vote).orFalse()) {
+ return false
+ }
+
+ val pollSummaryModel = ContentMapper.map(aggregatedPollSummaryEntity.aggregatedContent).toModel()
+ val existingVotes = pollSummaryModel?.votes.orEmpty().toMutableList()
+ val existingVoteIndex = existingVotes.indexOfFirst { it.userId == senderId }
+
+ if (existingVoteIndex != -1) {
+ val existingVote = existingVotes[existingVoteIndex]
+ if (existingVote.voteTimestamp > responseTime) {
+ return false
+ }
+ existingVotes[existingVoteIndex] = VoteInfo(senderId, vote, responseTime)
+ } else {
+ existingVotes.add(VoteInfo(senderId, vote, responseTime))
+ }
+
+ // Precompute the percentage of votes for all options
+ val totalVotes = existingVotes.size
+ val newVotesSummary = existingVotes
+ .groupBy({ it.option }, { it.userId })
+ .mapValues {
+ VoteSummary(
+ total = it.value.size,
+ percentage = if (totalVotes == 0 && it.value.isEmpty()) 0.0 else it.value.size.toDouble() / totalVotes
+ )
+ }
+ val newWinnerVoteCount = newVotesSummary.maxOf { it.value.total }
+
+ if (isLocalEcho) {
+ aggregatedPollSummaryEntity.sourceLocalEchoEvents.add(event.eventId)
+ } else {
+ aggregatedPollSummaryEntity.sourceEvents.add(event.eventId)
+ }
+
+ val myVote = existingVotes.find { it.userId == session.myUserId }?.option
+
+ val newSumModel = PollSummaryContent(
+ myVote = myVote,
+ votes = existingVotes,
+ votesSummary = newVotesSummary,
+ totalVotes = totalVotes,
+ winnerVoteCount = newWinnerVoteCount
+ )
+ aggregatedPollSummaryEntity.aggregatedContent = ContentMapper.map(newSumModel.toContent())
+
+ return true
+ }
+
+ override fun handlePollEndEvent(session: Session, powerLevelsHelper: PowerLevelsHelper, realm: Realm, event: Event): Boolean {
+ val content = event.getClearContent()?.toModel() ?: return false
+ val roomId = event.roomId ?: return false
+ val pollEventId = content.relatesTo?.eventId ?: return false
+ val pollOwnerId = getPollEvent(session, roomId, pollEventId)?.root?.senderId
+ val isPollOwner = pollOwnerId == event.senderId
+
+ if (!isPollOwner && !powerLevelsHelper.isUserAbleToRedact(event.senderId ?: "")) {
+ return false
+ }
+
+ val annotationsSummaryEntity = getAnnotationsSummaryEntity(realm, roomId, pollEventId)
+ val aggregatedPollSummaryEntity = getAggregatedPollSummaryEntity(realm, annotationsSummaryEntity)
+
+ val txId = event.unsignedData?.transactionId
+ aggregatedPollSummaryEntity.closedTime = event.originServerTs
+
+ val isLocalEcho = LocalEcho.isLocalEchoId(event.eventId ?: "")
+ if (!isLocalEcho && aggregatedPollSummaryEntity.sourceLocalEchoEvents.contains(txId)) {
+ aggregatedPollSummaryEntity.sourceLocalEchoEvents.remove(txId)
+ aggregatedPollSummaryEntity.sourceEvents.add(event.eventId)
+ }
+
+ return true
+ }
+
+ private fun getPollEvent(session: Session, roomId: String, eventId: String): TimelineEvent? {
+ return session.roomService().getRoom(roomId)?.getTimelineEvent(eventId)
+ }
+
+ private fun getPollContent(session: Session, roomId: String, eventId: String): MessagePollContent? {
+ val pollEvent = getPollEvent(session, roomId, eventId)
+ return pollEvent?.getLastMessageContent() as? MessagePollContent
+ }
+
+ private fun getAnnotationsSummaryEntity(realm: Realm, roomId: String, eventId: String): EventAnnotationsSummaryEntity {
+ return EventAnnotationsSummaryEntity.where(realm, roomId, eventId).findFirst()
+ ?: EventAnnotationsSummaryEntity.create(realm, roomId, eventId)
+ }
+
+ private fun getAggregatedPollSummaryEntity(realm: Realm,
+ eventAnnotationsSummaryEntity: EventAnnotationsSummaryEntity): PollResponseAggregatedSummaryEntity {
+ return eventAnnotationsSummaryEntity.pollResponseSummary
+ ?: realm.createObject(PollResponseAggregatedSummaryEntity::class.java).also {
+ eventAnnotationsSummaryEntity.pollResponseSummary = it
+ }
+ }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/PollAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/PollAggregationProcessor.kt
new file mode 100644
index 0000000000..848643b435
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/PollAggregationProcessor.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2022 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.session.room.aggregation.poll
+
+import io.realm.Realm
+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.room.powerlevels.PowerLevelsHelper
+
+interface PollAggregationProcessor {
+ /**
+ * Poll start events don't need to be processed by the aggregator.
+ * This function will only handle if the poll is edited and will update the poll summary entity.
+ * Returns true if the event is aggregated.
+ */
+ fun handlePollStartEvent(
+ realm: Realm,
+ event: Event
+ ): Boolean
+
+ /**
+ * Aggregates poll response event after many conditional checks like if the poll is ended, if the user is changing his/her vote etc.
+ * Returns true if the event is aggregated.
+ */
+ fun handlePollResponseEvent(
+ session: Session,
+ realm: Realm,
+ event: Event
+ ): Boolean
+
+ /**
+ * Updates poll summary entity and mark it is ended after many conditional checks like if the poll is already ended etc.
+ * Returns true if the event is aggregated.
+ */
+ fun handlePollEndEvent(
+ session: Session,
+ powerLevelsHelper: PowerLevelsHelper,
+ realm: Realm,
+ event: Event
+ ): Boolean
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/RoomAliasAvailabilityChecker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/RoomAliasAvailabilityChecker.kt
index 7c137a8102..fa19b4f9cf 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/RoomAliasAvailabilityChecker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/RoomAliasAvailabilityChecker.kt
@@ -16,7 +16,7 @@
package org.matrix.android.sdk.internal.session.room.alias
-import org.matrix.android.sdk.api.MatrixPatterns.getDomain
+import org.matrix.android.sdk.api.MatrixPatterns.getServerName
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.session.room.alias.RoomAliasError
import org.matrix.android.sdk.internal.di.UserId
@@ -65,6 +65,6 @@ internal class RoomAliasAvailabilityChecker @Inject constructor(
}
companion object {
- internal fun String.toFullLocalAlias(userId: String) = "#" + this + ":" + userId.getDomain()
+ internal fun String.toFullLocalAlias(userId: String) = "#" + this + ":" + userId.getServerName()
}
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt
index 59e0f81ece..9e672dcc5c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt
@@ -52,8 +52,8 @@ internal class RoomDisplayNameResolver @Inject constructor(
/**
* Compute the room display name.
*
- * @param realm: the current instance of realm
- * @param roomId: the roomId to resolve the name of.
+ * @param realm the current instance of realm
+ * @param roomId the roomId to resolve the name of.
* @return the room display name
*/
fun resolve(realm: Realm, roomId: String): RoomName {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTask.kt
index 948786677d..983701857f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTask.kt
@@ -21,8 +21,8 @@ import timber.log.Timber
import java.util.concurrent.atomic.AtomicInteger
/**
- * @param queueIdentifier String value to identify a unique Queue
- * @param taskIdentifier String value to identify a unique Task. Should be different from queueIdentifier
+ * @property queueIdentifier String value to identify a unique Queue
+ * @property taskIdentifier String value to identify a unique Task. Should be different from queueIdentifier
*/
internal abstract class QueuedTask(
val queueIdentifier: String,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceApi.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceApi.kt
index d8daa55e15..33c3c3929f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceApi.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceApi.kt
@@ -24,11 +24,12 @@ import retrofit2.http.Query
internal interface SpaceApi {
/**
+ * @param spaceId the space Id
* @param suggestedOnly Optional. If true, return only child events and rooms where the m.space.child event has suggested: true.
- * @param limit: Optional: a client-defined limit to the maximum number of rooms to return per page. Must be a non-negative integer.
- * @param maxDepth: Optional: The maximum depth in the tree (from the root room) to return.
+ * @param limit Optional: a client-defined limit to the maximum number of rooms to return per page. Must be a non-negative integer.
+ * @param maxDepth Optional: The maximum depth in the tree (from the root room) to return.
* The deepest depth returned will not include children events. Defaults to no-limit. Must be a non-negative integer.
- * @param from: Optional. Pagination token given to retrieve the next set of rooms.
+ * @param from Optional. Pagination token given to retrieve the next set of rooms.
* Note that if a pagination token is provided, then the parameters given for suggested_only and max_depth must be the same.
*/
@GET(NetworkConstants.URI_API_PREFIX_PATH_V1 + "rooms/{roomId}/hierarchy")
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/CryptoSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/CryptoSyncHandler.kt
index dd95762166..e5a5a0bbad 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/CryptoSyncHandler.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/CryptoSyncHandler.kt
@@ -62,7 +62,7 @@ internal class CryptoSyncHandler @Inject constructor(private val cryptoService:
/**
* Decrypt an encrypted event.
*
- * @param event the event to decrypt
+ * @param event the event to decrypt
* @param timelineId the timeline identifier
* @return true if the event has been decrypted
*/
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/ThreadsAwarenessHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/ThreadsAwarenessHandler.kt
index 03e076c217..9beb8333a4 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/ThreadsAwarenessHandler.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/ThreadsAwarenessHandler.kt
@@ -206,6 +206,8 @@ internal class ThreadsAwarenessHandler @Inject constructor(
/**
* Handle for not thread events that we have marked them as root.
* Find relations and inject them accordingly
+ * @param realm the realm instance
+ * @param roomId the current room Id
* @param eventEntity the current eventEntity received
* @param event the current event received
* @return The content to inject in the roomSyncHandler live events
@@ -229,9 +231,12 @@ internal class ThreadsAwarenessHandler @Inject constructor(
* This function is responsible to check if there is any event that relates to our current event.
* This is useful when we receive an event that relates to a missing parent, so when later we receive the parent
* we can update the child as well.
+ * @param realm the realm instance
+ * @param roomId the current room Id
* @param event the current event that we examine
* @param eventBody the current body of the event
* @param isFromCache determines whether or not we already know this is root thread event
+ * @param threadRelation the information about thread
* @return The content to inject in the roomSyncHandler live events
*/
private fun handleEventsThatRelatesTo(
@@ -291,9 +296,12 @@ internal class ThreadsAwarenessHandler @Inject constructor(
}
/**
- * Injecting $eventToInject decrypted content as a reply to $event.
- * @param eventToInject the event that will inject
+ * Injecting [eventToInject] decrypted content as a reply to event.
+ * @param roomId the room id
* @param eventBody the actual event body
+ * @param eventToInject the event that will inject
+ * @param eventToInjectBody the event body to inject
+ * @param threadRelation the information about thread
* @return The final content with the injected event
*/
private fun injectEvent(roomId: String,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/AccountDataAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/AccountDataAPI.kt
index bbeff18c01..178f349ec8 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/AccountDataAPI.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/AccountDataAPI.kt
@@ -27,7 +27,7 @@ internal interface AccountDataAPI {
* Set some account_data for the client.
*
* @param userId the user id
- * @param type the type
+ * @param type the type
* @param params the put params
*/
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "user/{userId}/account_data/{type}")
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/DefaultWidgetPostAPIMediator.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/DefaultWidgetPostAPIMediator.kt
index 1da6827916..857105f6ef 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/DefaultWidgetPostAPIMediator.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/DefaultWidgetPostAPIMediator.kt
@@ -95,7 +95,7 @@ internal class DefaultWidgetPostAPIMediator @Inject constructor(private val mosh
/**
* Send a boolean response.
*
- * @param response the response
+ * @param response the response
* @param eventData the modular data
*/
override fun sendBoolResponse(response: Boolean, eventData: JsonDict) {
@@ -106,7 +106,7 @@ internal class DefaultWidgetPostAPIMediator @Inject constructor(private val mosh
/**
* Send an integer response.
*
- * @param response the response
+ * @param response the response
* @param eventData the modular data
*/
override fun sendIntegerResponse(response: Int, eventData: JsonDict) {
@@ -116,7 +116,9 @@ internal class DefaultWidgetPostAPIMediator @Inject constructor(private val mosh
/**
* Send an object response.
*
- * @param response the response
+ * @param T the Json type
+ * @param type the type
+ * @param response the response
* @param eventData the modular data
*/
override fun sendObjectResponse(type: Type, response: T?, eventData: JsonDict) {
@@ -145,7 +147,7 @@ internal class DefaultWidgetPostAPIMediator @Inject constructor(private val mosh
/**
* Send an error.
*
- * @param message the error message
+ * @param message the error message
* @param eventData the modular data
*/
override fun sendError(message: String, eventData: JsonDict) {
@@ -162,7 +164,7 @@ internal class DefaultWidgetPostAPIMediator @Inject constructor(private val mosh
/**
* Send the response to the javascript.
*
- * @param jsString the response data
+ * @param jsString the response data
* @param eventData the modular data
*/
private fun sendResponse(jsString: String, eventData: JsonDict) = uiHandler.post {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/WidgetsAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/WidgetsAPI.kt
index b871a317c8..97b40e545e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/WidgetsAPI.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/WidgetsAPI.kt
@@ -27,6 +27,7 @@ internal interface WidgetsAPI {
* Register to the server.
*
* @param body the body content (Ref: https://github.com/matrix-org/matrix-doc/pull/1961)
+ * @param version the widget API version
*/
@POST("register")
suspend fun register(@Body body: OpenIdToken,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/task/CoroutineSequencer.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/task/CoroutineSequencer.kt
index 80081e3186..dd4c5e7623 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/task/CoroutineSequencer.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/task/CoroutineSequencer.kt
@@ -24,6 +24,7 @@ import kotlinx.coroutines.sync.withPermit
*/
internal interface CoroutineSequencer {
/**
+ * @param T generic type
* @param block the suspendable block to execute
* @return the result of the block
*/
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/JsonCanonicalizer.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/JsonCanonicalizer.kt
index 94aa238789..c50b7fe675 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/JsonCanonicalizer.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/JsonCanonicalizer.kt
@@ -53,7 +53,7 @@ internal object JsonCanonicalizer {
/**
* Canonicalize a JSON element.
*
- * @param src the src
+ * @param any the src
* @return the canonicalize element
*/
private fun canonicalizeRecursive(any: Any): String {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/StringUtils.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/StringUtils.kt
index d9fd312a6f..c6a417f6eb 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/StringUtils.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/StringUtils.kt
@@ -54,7 +54,7 @@ internal fun convertFromUTF8(s: String): String {
/**
* Returns whether a string contains an occurrence of another, as a standalone word, regardless of case.
*
- * @param subString the string to search for
+ * @param subString the string to search for
* @return whether a match was found
*/
internal fun String.caseInsensitiveFind(subString: String): Boolean {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/wellknown/GetWellknownTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/wellknown/GetWellknownTask.kt
index 0d4a5ac28f..31549155d3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/wellknown/GetWellknownTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/wellknown/GetWellknownTask.kt
@@ -75,7 +75,8 @@ internal class DefaultGetWellknownTask @Inject constructor(
* - validate homeserver url and identity server url if provide in .well-known result
* - return action and .well-known data
*
- * @param domain: homeserver domain, deduced from mx userId (ex: "matrix.org" from userId "@user:matrix.org")
+ * @param domain homeserver domain, deduced from mx userId (ex: "matrix.org" from userId "@user:matrix.org")
+ * @param client Http client to perform the request
*/
private suspend fun findClientConfig(domain: String, client: OkHttpClient): WellknownResult {
val wellKnownAPI = retrofitFactory.create(client, "https://dummy.org")
diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/PollAggregationProcessorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/PollAggregationProcessorTest.kt
new file mode 100644
index 0000000000..837bbeea26
--- /dev/null
+++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/PollAggregationProcessorTest.kt
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2022 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.session.room.aggregation.poll
+
+import io.mockk.every
+import io.mockk.mockk
+import io.realm.RealmList
+import io.realm.RealmModel
+import io.realm.RealmQuery
+import org.amshove.kluent.shouldBeFalse
+import org.amshove.kluent.shouldBeTrue
+import org.junit.Before
+import org.junit.Test
+import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.api.session.getRoom
+import org.matrix.android.sdk.api.session.room.Room
+import org.matrix.android.sdk.api.session.room.getTimelineEvent
+import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
+import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntity
+import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntityFields
+import org.matrix.android.sdk.internal.database.model.PollResponseAggregatedSummaryEntity
+import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.AN_EVENT_ID
+import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.AN_INVALID_POLL_RESPONSE_EVENT
+import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_BROKEN_POLL_REPLACE_EVENT
+import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_POLL_END_EVENT
+import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_POLL_REFERENCE_EVENT
+import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_POLL_REPLACE_EVENT
+import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_POLL_RESPONSE_EVENT
+import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_POLL_RESPONSE_EVENT_WITH_A_WRONG_REFERENCE
+import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_POLL_START_EVENT
+import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_ROOM_ID
+import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_TIMELINE_EVENT
+import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_USER_ID_1
+import org.matrix.android.sdk.test.fakes.FakeRealm
+
+class PollAggregationProcessorTest {
+
+ private val pollAggregationProcessor: PollAggregationProcessor = DefaultPollAggregationProcessor()
+ private val realm = FakeRealm()
+ private val session = mockk()
+
+ @Before
+ fun setup() {
+ mockEventAnnotationsSummaryEntity()
+ mockRoom(A_ROOM_ID, AN_EVENT_ID)
+ every { session.myUserId } returns A_USER_ID_1
+ }
+
+ @Test
+ fun `given a poll start event, when processing, then is ignored and returns false`() {
+ pollAggregationProcessor.handlePollStartEvent(realm.instance, A_POLL_START_EVENT).shouldBeFalse()
+ }
+
+ @Test
+ fun `given a poll start event with a reference, when processing, then is ignored and returns false`() {
+ pollAggregationProcessor.handlePollStartEvent(realm.instance, A_POLL_REFERENCE_EVENT).shouldBeFalse()
+ }
+
+ @Test
+ fun `given a poll start event with a replace relation but without a target event id, when processing, then is ignored and returns false`() {
+ pollAggregationProcessor.handlePollStartEvent(realm.instance, A_BROKEN_POLL_REPLACE_EVENT).shouldBeFalse()
+ }
+
+ @Test
+ fun `given a poll start event with a replace, when processing, then is processed and returns true`() {
+ pollAggregationProcessor.handlePollStartEvent(realm.instance, A_POLL_REPLACE_EVENT).shouldBeTrue()
+ }
+
+ @Test
+ fun `given a poll response event with a broken reference, when processing, then is ignored and returns false`() {
+ pollAggregationProcessor.handlePollResponseEvent(session, realm.instance, A_POLL_RESPONSE_EVENT_WITH_A_WRONG_REFERENCE).shouldBeFalse()
+ }
+
+ @Test
+ fun `given a poll response event with a reference, when processing, then is processed and returns true`() {
+ every { realm.instance.createObject(PollResponseAggregatedSummaryEntity::class.java) } returns PollResponseAggregatedSummaryEntity()
+ pollAggregationProcessor.handlePollResponseEvent(session, realm.instance, A_POLL_RESPONSE_EVENT).shouldBeTrue()
+ }
+
+ @Test
+ fun `given a poll response event after poll is closed, when processing, then is ignored and returns false`() {
+ every { realm.instance.createObject(PollResponseAggregatedSummaryEntity::class.java) } returns PollResponseAggregatedSummaryEntity().apply {
+ closedTime = (A_POLL_RESPONSE_EVENT.originServerTs ?: 0) - 1
+ }
+ pollAggregationProcessor.handlePollResponseEvent(session, realm.instance, A_POLL_RESPONSE_EVENT).shouldBeFalse()
+ }
+
+ @Test
+ fun `given a poll response event which is already processed, when processing, then is ignored and returns false`() {
+ every { realm.instance.createObject(PollResponseAggregatedSummaryEntity::class.java) } returns PollResponseAggregatedSummaryEntity().apply {
+ sourceEvents = RealmList(A_POLL_RESPONSE_EVENT.eventId)
+ }
+ pollAggregationProcessor.handlePollResponseEvent(session, realm.instance, A_POLL_RESPONSE_EVENT).shouldBeFalse()
+ }
+
+ @Test
+ fun `given a poll response event which is not one of the options, when processing, then is ignored and returns false`() {
+ every { realm.instance.createObject(PollResponseAggregatedSummaryEntity::class.java) } returns PollResponseAggregatedSummaryEntity()
+ pollAggregationProcessor.handlePollResponseEvent(session, realm.instance, AN_INVALID_POLL_RESPONSE_EVENT).shouldBeFalse()
+ }
+
+ @Test
+ fun `given a poll end event, when processing, then is processed and return true`() {
+ every { realm.instance.createObject(PollResponseAggregatedSummaryEntity::class.java) } returns PollResponseAggregatedSummaryEntity()
+ val powerLevelsHelper = mockRedactionPowerLevels(A_USER_ID_1, true)
+ pollAggregationProcessor.handlePollEndEvent(session, powerLevelsHelper, realm.instance, A_POLL_END_EVENT).shouldBeTrue()
+ }
+
+ @Test
+ fun `given a poll end event for my own poll without enough redaction power level, when processing, then is processed and returns true`() {
+ every { realm.instance.createObject(PollResponseAggregatedSummaryEntity::class.java) } returns PollResponseAggregatedSummaryEntity()
+ val powerLevelsHelper = mockRedactionPowerLevels(A_USER_ID_1, false)
+ pollAggregationProcessor.handlePollEndEvent(session, powerLevelsHelper, realm.instance, A_POLL_END_EVENT).shouldBeTrue()
+ }
+
+ @Test
+ fun `given a poll end event without enough redaction power level, when is processed, then is ignored and return false`() {
+ every { realm.instance.createObject(PollResponseAggregatedSummaryEntity::class.java) } returns PollResponseAggregatedSummaryEntity()
+ val powerLevelsHelper = mockRedactionPowerLevels("another-sender-id", false)
+ val event = A_POLL_END_EVENT.copy(senderId = "another-sender-id")
+ pollAggregationProcessor.handlePollEndEvent(session, powerLevelsHelper, realm.instance, event).shouldBeFalse()
+ }
+
+ private inline fun RealmQuery.givenEqualTo(fieldName: String, value: String, result: RealmQuery) {
+ every { equalTo(fieldName, value) } returns result
+ }
+
+ private fun mockEventAnnotationsSummaryEntity() {
+ val queryResult = realm.givenWhereReturns(result = EventAnnotationsSummaryEntity())
+ queryResult.givenEqualTo(EventAnnotationsSummaryEntityFields.ROOM_ID, A_POLL_REPLACE_EVENT.roomId!!, queryResult)
+ queryResult.givenEqualTo(EventAnnotationsSummaryEntityFields.EVENT_ID, A_POLL_REPLACE_EVENT.eventId!!, queryResult)
+ }
+
+ private fun mockRoom(
+ roomId: String,
+ eventId: String
+ ) {
+ val room = mockk()
+ every { session.getRoom(roomId) } returns room
+ every { room.getTimelineEvent(eventId) } returns A_TIMELINE_EVENT
+ }
+
+ private fun mockRedactionPowerLevels(userId: String, isAbleToRedact: Boolean): PowerLevelsHelper {
+ val powerLevelsHelper = mockk()
+ every { powerLevelsHelper.isUserAbleToRedact(userId) } returns isAbleToRedact
+ return powerLevelsHelper
+ }
+}
diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/PollEventsTestData.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/PollEventsTestData.kt
new file mode 100644
index 0000000000..129d49633e
--- /dev/null
+++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/PollEventsTestData.kt
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2022 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.session.room.aggregation.poll
+
+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.RelationType
+import org.matrix.android.sdk.api.session.events.model.toContent
+import org.matrix.android.sdk.api.session.room.model.message.MessageEndPollContent
+import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
+import org.matrix.android.sdk.api.session.room.model.message.MessagePollResponseContent
+import org.matrix.android.sdk.api.session.room.model.message.PollAnswer
+import org.matrix.android.sdk.api.session.room.model.message.PollCreationInfo
+import org.matrix.android.sdk.api.session.room.model.message.PollQuestion
+import org.matrix.android.sdk.api.session.room.model.message.PollResponse
+import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
+import org.matrix.android.sdk.api.session.room.sender.SenderInfo
+import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
+
+object PollEventsTestData {
+ internal const val A_USER_ID_1 = "@user_1:matrix.org"
+ internal const val A_ROOM_ID = "!sUeOGZKsBValPTUMax:matrix.org"
+ internal const val AN_EVENT_ID = "\$vApgexcL8Vfh-WxYKsFKCDooo67ttbjm3TiVKXaWijU"
+
+ internal val A_POLL_CONTENT = MessagePollContent(
+ unstablePollCreationInfo = PollCreationInfo(
+ question = PollQuestion(
+ unstableQuestion = "What is your favourite coffee?"
+ ),
+ maxSelections = 1,
+ answers = listOf(
+ PollAnswer(
+ id = "5ef5f7b0-c9a1-49cf-a0b3-374729a43e76",
+ unstableAnswer = "Double Espresso"
+ ),
+ PollAnswer(
+ id = "ec1a4db0-46d8-4d7a-9bb6-d80724715938",
+ unstableAnswer = "Macchiato"
+ ),
+ PollAnswer(
+ id = "3677ca8e-061b-40ab-bffe-b22e4e88fcad",
+ unstableAnswer = "Iced Coffee"
+ )
+ )
+ )
+ )
+
+ internal val A_POLL_RESPONSE_CONTENT = MessagePollResponseContent(
+ unstableResponse = PollResponse(
+ answers = listOf("5ef5f7b0-c9a1-49cf-a0b3-374729a43e76")
+ ),
+ relatesTo = RelationDefaultContent(
+ type = RelationType.REFERENCE,
+ eventId = AN_EVENT_ID
+ )
+ )
+
+ internal val A_POLL_END_CONTENT = MessageEndPollContent(
+ relatesTo = RelationDefaultContent(
+ type = RelationType.REFERENCE,
+ eventId = AN_EVENT_ID
+ )
+ )
+
+ internal val AN_INVALID_POLL_RESPONSE_CONTENT = MessagePollResponseContent(
+ unstableResponse = PollResponse(
+ answers = listOf("fake-option-id")
+ ),
+ relatesTo = RelationDefaultContent(
+ type = RelationType.REFERENCE,
+ eventId = AN_EVENT_ID
+ )
+ )
+
+ internal val A_POLL_START_EVENT = Event(
+ type = EventType.POLL_START.first(),
+ eventId = AN_EVENT_ID,
+ originServerTs = 1652435922563,
+ senderId = A_USER_ID_1,
+ roomId = A_ROOM_ID,
+ content = A_POLL_CONTENT.toContent()
+ )
+
+ internal val A_POLL_RESPONSE_EVENT = Event(
+ type = EventType.POLL_RESPONSE.first(),
+ eventId = AN_EVENT_ID,
+ originServerTs = 1652435922563,
+ senderId = A_USER_ID_1,
+ roomId = A_ROOM_ID,
+ content = A_POLL_RESPONSE_CONTENT.toContent()
+ )
+
+ internal val A_POLL_END_EVENT = Event(
+ type = EventType.POLL_END.first(),
+ eventId = AN_EVENT_ID,
+ originServerTs = 1652435922563,
+ senderId = A_USER_ID_1,
+ roomId = A_ROOM_ID,
+ content = A_POLL_END_CONTENT.toContent()
+ )
+
+ internal val A_TIMELINE_EVENT = TimelineEvent(
+ root = A_POLL_START_EVENT,
+ localId = 1234,
+ eventId = AN_EVENT_ID,
+ displayIndex = 0,
+ senderInfo = SenderInfo(A_USER_ID_1, "A_USER_ID_1", true, null)
+ )
+
+ internal val A_POLL_RESPONSE_EVENT_WITH_A_WRONG_REFERENCE = A_POLL_RESPONSE_EVENT.copy(
+ content = A_POLL_RESPONSE_CONTENT
+ .copy(
+ relatesTo = RelationDefaultContent(
+ type = RelationType.REPLACE,
+ eventId = null
+ )
+ )
+ .toContent()
+ )
+
+ internal val A_POLL_REPLACE_EVENT = A_POLL_START_EVENT.copy(
+ content = A_POLL_CONTENT
+ .copy(
+ relatesTo = RelationDefaultContent(
+ type = RelationType.REPLACE,
+ eventId = AN_EVENT_ID
+ )
+ )
+ .toContent()
+ )
+
+ internal val A_BROKEN_POLL_REPLACE_EVENT = A_POLL_START_EVENT.copy(
+ content = A_POLL_CONTENT
+ .copy(
+ relatesTo = RelationDefaultContent(
+ type = RelationType.REPLACE,
+ eventId = null
+ )
+ )
+ .toContent()
+ )
+
+ internal val A_POLL_REFERENCE_EVENT = A_POLL_START_EVENT.copy(
+ content = A_POLL_CONTENT
+ .copy(
+ relatesTo = RelationDefaultContent(
+ type = RelationType.REFERENCE,
+ eventId = AN_EVENT_ID
+ )
+ )
+ .toContent()
+ )
+
+ internal val AN_INVALID_POLL_RESPONSE_EVENT = A_POLL_RESPONSE_EVENT.copy(
+ content = AN_INVALID_POLL_RESPONSE_CONTENT.toContent()
+ )
+}
diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRealm.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRealm.kt
new file mode 100644
index 0000000000..c07f8e1873
--- /dev/null
+++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRealm.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2022 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.test.fakes
+
+import io.mockk.every
+import io.mockk.mockk
+import io.realm.Realm
+import io.realm.RealmModel
+import io.realm.RealmQuery
+import io.realm.kotlin.where
+
+internal class FakeRealm {
+
+ val instance = mockk(relaxed = true)
+
+ inline fun givenWhereReturns(result: T?): RealmQuery {
+ val queryResult = mockk>()
+ every { queryResult.findFirst() } returns result
+ every { instance.where() } returns queryResult
+ return queryResult
+ }
+}
diff --git a/tools/check/check_code_quality.sh b/tools/check/check_code_quality.sh
index e40d3635e8..910616176c 100755
--- a/tools/check/check_code_quality.sh
+++ b/tools/check/check_code_quality.sh
@@ -67,6 +67,9 @@ echo "Search for forbidden patterns in code..."
${searchForbiddenStringsScript} ./tools/check/forbidden_strings_in_code.txt \
./matrix-sdk-android/src/main/java \
./matrix-sdk-android-flow/src/main/java \
+ ./library/core-utils/src/main/java \
+ ./library/jsonviewer/src/main/java \
+ ./library/ui-styles/src/main/java \
./vector/src/main/java \
./vector/src/debug/java \
./vector/src/release/java \
@@ -100,6 +103,7 @@ echo
echo "Search for forbidden patterns in resources..."
${searchForbiddenStringsScript} ./tools/check/forbidden_strings_in_resources.txt \
+ ./library/ui-styles/src/main/res/values \
./vector/src/main/res/color \
./vector/src/main/res/layout \
./vector/src/main/res/values \
diff --git a/tools/check/forbidden_strings_in_code.txt b/tools/check/forbidden_strings_in_code.txt
index 7362ff2d10..962a14843d 100755
--- a/tools/check/forbidden_strings_in_code.txt
+++ b/tools/check/forbidden_strings_in_code.txt
@@ -177,3 +177,6 @@ R\.string\.template_
### Use the Clock interface, or use `measureTimeMillis`
System\.currentTimeMillis\(\)===2
+
+### Remove extra space between the name and the description
+\* @\w+ \w+ +
diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml
index 322f29e5b7..a836edc47a 100644
--- a/tools/detekt/detekt.yml
+++ b/tools/detekt/detekt.yml
@@ -87,8 +87,7 @@ comments:
EndOfSentenceFormat:
active: true
OutdatedDocumentation:
- # TODO Enable it
- active: false
+ active: true
UndocumentedPublicClass:
active: false
UndocumentedPublicFunction:
diff --git a/vector/build.gradle b/vector/build.gradle
index affe10e57b..4ae6527e0b 100644
--- a/vector/build.gradle
+++ b/vector/build.gradle
@@ -31,7 +31,7 @@ ext.versionMinor = 4
// Note: even values are reserved for regular release, odd values for hotfix release.
// When creating a hotfix, you should decrease the value, since the current value
// is the value for the next regular release.
-ext.versionPatch = 16
+ext.versionPatch = 18
static def getGitTimestamp() {
def cmd = 'git show -s --format=%ct'
@@ -452,9 +452,6 @@ dependencies {
kapt libs.github.glideCompiler
implementation 'com.github.yalantis:ucrop:2.2.8'
- // Badge for compatibility
- implementation 'me.leolin:ShortcutBadger:1.1.22@aar'
-
// Chat effects
implementation 'nl.dionsegijn:konfetti-xml:2.0.2'
diff --git a/vector/src/androidTest/java/im/vector/app/ui/robot/ElementRobot.kt b/vector/src/androidTest/java/im/vector/app/ui/robot/ElementRobot.kt
index 3c5de8b221..b9292bd916 100644
--- a/vector/src/androidTest/java/im/vector/app/ui/robot/ElementRobot.kt
+++ b/vector/src/androidTest/java/im/vector/app/ui/robot/ElementRobot.kt
@@ -120,6 +120,26 @@ class ElementRobot {
.perform(ViewActions.closeSoftKeyboard(), click())
}
}
+ // at this point we are in a race with the app restarting. The steps that happen are:
+ // - (initially) app has started, app has initial synched
+ // - (restart) app has strted, app has not initial synched
+ // - (racey) app shows some UI but overlays with initial sync ui
+ // - (initial sync finishes) app has started, has initial synched
+
+ // We need to wait for the initial sync to complete; but we can't
+ // use waitForHome() like login does.
+
+ // waitForHome() -- does not work because we have already fufilled the initialSync
+ // so we can racily have an IllegalStateException that we have transitioned from busy -> idle
+ // but never having sent the signal.
+
+ // So we need to not start waiting for an initial sync until we have restarted
+ // then we do need to wait for the sync to complete.
+
+ // Which is convoluted especially as it involves the app state refreshing
+ // so; in order to make this be more stable
+ // I hereby cheat and write:
+ Thread.sleep(30_000)
}
else -> {
}
diff --git a/vector/src/androidTest/java/im/vector/app/ui/robot/space/SpaceMenuRobot.kt b/vector/src/androidTest/java/im/vector/app/ui/robot/space/SpaceMenuRobot.kt
index 4d35e3c550..289c6e21b4 100644
--- a/vector/src/androidTest/java/im/vector/app/ui/robot/space/SpaceMenuRobot.kt
+++ b/vector/src/androidTest/java/im/vector/app/ui/robot/space/SpaceMenuRobot.kt
@@ -104,11 +104,10 @@ class SpaceMenuRobot {
fun leaveSpace() {
clickOnSheet(R.id.leaveSpace)
- waitUntilDialogVisible(ViewMatchers.withId(R.id.leaveButton))
- clickOn(R.id.leave_selected)
waitUntilActivityVisible {
waitUntilViewVisible(ViewMatchers.withId(R.id.roomList))
}
+ clickOn(R.id.spaceLeaveSelectAll)
clickOn(R.id.spaceLeaveButton)
waitUntilViewVisible(ViewMatchers.withId(R.id.groupListView))
}
diff --git a/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt b/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
index f2904e4b1a..00a073f832 100644
--- a/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
+++ b/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
@@ -60,11 +60,6 @@ class DebugFeaturesStateFactory @Inject constructor(
key = DebugFeatureKeys.onboardingCombinedRegister,
factory = VectorFeatures::isOnboardingCombinedRegisterEnabled
),
- createBooleanFeature(
- label = "Live location sharing",
- key = DebugFeatureKeys.liveLocationSharing,
- factory = VectorFeatures::isLiveLocationEnabled
- ),
)
)
}
diff --git a/vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt b/vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
index 07fab8a58d..1bc37ff97e 100644
--- a/vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
+++ b/vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
@@ -57,9 +57,6 @@ class DebugVectorFeatures(
override fun isOnboardingCombinedRegisterEnabled(): Boolean = read(DebugFeatureKeys.onboardingCombinedRegister)
?: vectorFeatures.isOnboardingCombinedRegisterEnabled()
- override fun isLiveLocationEnabled(): Boolean = read(DebugFeatureKeys.liveLocationSharing)
- ?: vectorFeatures.isLiveLocationEnabled()
-
override fun isScreenSharingEnabled(): Boolean = read(DebugFeatureKeys.screenSharing)
?: vectorFeatures.isScreenSharingEnabled()
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 425fd1081a..6e36d5dd81 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
@@ -46,7 +46,7 @@ object FcmHelper {
* Store FCM token to the SharedPrefs
*
* @param context android context
- * @param token the token to store
+ * @param token the token to store
*/
fun storeFcmToken(context: Context, token: String?) {
// No op
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 b62520278a..a7d814052a 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
@@ -1,15 +1,12 @@
/*
* 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.
@@ -32,7 +29,6 @@ import im.vector.app.BuildConfig
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.network.WifiDetector
import im.vector.app.core.pushers.PushersManager
-import im.vector.app.features.badge.BadgeProxy
import im.vector.app.features.notifications.NotifiableEventResolver
import im.vector.app.features.notifications.NotificationDrawerManager
import im.vector.app.features.notifications.NotificationUtils
@@ -152,10 +148,6 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
Timber.tag(loggerTag.value).d("## onMessageReceivedInternal()")
}
- // update the badge counter
- val unreadCount = data["unread"]?.let { Integer.parseInt(it) } ?: 0
- BadgeProxy.updateBadgeCount(applicationContext, unreadCount)
-
val session = activeSessionHolder.getSafeActiveSession()
if (session == null) {
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 3d44f10f76..74ab3b38f1 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
@@ -53,7 +53,7 @@ object FcmHelper {
* TODO Store in realm
*
* @param context android context
- * @param token the token to store
+ * @param token the token to store
*/
fun storeFcmToken(context: Context,
token: String?) {
diff --git a/vector/src/main/assets/open_source_licenses.html b/vector/src/main/assets/open_source_licenses.html
index 0bead1f826..8f27776fbf 100755
--- a/vector/src/main/assets/open_source_licenses.html
+++ b/vector/src/main/assets/open_source_licenses.html
@@ -369,11 +369,6 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Copyright 2012 Square, Inc.
-
- ShortcutBadger
-
- Copyright 2014 Leo Lin
-
diff-match-patch
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 c43b2e4f09..8eaced1c48 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
@@ -39,8 +39,9 @@ class UnrecognizedCertificateDialog @Inject constructor(
* Display a certificate dialog box, asking the user about an unknown certificate
* To use when user is currently logged in.
*
+ * @param activity the Android activity
* @param unrecognizedFingerprint the fingerprint for the unknown certificate
- * @param callback callback to fire when the user makes a decision
+ * @param callback callback to fire when the user makes a decision
*/
fun show(activity: Activity,
unrecognizedFingerprint: Fingerprint,
@@ -80,9 +81,13 @@ class UnrecognizedCertificateDialog @Inject constructor(
/**
* Display a certificate dialog box, asking the user about an unknown certificate.
*
+ * @param activity the Activity
* @param unrecognizedFingerprint the fingerprint for the unknown certificate
- * @param existing the current session already exist, so it mean that something has changed server side
- * @param callback callback to fire when the user makes a decision
+ * @param existing the current session already exist, so it mean that something has changed server side
+ * @param callback callback to fire when the user makes a decision
+ * @param userId the matrix userId
+ * @param homeServerUrl the homeserver url
+ * @param homeServerConnectionConfigHasFingerprints true if the homeServerConnectionConfig has fingerprint
*/
private fun internalShow(activity: Activity,
unrecognizedFingerprint: Fingerprint,
diff --git a/vector/src/main/java/im/vector/app/core/intent/ExternalIntentData.kt b/vector/src/main/java/im/vector/app/core/intent/ExternalIntentData.kt
index 142a7a6782..1d7247d758 100644
--- a/vector/src/main/java/im/vector/app/core/intent/ExternalIntentData.kt
+++ b/vector/src/main/java/im/vector/app/core/intent/ExternalIntentData.kt
@@ -29,9 +29,11 @@ sealed class ExternalIntentData {
/**
* Constructor for a text message.
*
- * @param text the text
- * @param htmlText the HTML text
- * @param format the formatted text format
+ * @property text the text
+ * @property htmlText the HTML text
+ * @property format the formatted text format
+ * @property clipDataItem the ClipData
+ * @property mimeType the mimetype
*/
data class IntentDataText(
val text: CharSequence? = null,
@@ -52,8 +54,8 @@ sealed class ExternalIntentData {
/**
* Constructor from a media Uri/.
*
- * @param uri the media uri
- * @param filename the media file name
+ * @property uri the media uri
+ * @property filename the media file name
*/
data class IntentDataUri(
val uri: Uri,
diff --git a/vector/src/main/java/im/vector/app/core/intent/VectorMimeType.kt b/vector/src/main/java/im/vector/app/core/intent/VectorMimeType.kt
index e68b5e1b07..38e304e1ce 100644
--- a/vector/src/main/java/im/vector/app/core/intent/VectorMimeType.kt
+++ b/vector/src/main/java/im/vector/app/core/intent/VectorMimeType.kt
@@ -27,6 +27,7 @@ import java.util.Locale
* Returns the mimetype from a uri.
*
* @param context the context
+ * @param uri the uri
* @return the mimetype
*/
fun getMimeTypeFromUri(context: Context, uri: Uri): String? {
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 d904a5a4de..8a4aaa4b26 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
@@ -55,6 +55,7 @@ import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.di.ActivityEntryPoint
import im.vector.app.core.dialogs.DialogLocker
import im.vector.app.core.dialogs.UnrecognizedCertificateDialog
+import im.vector.app.core.error.fatalError
import im.vector.app.core.extensions.observeEvent
import im.vector.app.core.extensions.observeNotNull
import im.vector.app.core.extensions.registerStartForActivityResult
@@ -611,11 +612,7 @@ abstract class VectorBaseActivity : AppCompatActivity(), Maver
}
}.show()
} else {
- if (vectorPreferences.failFast()) {
- error("No CoordinatorLayout to display this snackbar!")
- } else {
- Timber.w("No CoordinatorLayout to display this snackbar!")
- }
+ fatalError("No CoordinatorLayout to display this snackbar!", vectorPreferences.failFast())
}
}
diff --git a/vector/src/main/java/im/vector/app/core/preference/PushRulePreference.kt b/vector/src/main/java/im/vector/app/core/preference/PushRulePreference.kt
index dad7f26560..bea29195c9 100644
--- a/vector/src/main/java/im/vector/app/core/preference/PushRulePreference.kt
+++ b/vector/src/main/java/im/vector/app/core/preference/PushRulePreference.kt
@@ -45,7 +45,7 @@ class PushRulePreference : VectorPreference {
/**
* Update the notification index.
*
- * @param pushRule
+ * @param notificationIndex the new notification index
*/
fun setIndex(notificationIndex: NotificationIndex?) {
index = notificationIndex
diff --git a/vector/src/main/java/im/vector/app/core/resources/DateProvider.kt b/vector/src/main/java/im/vector/app/core/resources/DateProvider.kt
index 30cb1dcae4..6762bd68da 100644
--- a/vector/src/main/java/im/vector/app/core/resources/DateProvider.kt
+++ b/vector/src/main/java/im/vector/app/core/resources/DateProvider.kt
@@ -19,27 +19,30 @@ package im.vector.app.core.resources
import org.threeten.bp.Instant
import org.threeten.bp.LocalDateTime
import org.threeten.bp.ZoneId
+import org.threeten.bp.ZoneOffset
object DateProvider {
- private val zoneId = ZoneId.systemDefault()
- private val zoneOffset by lazy {
- val now = currentLocalDateTime()
- zoneId.rules.getOffset(now)
- }
+ // recompute the zoneId each time we access it to handle change of timezones
+ private val defaultZoneId: ZoneId
+ get() = ZoneId.systemDefault()
+
+ // recompute the zoneOffset each time we access it to handle change of timezones
+ private val defaultZoneOffset: ZoneOffset
+ get() = defaultZoneId.rules.getOffset(currentLocalDateTime())
fun toLocalDateTime(timestamp: Long?): LocalDateTime {
val instant = Instant.ofEpochMilli(timestamp ?: 0)
- return LocalDateTime.ofInstant(instant, zoneId)
+ return LocalDateTime.ofInstant(instant, defaultZoneId)
}
fun currentLocalDateTime(): LocalDateTime {
val instant = Instant.now()
- return LocalDateTime.ofInstant(instant, zoneId)
+ return LocalDateTime.ofInstant(instant, defaultZoneId)
}
fun toTimestamp(localDateTime: LocalDateTime): Long {
- return localDateTime.toInstant(zoneOffset).toEpochMilli()
+ return localDateTime.toInstant(defaultZoneOffset).toEpochMilli()
}
}
diff --git a/vector/src/main/java/im/vector/app/core/resources/Resource.kt b/vector/src/main/java/im/vector/app/core/resources/Resource.kt
index f14c9b834d..861dfdb781 100644
--- a/vector/src/main/java/im/vector/app/core/resources/Resource.kt
+++ b/vector/src/main/java/im/vector/app/core/resources/Resource.kt
@@ -56,8 +56,8 @@ data class Resource(
/**
* Get a resource stream and metadata about it given its URI returned from onActivityResult.
*
- * @param context the context.
- * @param uri the URI
+ * @param context the context.
+ * @param uri the URI
* @param providedMimetype the mimetype
* @return a [Resource] encapsulating the opened resource stream and associated metadata
* or `null` if opening the resource stream failed.
diff --git a/vector/src/main/java/im/vector/app/core/ui/views/KeysBackupBanner.kt b/vector/src/main/java/im/vector/app/core/ui/views/KeysBackupBanner.kt
index f2ea79984e..80603aa3bf 100755
--- a/vector/src/main/java/im/vector/app/core/ui/views/KeysBackupBanner.kt
+++ b/vector/src/main/java/im/vector/app/core/ui/views/KeysBackupBanner.kt
@@ -54,6 +54,7 @@ class KeysBackupBanner @JvmOverloads constructor(
* This methods is responsible for rendering the view according to the newState.
*
* @param newState the newState representing the view
+ * @param force true to force the rendering of the view
*/
fun render(newState: State, force: Boolean = false) {
if (newState == state && !force) {
diff --git a/vector/src/main/java/im/vector/app/core/ui/views/TypingMessageAvatar.kt b/vector/src/main/java/im/vector/app/core/ui/views/TypingMessageAvatar.kt
index 22312f36fa..1f78584a6b 100644
--- a/vector/src/main/java/im/vector/app/core/ui/views/TypingMessageAvatar.kt
+++ b/vector/src/main/java/im/vector/app/core/ui/views/TypingMessageAvatar.kt
@@ -22,7 +22,7 @@ import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
-import im.vector.app.core.utils.DimensionConverter
+import im.vector.app.R
import im.vector.app.features.home.AvatarRenderer
import org.matrix.android.sdk.api.session.room.sender.SenderInfo
import org.matrix.android.sdk.api.util.toMatrixItem
@@ -34,19 +34,22 @@ class TypingMessageAvatar @JvmOverloads constructor(
) : LinearLayout(context, attrs, defStyleAttr) {
companion object {
- const val AVATAR_SIZE_DP = 20
const val OVERLAP_FACT0R = -3 // =~ 30% to left
}
+ private val typingAvatarSize by lazy(LazyThreadSafetyMode.NONE) {
+ context.resources.getDimension(R.dimen.typing_avatar_size).toInt()
+ }
+
fun render(typingUsers: List, avatarRenderer: AvatarRenderer) {
removeAllViews()
for ((index, value) in typingUsers.withIndex()) {
val avatar = ImageView(context)
avatar.id = View.generateViewId()
val layoutParams = MarginLayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
- if (index != 0) layoutParams.marginStart = DimensionConverter(resources).dpToPx(AVATAR_SIZE_DP / OVERLAP_FACT0R)
- layoutParams.width = DimensionConverter(resources).dpToPx(AVATAR_SIZE_DP)
- layoutParams.height = DimensionConverter(resources).dpToPx(AVATAR_SIZE_DP)
+ if (index != 0) layoutParams.marginStart = typingAvatarSize / OVERLAP_FACT0R
+ layoutParams.width = typingAvatarSize
+ layoutParams.height = typingAvatarSize
avatar.layoutParams = layoutParams
avatarRenderer.render(value.toMatrixItem(), avatar)
addView(avatar)
diff --git a/vector/src/main/java/im/vector/app/core/ui/views/TypingMessageView.kt b/vector/src/main/java/im/vector/app/core/ui/views/TypingMessageView.kt
index 2dc9eedd99..263f043fad 100644
--- a/vector/src/main/java/im/vector/app/core/ui/views/TypingMessageView.kt
+++ b/vector/src/main/java/im/vector/app/core/ui/views/TypingMessageView.kt
@@ -31,7 +31,8 @@ import javax.inject.Inject
class TypingMessageView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
- defStyleAttr: Int = 0) : ConstraintLayout(context, attrs, defStyleAttr) {
+ defStyleAttr: Int = 0
+) : ConstraintLayout(context, attrs, defStyleAttr) {
val views: TypingMessageLayoutBinding
@@ -44,8 +45,8 @@ class TypingMessageView @JvmOverloads constructor(
}
fun render(typingUsers: List, avatarRenderer: AvatarRenderer) {
- views.usersName.text = typingHelper.getNotificationTypingMessage(typingUsers)
- views.avatars.render(typingUsers, avatarRenderer)
+ views.typingUserText.text = typingHelper.getNotificationTypingMessage(typingUsers)
+ views.typingUserAvatars.render(typingUsers, avatarRenderer)
}
override fun onDetachedFromWindow() {
diff --git a/vector/src/main/java/im/vector/app/core/utils/Dialogs.kt b/vector/src/main/java/im/vector/app/core/utils/Dialogs.kt
index 10ab0fc027..9f3e6a91cf 100644
--- a/vector/src/main/java/im/vector/app/core/utils/Dialogs.kt
+++ b/vector/src/main/java/im/vector/app/core/utils/Dialogs.kt
@@ -30,7 +30,7 @@ import me.gujun.android.span.span
/**
* Open a web view above the current activity.
*
- * @param url the url to open
+ * @param url the url to open
*/
fun Context.displayInWebView(url: String) {
val wv = WebView(this)
diff --git a/vector/src/main/java/im/vector/app/core/utils/EvenBetterLinkMovementMethod.kt b/vector/src/main/java/im/vector/app/core/utils/EvenBetterLinkMovementMethod.kt
index b9c1386933..a53c8161b1 100644
--- a/vector/src/main/java/im/vector/app/core/utils/EvenBetterLinkMovementMethod.kt
+++ b/vector/src/main/java/im/vector/app/core/utils/EvenBetterLinkMovementMethod.kt
@@ -26,9 +26,9 @@ class EvenBetterLinkMovementMethod(private val onLinkClickListener: OnLinkClickL
interface OnLinkClickListener {
/**
- * @param textView The TextView on which a click was registered.
- * @param span The ClickableSpan which is clicked on.
- * @param url The clicked URL.
+ * @param textView The TextView on which a click was registered.
+ * @param span The ClickableSpan which is clicked on.
+ * @param url The clicked URL.
* @param actualText The original text which is spanned. Can be used to compare actualText and target url to prevent misleading urls.
* @return true if this click was handled, false to let Android handle the URL.
*/
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 8bfbcaeb92..9616e35840 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
@@ -170,9 +170,9 @@ fun openUri(activity: Activity, uri: String) {
/**
* Send media to a third party application.
*
- * @param activity the activity
+ * @param activity the activity
* @param savedMediaPath the media path
- * @param mimeType the media mime type.
+ * @param mimeType the media mime type.
*/
fun openMedia(activity: Activity, savedMediaPath: String, mimeType: String) {
val file = File(savedMediaPath)
@@ -415,8 +415,8 @@ fun selectTxtFileToWrite(
*
* ~~ This is copied from the old matrix sdk ~~
*
- * @param sourceFile the file source path
- * @param dstDirPath the dst path
+ * @param sourceFile the file source path
+ * @param dstDirPath the dst path
* @param outputFilename optional the output filename
* @param currentTimeMillis the current time in milliseconds
* @return the created file
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 b4f8de2485..a41abba7ab 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
@@ -101,9 +101,9 @@ private fun onPermissionResult(result: Map, lambda: (allGranted
* explain why vector needs the corresponding permission.
*
* @param permissionsToBeGranted the permissions to be granted
- * @param activity the calling Activity that is requesting the permissions (or fragment parent)
+ * @param activity the calling Activity that is requesting the permissions (or fragment parent)
* @param activityResultLauncher from the calling fragment/Activity that is requesting the permissions
- * @param rationaleMessage message to be displayed BEFORE requesting for the permission
+ * @param rationaleMessage message to be displayed BEFORE requesting for the permission
* @return true if the permissions are granted (synchronous flow), false otherwise (asynchronous flow)
*/
fun checkPermissions(permissionsToBeGranted: List,
@@ -145,7 +145,7 @@ fun checkPermissions(permissionsToBeGranted: List,
* To be call after the permission request.
*
* @param permissionsToBeGranted the permissions to be granted
- * @param activity the calling Activity that is requesting the permissions (or fragment parent)
+ * @param activity the calling Activity that is requesting the permissions (or fragment parent)
*
* @return true if one of the permission has been denied and the user check the do not ask again checkbox
*/
diff --git a/vector/src/main/java/im/vector/app/core/utils/RingtoneUtils.kt b/vector/src/main/java/im/vector/app/core/utils/RingtoneUtils.kt
index a0fd3addac..bbed2f6000 100644
--- a/vector/src/main/java/im/vector/app/core/utils/RingtoneUtils.kt
+++ b/vector/src/main/java/im/vector/app/core/utils/RingtoneUtils.kt
@@ -90,6 +90,7 @@ fun getCallRingtoneName(context: Context): String? {
/**
* Sets the selected ringtone for riot calls.
*
+ * @param context Android context
* @param ringtoneUri
* @see Ringtone
*/
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 18a467d8d0..1d9ac6c3ef 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
@@ -82,7 +82,9 @@ fun requestDisablingBatteryOptimization(activity: Activity, activityResultLaunch
* Copy a text to the clipboard, and display a Toast when done.
*
* @param context the context
- * @param text the text to copy
+ * @param text the text to copy
+ * @param showToast true to also show a Toast to the user
+ * @param toastMessage content of the toast message as a String resource
*/
fun copyToClipboard(context: Context, text: CharSequence, showToast: Boolean = true, @StringRes toastMessage: Int = R.string.copied_to_clipboard) {
val clipboard = context.getSystemService()!!
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 fb386e0876..bd1e396126 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,7 +23,8 @@ 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
+ * @param T type of the data to store
+ * @property delay delay to keep the data, in millis
*/
open class TemporaryStore(private val delay: Long = THREE_MINUTES) {
diff --git a/vector/src/main/java/im/vector/app/core/utils/TextUtils.kt b/vector/src/main/java/im/vector/app/core/utils/TextUtils.kt
index 992a85679c..d2f8c4022b 100644
--- a/vector/src/main/java/im/vector/app/core/utils/TextUtils.kt
+++ b/vector/src/main/java/im/vector/app/core/utils/TextUtils.kt
@@ -19,11 +19,15 @@ package im.vector.app.core.utils
import android.content.Context
import android.os.Build
import android.text.format.Formatter
+import im.vector.app.R
import org.threeten.bp.Duration
import java.util.TreeMap
object TextUtils {
+ private const val MINUTES_PER_HOUR = 60
+ private const val SECONDS_PER_MINUTE = 60
+
private val suffixes = TreeMap().also {
it[1000] = "k"
it[1000000] = "M"
@@ -71,13 +75,63 @@ object TextUtils {
}
fun formatDuration(duration: Duration): String {
- val hours = duration.seconds / 3600
- val minutes = (duration.seconds % 3600) / 60
- val seconds = duration.seconds % 60
+ val hours = getHours(duration)
+ val minutes = getMinutes(duration)
+ val seconds = getSeconds(duration)
return if (hours > 0) {
String.format("%d:%02d:%02d", hours, minutes, seconds)
} else {
String.format("%02d:%02d", minutes, seconds)
}
}
+
+ fun formatDurationWithUnits(context: Context, duration: Duration): String {
+ val hours = getHours(duration)
+ val minutes = getMinutes(duration)
+ val seconds = getSeconds(duration)
+ val builder = StringBuilder()
+ when {
+ hours > 0 -> {
+ appendHours(context, builder, hours)
+ if (minutes > 0) {
+ builder.append(" ")
+ appendMinutes(context, builder, minutes)
+ }
+ if (seconds > 0) {
+ builder.append(" ")
+ appendSeconds(context, builder, seconds)
+ }
+ }
+ minutes > 0 -> {
+ appendMinutes(context, builder, minutes)
+ if (seconds > 0) {
+ builder.append(" ")
+ appendSeconds(context, builder, seconds)
+ }
+ }
+ else -> {
+ appendSeconds(context, builder, seconds)
+ }
+ }
+ return builder.toString()
+ }
+
+ private fun appendHours(context: Context, builder: StringBuilder, hours: Int) {
+ builder.append(hours)
+ builder.append(context.resources.getString(R.string.time_unit_hour_short))
+ }
+
+ private fun appendMinutes(context: Context, builder: StringBuilder, minutes: Int) {
+ builder.append(minutes)
+ builder.append(context.getString(R.string.time_unit_minute_short))
+ }
+
+ private fun appendSeconds(context: Context, builder: StringBuilder, seconds: Int) {
+ builder.append(seconds)
+ builder.append(context.getString(R.string.time_unit_second_short))
+ }
+
+ private fun getHours(duration: Duration): Int = duration.toHours().toInt()
+ private fun getMinutes(duration: Duration): Int = duration.toMinutes().toInt() % MINUTES_PER_HOUR
+ private fun getSeconds(duration: Duration): Int = (duration.seconds % SECONDS_PER_MINUTE).toInt()
}
diff --git a/vector/src/main/java/im/vector/app/core/utils/ToggleableAppBarLayoutBehavior.kt b/vector/src/main/java/im/vector/app/core/utils/ToggleableAppBarLayoutBehavior.kt
new file mode 100644
index 0000000000..c829313256
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/core/utils/ToggleableAppBarLayoutBehavior.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.core.utils
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.View
+import androidx.coordinatorlayout.widget.CoordinatorLayout
+import com.google.android.material.appbar.AppBarLayout
+
+/**
+ * [AppBarLayout.Behavior] subclass with a possibility to disable behavior.
+ * Useful for cases when in some view state we want prevent toolbar from collapsing/expanding by scroll events
+ */
+class ToggleableAppBarLayoutBehavior : AppBarLayout.Behavior {
+ constructor() : super()
+ constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
+
+ var isEnabled = true
+
+ override fun onStartNestedScroll(parent: CoordinatorLayout,
+ child: AppBarLayout,
+ directTargetChild: View,
+ target: View,
+ nestedScrollAxes: Int,
+ type: Int): Boolean {
+ return isEnabled && super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes, type)
+ }
+
+ override fun onNestedScroll(coordinatorLayout: CoordinatorLayout,
+ child: AppBarLayout,
+ target: View,
+ dxConsumed: Int,
+ dyConsumed: Int,
+ dxUnconsumed: Int,
+ dyUnconsumed: Int,
+ type: Int,
+ consumed: IntArray) {
+ if (!isEnabled) return
+ super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type, consumed)
+ }
+
+ override fun onNestedPreScroll(coordinatorLayout: CoordinatorLayout,
+ child: AppBarLayout,
+ target: View,
+ dx: Int,
+ dy: Int,
+ consumed: IntArray,
+ type: Int) {
+ if (!isEnabled) return
+ super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type)
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
index 42693a53f9..e3fded2824 100644
--- a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
+++ b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
@@ -26,7 +26,6 @@ interface VectorFeatures {
fun isOnboardingUseCaseEnabled(): Boolean
fun isOnboardingPersonalizeEnabled(): Boolean
fun isOnboardingCombinedRegisterEnabled(): Boolean
- fun isLiveLocationEnabled(): Boolean
fun isScreenSharingEnabled(): Boolean
enum class OnboardingVariant {
@@ -43,6 +42,5 @@ class DefaultVectorFeatures : VectorFeatures {
override fun isOnboardingUseCaseEnabled() = true
override fun isOnboardingPersonalizeEnabled() = false
override fun isOnboardingCombinedRegisterEnabled() = false
- override fun isLiveLocationEnabled(): Boolean = false
override fun isScreenSharingEnabled(): Boolean = true
}
diff --git a/vector/src/main/java/im/vector/app/features/badge/BadgeProxy.kt b/vector/src/main/java/im/vector/app/features/badge/BadgeProxy.kt
deleted file mode 100644
index c15ec8bdf4..0000000000
--- a/vector/src/main/java/im/vector/app/features/badge/BadgeProxy.kt
+++ /dev/null
@@ -1,129 +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.
- */
-
-@file:Suppress("UNUSED_PARAMETER")
-
-package im.vector.app.features.badge
-
-import android.content.Context
-import android.os.Build
-import me.leolin.shortcutbadger.ShortcutBadger
-import org.matrix.android.sdk.api.session.Session
-
-/**
- * Manage application badge (displayed in the launcher).
- */
-object BadgeProxy {
-
- /**
- * Badge is now managed by notification channel, so no need to use compatibility library in recent versions.
- *
- * @return true if library ShortcutBadger can be used
- */
- private fun useShortcutBadger() = Build.VERSION.SDK_INT < Build.VERSION_CODES.O
-
- /**
- * Update the application badge value.
- *
- * @param context the context
- * @param badgeValue the new badge value
- */
- fun updateBadgeCount(context: Context, badgeValue: Int) {
- if (!useShortcutBadger()) {
- return
- }
-
- ShortcutBadger.applyCount(context, badgeValue)
- }
-
- /**
- * Refresh the badge count for specific configurations.
- * The refresh is only effective if the device is:
- * * offline * does not support FCM
- * * FCM registration failed
- * Notifications rooms are parsed to track the notification count value.
- *
- * @param aSession session value
- * @param aContext App context
- */
- fun specificUpdateBadgeUnreadCount(aSession: Session?, aContext: Context?) {
- if (!useShortcutBadger()) {
- return
- }
-
- /* TODO
- val dataHandler: MXDataHandler
-
- // sanity check
- if (null == aContext || null == aSession) {
- Timber.w("## specificUpdateBadgeUnreadCount(): invalid input null values")
- } else {
- dataHandler = aSession.dataHandler
-
- if (dataHandler == null) {
- Timber.w("## specificUpdateBadgeUnreadCount(): invalid DataHandler instance")
- } else {
- if (aSession.isAlive) {
- var isRefreshRequired: Boolean
- val pushManager = Matrix.getInstance(aContext)!!.pushManager
-
- // update the badge count if the device is offline, FCM is not supported or FCM registration failed
- isRefreshRequired = !Matrix.getInstance(aContext)!!.isConnected
- isRefreshRequired = isRefreshRequired or (null != pushManager && (!pushManager.useFcm() || !pushManager.hasRegistrationToken()))
-
- if (isRefreshRequired) {
- updateBadgeCount(aContext, dataHandler)
- }
- }
- }
- }
- */
- }
-
- /**
- * Update the badge count value according to the rooms content.
- *
- * @param aContext App context
- * @param aDataHandler data handler instance
- */
- private fun updateBadgeCount(aSession: Session?, aContext: Context?) {
- if (!useShortcutBadger()) {
- return
- }
-
- /* TODO
- //sanity check
- if (null == aContext || null == aDataHandler) {
- Timber.w("## updateBadgeCount(): invalid input null values")
- } else if (null == aDataHandler.store) {
- Timber.w("## updateBadgeCount(): invalid store instance")
- } else {
- val roomCompleteList = ArrayList(aDataHandler.store.rooms)
- var unreadRoomsCount = 0
-
- for (room in roomCompleteList) {
- if (room.notificationCount > 0) {
- unreadRoomsCount++
- }
- }
-
- // update the badge counter
- Timber.v("## updateBadgeCount(): badge update count=$unreadRoomsCount")
- updateBadgeCount(aContext, unreadRoomsCount)
- }
- */
- }
-}
diff --git a/vector/src/main/java/im/vector/app/features/call/audio/CallAudioManager.kt b/vector/src/main/java/im/vector/app/features/call/audio/CallAudioManager.kt
index d4793640d3..6577d0374d 100644
--- a/vector/src/main/java/im/vector/app/features/call/audio/CallAudioManager.kt
+++ b/vector/src/main/java/im/vector/app/features/call/audio/CallAudioManager.kt
@@ -122,6 +122,7 @@ class CallAudioManager(private val context: Context, val configChange: (() -> Un
* Updates the audio route for the given mode.
*
* @param mode the audio mode to be used when computing the audio route.
+ * @param force true to force setting the audio route
* @return `true` if the audio route was updated successfully;
* `false`, otherwise.
*/
diff --git a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt
index 672e9ca849..b92316c292 100644
--- a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt
+++ b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt
@@ -683,6 +683,8 @@ class WebRtcCall(
direction = RtpTransceiver.RtpTransceiverDirection.SEND_RECV
}
for (transceiver in peerConnection?.transceivers ?: emptyList()) {
+ transceiver.sender.track()?.setEnabled(!onHold)
+ transceiver.receiver.track()?.setEnabled(!onHold)
transceiver.direction = direction
}
updateMuteStatus()
diff --git a/vector/src/main/java/im/vector/app/features/command/CommandParser.kt b/vector/src/main/java/im/vector/app/features/command/CommandParser.kt
index 49e35687f4..17b8087601 100644
--- a/vector/src/main/java/im/vector/app/features/command/CommandParser.kt
+++ b/vector/src/main/java/im/vector/app/features/command/CommandParser.kt
@@ -30,7 +30,8 @@ class CommandParser @Inject constructor() {
/**
* Convert the text message into a Slash command.
*
- * @param textMessage the text message
+ * @param textMessage the text message
+ * @param isInThreadTimeline true if the user is currently typing in a thread
* @return a parsed slash command (ok or error)
*/
fun parseSlashCommand(textMessage: CharSequence, isInThreadTimeline: Boolean): ParsedCommand {
@@ -412,8 +413,8 @@ class CommandParser @Inject constructor() {
/**
* Checks whether or not the current command is not supported by threads.
- * @param slashCommand the slash command that will be checked
* @param isInThreadTimeline if its true we are in a thread timeline
+ * @param slashCommand the slash command that will be checked
* @return The command that is not supported
*/
private fun getNotSupportedByThreads(isInThreadTimeline: Boolean, slashCommand: String): Command? {
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt
index 2bb620623c..635b00c05d 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt
@@ -1529,7 +1529,7 @@ class TimelineFragment @Inject constructor(
views.composerLayout.views.composerEmojiButton.isVisible = vectorPreferences.showEmojiKeyboard()
- if (isThreadTimeLine() && timelineArgs.threadTimelineArgs?.startsThread == true) {
+ if (isThreadTimeLine() && timelineArgs.threadTimelineArgs?.showKeyboard == true) {
// Show keyboard when the user started a thread
views.composerLayout.views.composerEditText.showKeyboard(andRequestFocus = true)
}
@@ -2443,7 +2443,11 @@ class TimelineFragment @Inject constructor(
private fun onReplyInThreadClicked(action: EventSharedAction.ReplyInThread) {
if (vectorPreferences.areThreadMessagesEnabled()) {
- navigateToThreadTimeline(action.eventId, action.startsThread)
+ navigateToThreadTimeline(
+ rootThreadEventId = action.eventId,
+ startsThread = action.startsThread,
+ showKeyboard = true
+ )
} else {
displayThreadsBetaOptInDialog()
}
@@ -2453,7 +2457,7 @@ class TimelineFragment @Inject constructor(
* Navigate to Threads timeline for the specified rootThreadEventId
* using the ThreadsActivity.
*/
- private fun navigateToThreadTimeline(rootThreadEventId: String, startsThread: Boolean = false) {
+ private fun navigateToThreadTimeline(rootThreadEventId: String, startsThread: Boolean = false, showKeyboard: Boolean = false) {
analyticsTracker.capture(Interaction.Name.MobileRoomThreadSummaryItem.toAnalyticsInteraction())
context?.let {
val roomThreadDetailArgs = ThreadTimelineArgs(
@@ -2462,7 +2466,8 @@ class TimelineFragment @Inject constructor(
displayName = timelineViewModel.getRoomSummary()?.displayName,
avatarUrl = timelineViewModel.getRoomSummary()?.avatarUrl,
roomEncryptionTrustLevel = timelineViewModel.getRoomSummary()?.roomEncryptionTrustLevel,
- rootThreadEventId = rootThreadEventId
+ rootThreadEventId = rootThreadEventId,
+ showKeyboard = showKeyboard
)
navigator.openThread(it, roomThreadDetailArgs)
}
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
index 1952e598a6..011258f126 100644
--- 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
@@ -121,14 +121,25 @@ class SearchFragment @Inject constructor(
override fun onItemClicked(event: Event) =
navigateToEvent(event)
+ override fun onThreadSummaryClicked(event: Event) {
+ navigateToEvent(event, true)
+ }
+
/**
* Navigate and highlight the event. If this is a thread event,
* user will be redirected to the appropriate thread room
* @param event the event to navigate and highlight
+ * @param forceNavigateToThread force navigate within the thread (ex. when user clicks on thread summary)
*/
- private fun navigateToEvent(event: Event) {
+ private fun navigateToEvent(event: Event, forceNavigateToThread: Boolean = false) {
val roomId = event.roomId ?: return
- event.getRootThreadEventId()?.let {
+ val rootThreadEventId = if (forceNavigateToThread) {
+ event.eventId
+ } else {
+ event.getRootThreadEventId()
+ }
+
+ rootThreadEventId?.let {
val threadTimelineArgs = ThreadTimelineArgs(
roomId = roomId,
displayName = fragmentArgs.roomDisplayName,
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
index 913e440a20..81e4d8fd5f 100644
--- 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
@@ -58,6 +58,7 @@ class SearchResultController @Inject constructor(
interface Listener {
fun onItemClicked(event: Event)
+ fun onThreadSummaryClicked(event: Event)
fun loadMore()
}
@@ -134,6 +135,7 @@ class SearchResultController @Inject constructor(
.threadSummaryFormatted(displayableEventFormatter.formatThreadSummary(event.threadDetails?.threadSummaryLatestEvent).toString())
.areThreadMessagesEnabled(userPreferencesProvider.areThreadMessagesEnabled())
.listener { listener?.onItemClicked(eventAndSender.event) }
+ .threadSummaryListener { listener?.onThreadSummaryClicked(eventAndSender.event) }
.let { result.add(it) }
}
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
index 3e141ab0e9..d92dcdd3ef 100644
--- 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
@@ -46,6 +46,7 @@ abstract class SearchResultItem : VectorEpoxyModel() {
@EpoxyAttribute var areThreadMessagesEnabled: Boolean = false
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var listener: ClickListener? = null
+ @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var threadSummaryListener: ClickListener? = null
override fun bind(holder: Holder) {
super.bind(holder)
@@ -66,23 +67,24 @@ abstract class SearchResultItem : VectorEpoxyModel() {
val displayName = it.threadSummarySenderInfo?.displayName
val avatarUrl = it.threadSummarySenderInfo?.avatarUrl
avatarRenderer.render(MatrixItem.UserItem(userId, displayName, avatarUrl), holder.threadSummaryAvatarImageView)
+ holder.threadSummaryContainer.onClick(threadSummaryListener)
} else {
showFromThread(holder)
}
} ?: run {
- holder.threadSummaryConstraintLayout.isVisible = false
+ holder.threadSummaryContainer.isVisible = false
holder.fromThreadConstraintLayout.isVisible = false
}
}
}
private fun showThreadSummary(holder: Holder, show: Boolean = true) {
- holder.threadSummaryConstraintLayout.isVisible = show
+ holder.threadSummaryContainer.isVisible = show
holder.fromThreadConstraintLayout.isVisible = !show
}
private fun showFromThread(holder: Holder, show: Boolean = true) {
- holder.threadSummaryConstraintLayout.isVisible = !show
+ holder.threadSummaryContainer.isVisible = !show
holder.fromThreadConstraintLayout.isVisible = show
}
@@ -91,7 +93,7 @@ abstract class SearchResultItem : VectorEpoxyModel() {
val memberNameView by bind(R.id.messageMemberNameView)
val timeView by bind(R.id.messageTimeView)
val contentView by bind(R.id.messageContentView)
- val threadSummaryConstraintLayout by bind(R.id.searchThreadSummaryConstraintLayout)
+ val threadSummaryContainer by bind(R.id.searchThreadSummaryContainer)
val threadSummaryCounterTextView by bind(R.id.messageThreadSummaryCounterTextView)
val threadSummaryAvatarImageView by bind(R.id.messageThreadSummaryAvatarImageView)
val threadSummaryInfoTextView by bind(R.id.messageThreadSummaryInfoTextView)
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationMessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationMessageItemFactory.kt
deleted file mode 100644
index d233deffb8..0000000000
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationMessageItemFactory.kt
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (c) 2022 New Vector Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package im.vector.app.features.home.room.detail.timeline.factory
-
-import im.vector.app.core.epoxy.VectorEpoxyModel
-import im.vector.app.core.utils.DimensionConverter
-import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider
-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.MessageLiveLocationStartItem
-import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationStartItem_
-import org.matrix.android.sdk.api.extensions.orFalse
-import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent
-import javax.inject.Inject
-
-class LiveLocationMessageItemFactory @Inject constructor(
- private val dimensionConverter: DimensionConverter,
- private val timelineMediaSizeProvider: TimelineMediaSizeProvider,
- private val avatarSizeProvider: AvatarSizeProvider,
-) {
-
- fun create(
- beaconInfoContent: MessageBeaconInfoContent,
- highlight: Boolean,
- attributes: AbsMessageItem.Attributes,
- ): VectorEpoxyModel<*>? {
- // TODO handle location received and stopped states
- return when {
- isLiveRunning(beaconInfoContent) -> buildStartLiveItem(highlight, attributes)
- else -> null
- }
- }
-
- private fun isLiveRunning(beaconInfoContent: MessageBeaconInfoContent): Boolean {
- // TODO when we will use aggregatedSummary, check if the live has timed out as well
- return beaconInfoContent.isLive.orFalse()
- }
-
- private fun buildStartLiveItem(
- highlight: Boolean,
- attributes: AbsMessageItem.Attributes,
- ): MessageLiveLocationStartItem {
- val width = timelineMediaSizeProvider.getMaxSize().first
- val height = dimensionConverter.dpToPx(MessageItemFactory.MESSAGE_LOCATION_ITEM_HEIGHT_IN_DP)
-
- return MessageLiveLocationStartItem_()
- .attributes(attributes)
- .mapWidth(width)
- .mapHeight(height)
- .highlighted(highlight)
- .leftGuideline(avatarSizeProvider.leftGuideline)
- }
-}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt
new file mode 100644
index 0000000000..479a742369
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.home.room.detail.timeline.factory
+
+import im.vector.app.core.date.VectorDateFormatter
+import im.vector.app.core.epoxy.VectorEpoxyModel
+import im.vector.app.core.resources.DateProvider
+import im.vector.app.core.utils.DimensionConverter
+import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider
+import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
+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.MessageLiveLocationInactiveItem
+import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationInactiveItem_
+import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationItem
+import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationItem_
+import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationStartItem
+import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationStartItem_
+import im.vector.app.features.location.INITIAL_MAP_ZOOM_IN_TIMELINE
+import im.vector.app.features.location.UrlMapProvider
+import im.vector.app.features.location.toLocationData
+import org.matrix.android.sdk.api.extensions.orFalse
+import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
+import org.threeten.bp.LocalDateTime
+import timber.log.Timber
+import javax.inject.Inject
+
+class LiveLocationShareMessageItemFactory @Inject constructor(
+ private val session: Session,
+ private val dimensionConverter: DimensionConverter,
+ private val timelineMediaSizeProvider: TimelineMediaSizeProvider,
+ private val avatarSizeProvider: AvatarSizeProvider,
+ private val urlMapProvider: UrlMapProvider,
+ private val locationPinProvider: LocationPinProvider,
+ private val vectorDateFormatter: VectorDateFormatter,
+) {
+
+ fun create(
+ event: TimelineEvent,
+ highlight: Boolean,
+ attributes: AbsMessageItem.Attributes,
+ ): VectorEpoxyModel<*>? {
+ val liveLocationShareSummaryData = getLiveLocationShareSummaryData(event)
+ val item = when (val currentState = getViewState(liveLocationShareSummaryData)) {
+ LiveLocationShareViewState.Inactive -> buildInactiveItem(highlight, attributes)
+ LiveLocationShareViewState.Loading -> buildLoadingItem(highlight, attributes)
+ is LiveLocationShareViewState.Running -> buildRunningItem(highlight, attributes, currentState)
+ LiveLocationShareViewState.Unkwown -> null
+ }
+ item?.layout(attributes.informationData.messageLayout.layoutRes)
+
+ return item
+ }
+
+ private fun buildInactiveItem(
+ highlight: Boolean,
+ attributes: AbsMessageItem.Attributes,
+ ): MessageLiveLocationInactiveItem {
+ val width = timelineMediaSizeProvider.getMaxSize().first
+ val height = dimensionConverter.dpToPx(MessageItemFactory.MESSAGE_LOCATION_ITEM_HEIGHT_IN_DP)
+
+ return MessageLiveLocationInactiveItem_()
+ .attributes(attributes)
+ .mapWidth(width)
+ .mapHeight(height)
+ .highlighted(highlight)
+ .leftGuideline(avatarSizeProvider.leftGuideline)
+ }
+
+ private fun buildLoadingItem(
+ highlight: Boolean,
+ attributes: AbsMessageItem.Attributes,
+ ): MessageLiveLocationStartItem {
+ val width = timelineMediaSizeProvider.getMaxSize().first
+ val height = dimensionConverter.dpToPx(MessageItemFactory.MESSAGE_LOCATION_ITEM_HEIGHT_IN_DP)
+
+ return MessageLiveLocationStartItem_()
+ .attributes(attributes)
+ .mapWidth(width)
+ .mapHeight(height)
+ .highlighted(highlight)
+ .leftGuideline(avatarSizeProvider.leftGuideline)
+ }
+
+ private fun buildRunningItem(
+ highlight: Boolean,
+ attributes: AbsMessageItem.Attributes,
+ runningState: LiveLocationShareViewState.Running,
+ ): MessageLiveLocationItem {
+ // TODO only render location if enabled in preferences: to be handled in a next PR
+ val width = timelineMediaSizeProvider.getMaxSize().first
+ val height = dimensionConverter.dpToPx(MessageItemFactory.MESSAGE_LOCATION_ITEM_HEIGHT_IN_DP)
+
+ val locationUrl = runningState.lastGeoUri.toLocationData()?.let {
+ urlMapProvider.buildStaticMapUrl(it, INITIAL_MAP_ZOOM_IN_TIMELINE, width, height)
+ }
+
+ return MessageLiveLocationItem_()
+ .attributes(attributes)
+ .locationUrl(locationUrl)
+ .mapWidth(width)
+ .mapHeight(height)
+ .locationUserId(attributes.informationData.senderId)
+ .locationPinProvider(locationPinProvider)
+ .highlighted(highlight)
+ .leftGuideline(avatarSizeProvider.leftGuideline)
+ .currentUserId(session.myUserId)
+ .endOfLiveDateTime(runningState.endOfLiveDateTime)
+ .vectorDateFormatter(vectorDateFormatter)
+ }
+
+ private fun getViewState(liveLocationShareSummaryData: LiveLocationShareSummaryData?): LiveLocationShareViewState {
+ return when {
+ liveLocationShareSummaryData?.isActive == null -> LiveLocationShareViewState.Unkwown
+ liveLocationShareSummaryData.isActive.not() || isLiveTimedOut(liveLocationShareSummaryData) -> LiveLocationShareViewState.Inactive
+ liveLocationShareSummaryData.isActive && liveLocationShareSummaryData.lastGeoUri.isNullOrEmpty() -> LiveLocationShareViewState.Loading
+ else ->
+ LiveLocationShareViewState.Running(
+ liveLocationShareSummaryData.lastGeoUri.orEmpty(),
+ getEndOfLiveDateTime(liveLocationShareSummaryData)
+ )
+ }.also { viewState -> Timber.d("computed viewState: $viewState") }
+ }
+
+ private fun isLiveTimedOut(liveLocationShareSummaryData: LiveLocationShareSummaryData): Boolean {
+ return getEndOfLiveDateTime(liveLocationShareSummaryData)
+ ?.let { endOfLive ->
+ // this will only cover users with different timezones but not users with manually time set
+ val now = LocalDateTime.now()
+ now.isAfter(endOfLive)
+ }
+ .orFalse()
+ }
+
+ private fun getEndOfLiveDateTime(liveLocationShareSummaryData: LiveLocationShareSummaryData): LocalDateTime? {
+ return liveLocationShareSummaryData.endOfLiveTimestampMillis?.let { DateProvider.toLocalDateTime(timestamp = it) }
+ }
+
+ private fun getLiveLocationShareSummaryData(event: TimelineEvent): LiveLocationShareSummaryData? {
+ return event.annotations?.liveLocationShareAggregatedSummary?.let { summary ->
+ LiveLocationShareSummaryData(
+ isActive = summary.isActive,
+ endOfLiveTimestampMillis = summary.endOfLiveTimestampMillis,
+ lastGeoUri = summary.lastLocationDataContent?.getBestLocationInfo()?.geoUri
+ )
+ }
+ }
+
+ private data class LiveLocationShareSummaryData(
+ val isActive: Boolean?,
+ val endOfLiveTimestampMillis: Long?,
+ val lastGeoUri: String?,
+ )
+
+ private sealed class LiveLocationShareViewState {
+ object Loading : LiveLocationShareViewState()
+ data class Running(val lastGeoUri: String, val endOfLiveDateTime: LocalDateTime?) : LiveLocationShareViewState()
+ object Inactive : LiveLocationShareViewState()
+ object Unkwown : LiveLocationShareViewState()
+ }
+}
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 aca2aab174..224c1cdbea 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
@@ -55,8 +55,15 @@ class MergedHeaderItemFactory @Inject constructor(private val activeSessionHolde
private val mergeItemCollapseStates = HashMap()
/**
+ * @param event the main timeline event
* @param nextEvent is an older event than event
* @param items all known items, sorted from newer event to oldest event
+ * @param partialState partial state data
+ * @param addDaySeparator true to add a day separator
+ * @param currentPosition the current position
+ * @param eventIdToHighlight if not null the event which has to be highlighted
+ * @param callback callback for user event
+ * @param requestModelBuild lambda to let the built Item request a model build when the collapse state is changed
*/
fun create(event: TimelineEvent,
nextEvent: TimelineEvent?,
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 b960e2c6a9..13f783cded 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
@@ -148,7 +148,7 @@ class MessageItemFactory @Inject constructor(
private val locationPinProvider: LocationPinProvider,
private val vectorPreferences: VectorPreferences,
private val urlMapProvider: UrlMapProvider,
- private val liveLocationMessageItemFactory: LiveLocationMessageItemFactory,
+ private val liveLocationShareMessageItemFactory: LiveLocationShareMessageItemFactory,
) {
// TODO inject this properly?
@@ -216,7 +216,7 @@ class MessageItemFactory @Inject constructor(
buildMessageTextItem(messageContent.body, false, informationData, highlight, callback, attributes)
}
}
- is MessageBeaconInfoContent -> liveLocationMessageItemFactory.create(messageContent, highlight, attributes)
+ is MessageBeaconInfoContent -> liveLocationShareMessageItemFactory.create(params.event, highlight, attributes)
else -> buildNotHandledMessageItem(messageContent, informationData, highlight, callback, attributes)
}
return messageItem?.apply {
@@ -237,14 +237,14 @@ class MessageItemFactory @Inject constructor(
urlMapProvider.buildStaticMapUrl(it, INITIAL_MAP_ZOOM_IN_TIMELINE, width, height)
}
- val userId = if (locationContent.isSelfLocation()) informationData.senderId else null
+ val locationUserId = if (locationContent.isSelfLocation()) informationData.senderId else null
return MessageLocationItem_()
.attributes(attributes)
.locationUrl(locationUrl)
.mapWidth(width)
.mapHeight(height)
- .userId(userId)
+ .locationUserId(locationUserId)
.locationPinProvider(locationPinProvider)
.highlighted(highlight)
.leftGuideline(avatarSizeProvider.leftGuideline)
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 f4bcc1ba65..07ae9d66c3 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
@@ -100,7 +100,7 @@ class TimelineItemFactory @Inject constructor(
// Message itemsX
EventType.STICKER,
in EventType.POLL_START,
- EventType.MESSAGE -> messageItemFactory.create(params)
+ EventType.MESSAGE -> messageItemFactory.create(params)
EventType.REDACTION,
EventType.KEY_VERIFICATION_ACCEPT,
EventType.KEY_VERIFICATION_START,
@@ -113,14 +113,15 @@ class TimelineItemFactory @Inject constructor(
EventType.CALL_NEGOTIATE,
EventType.REACTION,
in EventType.POLL_RESPONSE,
- in EventType.POLL_END -> noticeItemFactory.create(params)
+ in EventType.POLL_END,
+ in EventType.BEACON_LOCATION_DATA -> noticeItemFactory.create(params)
// Calls
EventType.CALL_INVITE,
EventType.CALL_HANGUP,
EventType.CALL_REJECT,
- EventType.CALL_ANSWER -> callItemFactory.create(params)
+ EventType.CALL_ANSWER -> callItemFactory.create(params)
// Crypto
- EventType.ENCRYPTED -> {
+ EventType.ENCRYPTED -> {
if (event.root.isRedacted()) {
// Redacted event, let the MessageItemFactory handle it
messageItemFactory.create(params)
@@ -129,11 +130,11 @@ class TimelineItemFactory @Inject constructor(
}
}
EventType.KEY_VERIFICATION_CANCEL,
- EventType.KEY_VERIFICATION_DONE -> {
+ EventType.KEY_VERIFICATION_DONE -> {
verificationConclusionItemFactory.create(params)
}
// Unhandled event types
- else -> {
+ else -> {
// Should only happen when shouldShowHiddenEvents() settings is ON
Timber.v("Type ${event.root.getClearType()} not handled")
defaultItemFactory.create(params)
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 7ad0cb27c6..8e06b3ee5d 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
@@ -107,7 +107,8 @@ class NoticeEventFormatter @Inject constructor(
EventType.REDACTION,
EventType.STICKER,
in EventType.POLL_RESPONSE,
- in EventType.POLL_END -> formatDebug(timelineEvent.root)
+ in EventType.POLL_END,
+ in EventType.BEACON_LOCATION_DATA -> formatDebug(timelineEvent.root)
else -> {
Timber.v("Type $type not handled by this formatter")
null
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt
index 1e95f067d2..7874f843e1 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt
@@ -44,8 +44,7 @@ import org.matrix.android.sdk.api.session.room.timeline.hasBeenEdited
import javax.inject.Inject
/**
- * This class compute if data of an event (such has avatar, display name, ...) should be displayed, depending on the previous event in the timeline.
- * TODO Update this comment
+ * This class is responsible of building extra information data associated to a given event.
*/
class MessageInformationDataFactory @Inject constructor(private val session: Session,
private val dateFormatter: VectorDateFormatter,
@@ -119,7 +118,7 @@ class MessageInformationDataFactory @Inject constructor(private val session: Ses
isFirstFromThisSender = isFirstFromThisSender,
isLastFromThisSender = isLastFromThisSender,
e2eDecoration = e2eDecoration,
- sendStateDecoration = sendStateDecoration
+ sendStateDecoration = sendStateDecoration,
)
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageItemAttributesFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageItemAttributesFactory.kt
index 45c711ff93..737b0dc85d 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageItemAttributesFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageItemAttributesFactory.kt
@@ -57,6 +57,7 @@ class MessageItemAttributesFactory @Inject constructor(
memberClickListener = {
callback?.onMemberNameClicked(informationData)
},
+ callback = callback,
reactionPillCallback = callback,
avatarCallback = callback,
threadCallback = callback,
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventVisibilityHelper.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventVisibilityHelper.kt
index f317eb4f9a..8ca999309a 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventVisibilityHelper.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventVisibilityHelper.kt
@@ -36,6 +36,8 @@ class TimelineEventVisibilityHelper @Inject constructor(private val userPreferen
* @param index the index to start computing (inclusive)
* @param minSize the minimum number of same type events to have sequentially, otherwise will return an empty list
* @param eventIdToHighlight used to compute visibility
+ * @param rootThreadEventId the root thread event id if in a thread timeline
+ * @param isFromThreadTimeline true if the timeline is a thread
*
* @return a list of timeline events which have sequentially the same type following the next direction.
*/
@@ -86,6 +88,8 @@ class TimelineEventVisibilityHelper @Inject constructor(private val userPreferen
* @param index the index to start computing (inclusive)
* @param minSize the minimum number of same type events to have sequentially, otherwise will return an empty list
* @param eventIdToHighlight used to compute visibility
+ * @param rootThreadEventId the root thread eventId
+ * @param isFromThreadTimeline true if the timeline is a thread
*
* @return a list of timeline events which have sequentially the same type following the prev direction.
*/
@@ -107,6 +111,7 @@ class TimelineEventVisibilityHelper @Inject constructor(private val userPreferen
/**
* @param timelineEvent the event to check for visibility
* @param highlightedEventId can be checked to force visibility to true
+ * @param isFromThreadTimeline true if the timeline is a thread
* @param rootThreadEventId if this param is null it means we are in the original timeline
* @return true if the event should be shown in the timeline.
*/
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt
index 263f105b6b..b9d79d5818 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt
@@ -178,6 +178,7 @@ abstract class AbsMessageItem : AbsBaseMessageItem
override val itemLongClickListener: View.OnLongClickListener? = null,
override val itemClickListener: ClickListener? = null,
val memberClickListener: ClickListener? = null,
+ val callback: TimelineEventController.Callback? = null,
override val reactionPillCallback: TimelineEventController.ReactionPillCallback? = null,
val avatarCallback: TimelineEventController.AvatarCallback? = null,
val threadCallback: TimelineEventController.ThreadCallback? = null,
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageLocationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageLocationItem.kt
new file mode 100644
index 0000000000..f7146c24e9
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageLocationItem.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2021 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.timeline.item
+
+import android.graphics.drawable.Drawable
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.annotation.IdRes
+import androidx.core.view.isVisible
+import androidx.core.view.updateLayoutParams
+import com.airbnb.epoxy.EpoxyAttribute
+import com.bumptech.glide.load.DataSource
+import com.bumptech.glide.load.engine.GlideException
+import com.bumptech.glide.load.resource.bitmap.RoundedCorners
+import com.bumptech.glide.request.RequestListener
+import com.bumptech.glide.request.RequestOptions
+import com.bumptech.glide.request.target.Target
+import im.vector.app.R
+import im.vector.app.core.glide.GlideApp
+import im.vector.app.core.utils.DimensionConverter
+import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
+import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout
+import im.vector.app.features.home.room.detail.timeline.style.granularRoundedCorners
+
+abstract class AbsMessageLocationItem : AbsMessageItem() {
+
+ @EpoxyAttribute
+ var locationUrl: String? = null
+
+ @EpoxyAttribute
+ var locationUserId: String? = null
+
+ @EpoxyAttribute
+ var mapWidth: Int = 0
+
+ @EpoxyAttribute
+ var mapHeight: Int = 0
+
+ @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
+ var locationPinProvider: LocationPinProvider? = null
+
+ override fun bind(holder: H) {
+ super.bind(holder)
+ renderSendState(holder.view, null)
+ bindMap(holder)
+ }
+
+ private fun bindMap(holder: Holder) {
+ val location = locationUrl ?: return
+ val messageLayout = attributes.informationData.messageLayout
+ val imageCornerTransformation = if (messageLayout is TimelineMessageLayout.Bubble) {
+ messageLayout.cornersRadius.granularRoundedCorners()
+ } else {
+ val dimensionConverter = DimensionConverter(holder.view.resources)
+ RoundedCorners(dimensionConverter.dpToPx(8))
+ }
+ holder.staticMapImageView.updateLayoutParams {
+ width = mapWidth
+ height = mapHeight
+ }
+ GlideApp.with(holder.staticMapImageView)
+ .load(location)
+ .apply(RequestOptions.centerCropTransform())
+ .listener(object : RequestListener {
+ override fun onLoadFailed(e: GlideException?,
+ model: Any?,
+ target: Target?,
+ isFirstResource: Boolean): Boolean {
+ holder.staticMapPinImageView.setImageResource(R.drawable.ic_location_pin_failed)
+ holder.staticMapErrorTextView.isVisible = true
+ return false
+ }
+
+ override fun onResourceReady(resource: Drawable?,
+ model: Any?,
+ target: Target?,
+ dataSource: DataSource?,
+ isFirstResource: Boolean): Boolean {
+ locationPinProvider?.create(locationUserId) { pinDrawable ->
+ // we are not using Glide since it does not display it correctly when there is no user photo
+ holder.staticMapPinImageView.setImageDrawable(pinDrawable)
+ }
+ holder.staticMapErrorTextView.isVisible = false
+ return false
+ }
+ })
+ .transform(imageCornerTransformation)
+ .into(holder.staticMapImageView)
+ }
+
+ abstract class Holder(@IdRes stubId: Int) : AbsMessageItem.Holder(stubId) {
+ val staticMapImageView by bind(R.id.staticMapImageView)
+ val staticMapPinImageView by bind(R.id.staticMapPinImageView)
+ val staticMapErrorTextView by bind(R.id.staticMapErrorTextView)
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/DefaultLiveLocationShareStatusItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/DefaultLiveLocationShareStatusItem.kt
new file mode 100644
index 0000000000..c421efda12
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/DefaultLiveLocationShareStatusItem.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.home.room.detail.timeline.item
+
+import android.content.res.Resources
+import android.graphics.drawable.ColorDrawable
+import android.widget.ImageView
+import androidx.core.view.updateLayoutParams
+import com.bumptech.glide.load.resource.bitmap.GranularRoundedCorners
+import com.bumptech.glide.load.resource.bitmap.RoundedCorners
+import im.vector.app.R
+import im.vector.app.core.glide.GlideApp
+import im.vector.app.core.utils.DimensionConverter
+import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout
+import im.vector.app.features.home.room.detail.timeline.style.granularRoundedCorners
+import im.vector.app.features.themes.ThemeUtils
+
+/**
+ * Default implementation of common methods for item representing the status of a live location share.
+ */
+class DefaultLiveLocationShareStatusItem : LiveLocationShareStatusItem {
+
+ override fun bindMap(
+ mapImageView: ImageView,
+ mapWidth: Int,
+ mapHeight: Int,
+ messageLayout: TimelineMessageLayout
+ ) {
+ val mapCornerTransformation = if (messageLayout is TimelineMessageLayout.Bubble) {
+ messageLayout.cornersRadius.granularRoundedCorners()
+ } else {
+ RoundedCorners(getDefaultLayoutCornerRadiusInDp(mapImageView.resources))
+ }
+ mapImageView.updateLayoutParams {
+ width = mapWidth
+ height = mapHeight
+ }
+ GlideApp.with(mapImageView)
+ .load(R.drawable.bg_no_location_map)
+ .transform(mapCornerTransformation)
+ .into(mapImageView)
+ }
+
+ override fun bindBottomBanner(bannerImageView: ImageView, messageLayout: TimelineMessageLayout) {
+ val imageCornerTransformation = if (messageLayout is TimelineMessageLayout.Bubble) {
+ GranularRoundedCorners(
+ 0f,
+ 0f,
+ messageLayout.cornersRadius.bottomEndRadius,
+ messageLayout.cornersRadius.bottomStartRadius
+ )
+ } else {
+ val bottomCornerRadius = getDefaultLayoutCornerRadiusInDp(bannerImageView.resources).toFloat()
+ GranularRoundedCorners(0f, 0f, bottomCornerRadius, bottomCornerRadius)
+ }
+ GlideApp.with(bannerImageView)
+ .load(ColorDrawable(ThemeUtils.getColor(bannerImageView.context, android.R.attr.colorBackground)))
+ .transform(imageCornerTransformation)
+ .into(bannerImageView)
+ }
+
+ private fun getDefaultLayoutCornerRadiusInDp(resources: Resources): Int {
+ val dimensionConverter = DimensionConverter(resources)
+ return dimensionConverter.dpToPx(8)
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/LiveLocationShareStatusItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/LiveLocationShareStatusItem.kt
new file mode 100644
index 0000000000..2f79f2fc9e
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/LiveLocationShareStatusItem.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.home.room.detail.timeline.item
+
+import android.widget.ImageView
+import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout
+
+interface LiveLocationShareStatusItem {
+ fun bindMap(
+ mapImageView: ImageView,
+ mapWidth: Int,
+ mapHeight: Int,
+ messageLayout: TimelineMessageLayout
+ )
+
+ fun bindBottomBanner(bannerImageView: ImageView, messageLayout: TimelineMessageLayout)
+}
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 f3ca525136..f41c17d9e7 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
@@ -135,7 +135,7 @@ abstract class MergedRoomCreationItem : BasedMergedItem(),
+ LiveLocationShareStatusItem by DefaultLiveLocationShareStatusItem() {
+
+ @EpoxyAttribute
+ var mapWidth: Int = 0
+
+ @EpoxyAttribute
+ var mapHeight: Int = 0
+
+ override fun bind(holder: Holder) {
+ super.bind(holder)
+ renderSendState(holder.view, null)
+ bindMap(holder.noLocationMapImageView, mapWidth, mapHeight, attributes.informationData.messageLayout)
+ bindBottomBanner(holder.bannerImageView, attributes.informationData.messageLayout)
+ }
+
+ override fun getViewStubId() = STUB_ID
+
+ class Holder : AbsMessageItem.Holder(STUB_ID) {
+ val bannerImageView by bind(R.id.locationLiveInactiveBanner)
+ val noLocationMapImageView by bind(R.id.locationLiveInactiveMap)
+ }
+
+ companion object {
+ private const val STUB_ID = R.id.messageContentLiveLocationInactiveStub
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt
new file mode 100644
index 0000000000..838fbd46de
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.home.room.detail.timeline.item
+
+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.date.DateFormatKind
+import im.vector.app.core.date.VectorDateFormatter
+import im.vector.app.core.resources.toTimestamp
+import im.vector.app.core.utils.DimensionConverter
+import im.vector.app.features.home.room.detail.RoomDetailAction
+import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout
+import im.vector.app.features.location.live.LocationLiveMessageBannerView
+import im.vector.app.features.location.live.LocationLiveMessageBannerViewState
+import org.threeten.bp.LocalDateTime
+
+@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
+abstract class MessageLiveLocationItem : AbsMessageLocationItem() {
+
+ @EpoxyAttribute
+ var currentUserId: String? = null
+
+ @EpoxyAttribute
+ var endOfLiveDateTime: LocalDateTime? = null
+
+ @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
+ lateinit var vectorDateFormatter: VectorDateFormatter
+
+ override fun bind(holder: Holder) {
+ super.bind(holder)
+ bindLocationLiveBanner(holder)
+ }
+
+ private fun bindLocationLiveBanner(holder: Holder) {
+ // TODO in a future PR add check on device id to confirm that is the one that sent the beacon
+ val isEmitter = currentUserId != null && currentUserId == locationUserId
+ val messageLayout = attributes.informationData.messageLayout
+ val viewState = buildViewState(holder, messageLayout, isEmitter)
+ holder.locationLiveMessageBanner.isVisible = true
+ holder.locationLiveMessageBanner.render(viewState)
+ holder.locationLiveMessageBanner.stopButton.setOnClickListener {
+ attributes.callback?.onTimelineItemAction(RoomDetailAction.StopLiveLocationSharing)
+ }
+ }
+
+ private fun buildViewState(
+ holder: Holder,
+ messageLayout: TimelineMessageLayout,
+ isEmitter: Boolean
+ ): LocationLiveMessageBannerViewState {
+ return when {
+ messageLayout is TimelineMessageLayout.Bubble && isEmitter ->
+ LocationLiveMessageBannerViewState.Emitter(
+ remainingTimeInMillis = getRemainingTimeOfLiveInMillis(),
+ bottomStartCornerRadiusInDp = messageLayout.cornersRadius.bottomStartRadius,
+ bottomEndCornerRadiusInDp = messageLayout.cornersRadius.bottomEndRadius,
+ isStopButtonCenteredVertically = false
+ )
+ messageLayout is TimelineMessageLayout.Bubble ->
+ LocationLiveMessageBannerViewState.Watcher(
+ bottomStartCornerRadiusInDp = messageLayout.cornersRadius.bottomStartRadius,
+ bottomEndCornerRadiusInDp = messageLayout.cornersRadius.bottomEndRadius,
+ formattedLocalTimeOfEndOfLive = getFormattedLocalTimeEndOfLive(),
+ )
+ isEmitter -> {
+ val cornerRadius = getBannerCornerRadiusForDefaultLayout(holder)
+ LocationLiveMessageBannerViewState.Emitter(
+ remainingTimeInMillis = getRemainingTimeOfLiveInMillis(),
+ bottomStartCornerRadiusInDp = cornerRadius,
+ bottomEndCornerRadiusInDp = cornerRadius,
+ isStopButtonCenteredVertically = true
+ )
+ }
+ else -> {
+ val cornerRadius = getBannerCornerRadiusForDefaultLayout(holder)
+ LocationLiveMessageBannerViewState.Watcher(
+ bottomStartCornerRadiusInDp = cornerRadius,
+ bottomEndCornerRadiusInDp = cornerRadius,
+ formattedLocalTimeOfEndOfLive = getFormattedLocalTimeEndOfLive(),
+ )
+ }
+ }
+ }
+
+ private fun getBannerCornerRadiusForDefaultLayout(holder: Holder): Float {
+ val dimensionConverter = DimensionConverter(holder.view.resources)
+ return dimensionConverter.dpToPx(8).toFloat()
+ }
+
+ private fun getFormattedLocalTimeEndOfLive() =
+ endOfLiveDateTime?.toTimestamp()?.let { vectorDateFormatter.format(it, DateFormatKind.MESSAGE_SIMPLE) }.orEmpty()
+
+ private fun getRemainingTimeOfLiveInMillis() =
+ (endOfLiveDateTime?.toTimestamp() ?: 0) - LocalDateTime.now().toTimestamp()
+
+ override fun getViewStubId() = STUB_ID
+
+ class Holder : AbsMessageLocationItem.Holder(STUB_ID) {
+ val locationLiveMessageBanner by bind(R.id.locationLiveMessageBanner)
+ }
+
+ companion object {
+ private const val STUB_ID = R.id.messageContentLiveLocationStub
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationStartItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationStartItem.kt
index 390db0ef50..001774b579 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationStartItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationStartItem.kt
@@ -16,22 +16,15 @@
package im.vector.app.features.home.room.detail.timeline.item
-import android.graphics.drawable.ColorDrawable
import android.widget.ImageView
-import androidx.core.view.updateLayoutParams
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
-import com.bumptech.glide.load.resource.bitmap.GranularRoundedCorners
-import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import im.vector.app.R
-import im.vector.app.core.glide.GlideApp
-import im.vector.app.core.utils.DimensionConverter
-import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout
-import im.vector.app.features.home.room.detail.timeline.style.granularRoundedCorners
-import im.vector.app.features.themes.ThemeUtils
@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
-abstract class MessageLiveLocationStartItem : AbsMessageItem() {
+abstract class MessageLiveLocationStartItem :
+ AbsMessageItem(),
+ LiveLocationShareStatusItem by DefaultLiveLocationShareStatusItem() {
@EpoxyAttribute
var mapWidth: Int = 0
@@ -42,44 +35,8 @@ abstract class MessageLiveLocationStartItem : AbsMessageItem() {
-
- @EpoxyAttribute
- var locationUrl: String? = null
-
- @EpoxyAttribute
- var userId: String? = null
-
- @EpoxyAttribute
- var mapWidth: Int = 0
-
- @EpoxyAttribute
- var mapHeight: Int = 0
-
- @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
- var locationPinProvider: LocationPinProvider? = null
-
- override fun bind(holder: Holder) {
- super.bind(holder)
- renderSendState(holder.view, null)
- val location = locationUrl ?: return
- val messageLayout = attributes.informationData.messageLayout
- val dimensionConverter = DimensionConverter(holder.view.resources)
- val imageCornerTransformation = if (messageLayout is TimelineMessageLayout.Bubble) {
- messageLayout.cornersRadius.granularRoundedCorners()
- } else {
- RoundedCorners(dimensionConverter.dpToPx(8))
- }
- holder.staticMapImageView.updateLayoutParams {
- width = mapWidth
- height = mapHeight
- }
- GlideApp.with(holder.staticMapImageView)
- .load(location)
- .apply(RequestOptions.centerCropTransform())
- .listener(object : RequestListener {
- override fun onLoadFailed(e: GlideException?,
- model: Any?,
- target: Target?,
- isFirstResource: Boolean): Boolean {
- holder.staticMapPinImageView.setImageResource(R.drawable.ic_location_pin_failed)
- holder.staticMapErrorTextView.isVisible = true
- return false
- }
-
- override fun onResourceReady(resource: Drawable?,
- model: Any?,
- target: Target?,
- dataSource: DataSource?,
- isFirstResource: Boolean): Boolean {
- locationPinProvider?.create(userId) { pinDrawable ->
- GlideApp.with(holder.staticMapPinImageView)
- .load(pinDrawable)
- .into(holder.staticMapPinImageView)
- }
- holder.staticMapErrorTextView.isVisible = false
- return false
- }
- })
- .transform(imageCornerTransformation)
- .into(holder.staticMapImageView)
- }
+abstract class MessageLocationItem : AbsMessageLocationItem() {
override fun getViewStubId() = STUB_ID
- class Holder : AbsMessageItem.Holder(STUB_ID) {
- val staticMapImageView by bind(R.id.staticMapImageView)
- val staticMapPinImageView by bind(R.id.staticMapPinImageView)
- val staticMapErrorTextView by bind(R.id.staticMapErrorTextView)
- }
+ class Holder : AbsMessageLocationItem.Holder(STUB_ID)
companion object {
private const val STUB_ID = R.id.messageContentLocationStub
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/style/TimelineMessageLayoutFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/style/TimelineMessageLayoutFactory.kt
index cb0b2384ec..a0d10a8a75 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/style/TimelineMessageLayoutFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/style/TimelineMessageLayoutFactory.kt
@@ -66,6 +66,11 @@ class TimelineMessageLayoutFactory @Inject constructor(private val session: Sess
MessageType.MSGTYPE_VIDEO,
MessageType.MSGTYPE_BEACON_INFO,
)
+
+ private val MSG_TYPES_WITH_LOCATION_DATA = setOf(
+ MessageType.MSGTYPE_LOCATION,
+ MessageType.MSGTYPE_BEACON_LOCATION_DATA
+ )
}
private val cornerRadius: Float by lazy {
@@ -145,9 +150,11 @@ class TimelineMessageLayoutFactory @Inject constructor(private val session: Sess
}
private fun MessageContent?.timestampInsideMessage(): Boolean {
- if (this == null) return false
- if (msgType == MessageType.MSGTYPE_LOCATION) return vectorPreferences.labsRenderLocationsInTimeline()
- return this.msgType in MSG_TYPES_WITH_TIMESTAMP_INSIDE_MESSAGE
+ return when {
+ this == null -> false
+ msgType in MSG_TYPES_WITH_LOCATION_DATA -> vectorPreferences.labsRenderLocationsInTimeline()
+ else -> msgType in MSG_TYPES_WITH_TIMESTAMP_INSIDE_MESSAGE
+ }
}
private fun MessageContent?.shouldAddMessageOverlay(): Boolean {
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/url/PreviewUrlView.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/url/PreviewUrlView.kt
index 3ae6a1fea5..69455c767e 100755
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/url/PreviewUrlView.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/url/PreviewUrlView.kt
@@ -59,6 +59,8 @@ class PreviewUrlView @JvmOverloads constructor(
* This methods is responsible for rendering the view according to the newState.
*
* @param newState the newState representing the view
+ * @param imageContentRenderer the tool to render the image
+ * @param force true to force refresh
*/
fun render(newState: PreviewUrlUiState,
imageContentRenderer: ImageContentRenderer,
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/SpaceDirectoryFilterNoResults.kt b/vector/src/main/java/im/vector/app/features/home/room/list/SpaceDirectoryFilterNoResults.kt
new file mode 100644
index 0000000000..1ae017c98c
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/SpaceDirectoryFilterNoResults.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.home.room.list
+
+import com.airbnb.epoxy.EpoxyModelClass
+import im.vector.app.R
+import im.vector.app.core.epoxy.VectorEpoxyHolder
+import im.vector.app.core.epoxy.VectorEpoxyModel
+
+@EpoxyModelClass(layout = R.layout.item_space_directory_filter_no_results)
+abstract class SpaceDirectoryFilterNoResults : VectorEpoxyModel() {
+ class Holder : VectorEpoxyHolder()
+}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/threads/arguments/ThreadTimelineArgs.kt b/vector/src/main/java/im/vector/app/features/home/room/threads/arguments/ThreadTimelineArgs.kt
index 19419e52de..7756c3c5a5 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/threads/arguments/ThreadTimelineArgs.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/threads/arguments/ThreadTimelineArgs.kt
@@ -27,5 +27,6 @@ data class ThreadTimelineArgs(
val avatarUrl: String?,
val roomEncryptionTrustLevel: RoomEncryptionTrustLevel?,
val rootThreadEventId: String? = null,
- val startsThread: Boolean = false
+ val startsThread: Boolean = false,
+ val showKeyboard: Boolean = false
) : Parcelable
diff --git a/vector/src/main/java/im/vector/app/features/home/room/threads/list/views/ThreadListFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/threads/list/views/ThreadListFragment.kt
index 04889f375f..8e762fda96 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/threads/list/views/ThreadListFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/threads/list/views/ThreadListFragment.kt
@@ -19,6 +19,7 @@ package im.vector.app.features.home.room.threads.list.views
import android.os.Bundle
import android.view.LayoutInflater
import android.view.Menu
+import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
@@ -70,6 +71,16 @@ class ThreadListFragment @Inject constructor(
analyticsScreenName = MobileScreen.ScreenName.ThreadList
}
+ override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
+ super.onCreateOptionsMenu(menu, inflater)
+
+ menu.findItem(R.id.menu_thread_list_filter)?.let { menuItem ->
+ menuItem.actionView.setOnClickListener {
+ onOptionsItemSelected(menuItem)
+ }
+ }
+ }
+
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.menu_thread_list_filter -> {
@@ -82,6 +93,9 @@ class ThreadListFragment @Inject constructor(
override fun onPrepareOptionsMenu(menu: Menu) {
withState(threadListViewModel) { state ->
+ val filterIcon = menu.findItem(R.id.menu_thread_list_filter).actionView
+ val filterBadge = filterIcon.findViewById(R.id.threadListFilterBadge)
+ filterBadge.isVisible = state.shouldFilterThreads
when (threadListViewModel.canHomeserverUseThreading()) {
true -> menu.findItem(R.id.menu_thread_list_filter).isVisible = !state.threadSummaryList.invoke().isNullOrEmpty()
false -> menu.findItem(R.id.menu_thread_list_filter).isVisible = !state.rootThreadEventList.invoke().isNullOrEmpty()
@@ -146,9 +160,9 @@ class ThreadListFragment @Inject constructor(
override fun onThreadSummaryClicked(threadSummary: ThreadSummary) {
val roomThreadDetailArgs = ThreadTimelineArgs(
- roomId = threadSummary.roomId,
- displayName = threadSummary.rootThreadSenderInfo.displayName,
- avatarUrl = threadSummary.rootThreadSenderInfo.avatarUrl,
+ roomId = threadListArgs.roomId,
+ displayName = threadListArgs.displayName,
+ avatarUrl = threadListArgs.avatarUrl,
roomEncryptionTrustLevel = null,
rootThreadEventId = threadSummary.rootEventId
)
@@ -157,9 +171,9 @@ class ThreadListFragment @Inject constructor(
override fun onThreadListClicked(timelineEvent: TimelineEvent) {
val threadTimelineArgs = ThreadTimelineArgs(
- roomId = timelineEvent.roomId,
- displayName = timelineEvent.senderInfo.displayName,
- avatarUrl = timelineEvent.senderInfo.avatarUrl,
+ roomId = threadListArgs.roomId,
+ displayName = threadListArgs.displayName,
+ avatarUrl = threadListArgs.avatarUrl,
roomEncryptionTrustLevel = null,
rootThreadEventId = timelineEvent.eventId
)
diff --git a/vector/src/main/java/im/vector/app/features/lifecycle/VectorActivityLifecycleCallbacks.kt b/vector/src/main/java/im/vector/app/features/lifecycle/VectorActivityLifecycleCallbacks.kt
index 386e60359d..e453a347f5 100644
--- a/vector/src/main/java/im/vector/app/features/lifecycle/VectorActivityLifecycleCallbacks.kt
+++ b/vector/src/main/java/im/vector/app/features/lifecycle/VectorActivityLifecycleCallbacks.kt
@@ -136,7 +136,7 @@ class VectorActivityLifecycleCallbacks constructor(private val popupAlertManager
* Detect potential malicious activity.
* Check if the activity running in app task is declared in app manifest.
*
- * @param activity the activity of the task
+ * @param activity the activity of the task
* @return true if the activity is potentially malicious
*/
private fun isPotentialMaliciousActivity(activity: ComponentName): Boolean = activitiesInfo.none { it.name == activity.className }
diff --git a/vector/src/main/java/im/vector/app/features/location/Config.kt b/vector/src/main/java/im/vector/app/features/location/Config.kt
index 6f947290e2..c29e2e911a 100644
--- a/vector/src/main/java/im/vector/app/features/location/Config.kt
+++ b/vector/src/main/java/im/vector/app/features/location/Config.kt
@@ -22,5 +22,5 @@ const val DEFAULT_PIN_ID = "DEFAULT_PIN_ID"
const val INITIAL_MAP_ZOOM_IN_PREVIEW = 15.0
const val INITIAL_MAP_ZOOM_IN_TIMELINE = 17.0
-const val MIN_TIME_TO_UPDATE_LOCATION_MILLIS = 5 * 1_000L // every 5 seconds
+const val MIN_TIME_TO_UPDATE_LOCATION_MILLIS = 2 * 1_000L // every 2 seconds
const val MIN_DISTANCE_TO_UPDATE_LOCATION_METERS = 10f
diff --git a/vector/src/main/java/im/vector/app/features/location/LocationData.kt b/vector/src/main/java/im/vector/app/features/location/LocationData.kt
index 061f338e72..b3466ff871 100644
--- a/vector/src/main/java/im/vector/app/features/location/LocationData.kt
+++ b/vector/src/main/java/im/vector/app/features/location/LocationData.kt
@@ -29,7 +29,7 @@ data class LocationData(
) : Parcelable
/**
- * Creates location data from a LocationContent.
+ * Creates location data from a MessageLocationContent.
* "geo:40.05,29.24;30" -> LocationData(40.05, 29.24, 30)
* @return location data or null if geo uri is not valid
*/
@@ -37,6 +37,15 @@ fun MessageLocationContent.toLocationData(): LocationData? {
return parseGeo(getBestGeoUri())
}
+/**
+ * Creates location data from a geoUri String.
+ * "geo:40.05,29.24;30" -> LocationData(40.05, 29.24, 30)
+ * @return location data or null if geo uri is null or not valid
+ */
+fun String?.toLocationData(): LocationData? {
+ return this?.let { parseGeo(it) }
+}
+
@VisibleForTesting
fun parseGeo(geo: String): LocationData? {
val geoParts = geo
diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt
index e472c568b6..cc5586e7f5 100644
--- a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt
@@ -37,11 +37,11 @@ import im.vector.app.core.utils.PERMISSIONS_FOR_FOREGROUND_LOCATION_SHARING
import im.vector.app.core.utils.checkPermissions
import im.vector.app.core.utils.registerForPermissionsResult
import im.vector.app.databinding.FragmentLocationSharingBinding
-import im.vector.app.features.VectorFeatures
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider
import im.vector.app.features.location.live.duration.ChooseLiveDurationBottomSheet
import im.vector.app.features.location.option.LocationSharingOption
+import im.vector.app.features.settings.VectorPreferences
import org.matrix.android.sdk.api.util.MatrixItem
import java.lang.ref.WeakReference
import javax.inject.Inject
@@ -53,7 +53,7 @@ class LocationSharingFragment @Inject constructor(
private val urlMapProvider: UrlMapProvider,
private val avatarRenderer: AvatarRenderer,
private val matrixItemColorProvider: MatrixItemColorProvider,
- private val vectorFeatures: VectorFeatures,
+ private val vectorPreferences: VectorPreferences,
) : VectorBaseFragment(),
LocationTargetChangeListener,
VectorBaseBottomSheetDialogFragment.ResultListener {
@@ -255,7 +255,7 @@ class LocationSharingFragment @Inject constructor(
// first, update the options view
val options: Set = when (state.areTargetAndUserLocationEqual) {
true -> {
- if (vectorFeatures.isLiveLocationEnabled()) {
+ if (vectorPreferences.labsEnableLiveLocation()) {
setOf(LocationSharingOption.USER_CURRENT, LocationSharingOption.USER_LIVE)
} else {
setOf(LocationSharingOption.USER_CURRENT)
diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt
index 362b82ccf5..8b9a1c75ae 100644
--- a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt
+++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt
@@ -55,7 +55,10 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
private val binder = LocalBinder()
- private var roomArgsList = mutableListOf()
+ /**
+ * Keep track of a map between beacon event Id starting the live and RoomArgs.
+ */
+ private var roomArgsMap = mutableMapOf()
private var timers = mutableListOf()
override fun onCreate() {
@@ -73,8 +76,6 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
Timber.i("### LocationSharingService.onStartCommand. sessionId - roomId ${roomArgs?.sessionId} - ${roomArgs?.roomId}")
if (roomArgs != null) {
- roomArgsList.add(roomArgs)
-
// Show a sticky notification
val notification = notificationUtils.buildLiveLocationSharingNotification()
startForeground(roomArgs.roomId.hashCode(), notification)
@@ -87,7 +88,7 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
.getSafeActiveSession()
?.let { session ->
session.coroutineScope.launch(session.coroutineDispatchers.io) {
- sendLiveBeaconInfo(session, roomArgs)
+ sendStartingLiveBeaconInfo(session, roomArgs)
}
}
}
@@ -95,7 +96,7 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
return START_STICKY
}
- private suspend fun sendLiveBeaconInfo(session: Session, roomArgs: RoomArgs) {
+ private suspend fun sendStartingLiveBeaconInfo(session: Session, roomArgs: RoomArgs) {
val beaconContent = MessageBeaconInfoContent(
timeout = roomArgs.durationMillis,
isLive = true,
@@ -103,7 +104,7 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
).toContent()
val stateKey = session.myUserId
- session
+ val beaconEventId = session
.getRoom(roomArgs.roomId)
?.stateService()
?.sendStateEvent(
@@ -111,6 +112,16 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
stateKey = stateKey,
body = beaconContent
)
+
+ beaconEventId
+ ?.takeUnless { it.isEmpty() }
+ ?.let {
+ roomArgsMap[it] = roomArgs
+ locationTracker.requestLastKnownLocation()
+ }
+ ?: run {
+ Timber.w("### LocationSharingService.sendStartingLiveBeaconInfo error, no received beacon info id")
+ }
}
private fun scheduleTimer(roomId: String, durationMillis: Long) {
@@ -134,9 +145,13 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
// Send a new beacon info state by setting live field as false
sendStoppedBeaconInfo(roomId)
- synchronized(roomArgsList) {
- roomArgsList.removeAll { it.roomId == roomId }
- if (roomArgsList.isEmpty()) {
+ synchronized(roomArgsMap) {
+ val beaconIds = roomArgsMap
+ .filter { it.value.roomId == roomId }
+ .map { it.key }
+ beaconIds.forEach { roomArgsMap.remove(it) }
+
+ if (roomArgsMap.isEmpty()) {
Timber.i("### LocationSharingService. Destroying self, time is up for all rooms")
destroyMe()
}
@@ -156,16 +171,17 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
override fun onLocationUpdate(locationData: LocationData) {
Timber.i("### LocationSharingService.onLocationUpdate. Uncertainty: ${locationData.uncertainty}")
- val session = activeSessionHolder.getSafeActiveSession()
// Emit location update to all rooms in which live location sharing is active
- session?.coroutineScope?.launch(session.coroutineDispatchers.io) {
- roomArgsList.toList().forEach { roomArg ->
- sendLiveLocation(roomArg.roomId, locationData)
- }
+ roomArgsMap.toMap().forEach { item ->
+ sendLiveLocation(item.value.roomId, item.key, locationData)
}
}
- private suspend fun sendLiveLocation(roomId: String, locationData: LocationData) {
+ private fun sendLiveLocation(
+ roomId: String,
+ beaconInfoEventId: String,
+ locationData: LocationData
+ ) {
val session = activeSessionHolder.getSafeActiveSession()
val room = session?.getRoom(roomId)
val userId = session?.myUserId
@@ -174,18 +190,12 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
return
}
- room
- .stateService()
- .getLiveLocationBeaconInfo(userId, true)
- ?.eventId
- ?.let {
- room.sendService().sendLiveLocation(
- beaconInfoEventId = it,
- latitude = locationData.latitude,
- longitude = locationData.longitude,
- uncertainty = locationData.uncertainty
- )
- }
+ room.sendService().sendLiveLocation(
+ beaconInfoEventId = beaconInfoEventId,
+ latitude = locationData.latitude,
+ longitude = locationData.longitude,
+ uncertainty = locationData.uncertainty
+ )
}
override fun onLocationProviderIsNotAvailable() {
diff --git a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt
index b7006370a6..4e56e7954c 100644
--- a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt
+++ b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt
@@ -40,10 +40,12 @@ class LocationTracker @Inject constructor(
fun onLocationProviderIsNotAvailable()
}
- private var callbacks = mutableListOf()
+ private val callbacks = mutableListOf()
private var hasGpsProviderLiveLocation = false
+ private var lastLocation: LocationData? = null
+
@RequiresPermission(anyOf = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION])
fun start() {
Timber.d("## LocationTracker. start()")
@@ -92,6 +94,14 @@ class LocationTracker @Inject constructor(
callbacks.clear()
}
+ /**
+ * Request the last known location. It will be given async through Callback.
+ * Please ensure adding a callback to receive the value.
+ */
+ fun requestLastKnownLocation() {
+ lastLocation?.let { location -> callbacks.forEach { it.onLocationUpdate(location) } }
+ }
+
fun addCallback(callback: Callback) {
if (!callbacks.contains(callback)) {
callbacks.add(callback)
@@ -127,7 +137,9 @@ class LocationTracker @Inject constructor(
}
}
}
- callbacks.forEach { it.onLocationUpdate(location.toLocationData()) }
+ val locationData = location.toLocationData()
+ lastLocation = locationData
+ callbacks.forEach { it.onLocationUpdate(locationData) }
}
override fun onProviderDisabled(provider: String) {
diff --git a/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt b/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
new file mode 100644
index 0000000000..8cb552e3c4
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.location.live
+
+import android.content.Context
+import android.graphics.drawable.ColorDrawable
+import android.os.CountDownTimer
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.widget.Button
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.core.view.isVisible
+import com.bumptech.glide.load.resource.bitmap.GranularRoundedCorners
+import im.vector.app.R
+import im.vector.app.core.glide.GlideApp
+import im.vector.app.core.utils.TextUtils
+import im.vector.app.databinding.ViewLocationLiveMessageBannerBinding
+import im.vector.app.features.themes.ThemeUtils
+import org.threeten.bp.Duration
+
+private const val REMAINING_TIME_COUNTER_INTERVAL_IN_MS = 1000L
+
+class LocationLiveMessageBannerView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : ConstraintLayout(context, attrs, defStyleAttr) {
+
+ private val binding = ViewLocationLiveMessageBannerBinding.inflate(
+ LayoutInflater.from(context),
+ this
+ )
+
+ val stopButton: Button
+ get() = binding.locationLiveMessageBannerStop
+
+ private val background: ImageView
+ get() = binding.locationLiveMessageBannerBackground
+
+ private val title: TextView
+ get() = binding.locationLiveMessageBannerTitle
+
+ private val subTitle: TextView
+ get() = binding.locationLiveMessageBannerSubTitle
+
+ private var countDownTimer: CountDownTimer? = null
+
+ fun render(viewState: LocationLiveMessageBannerViewState) {
+ when (viewState) {
+ is LocationLiveMessageBannerViewState.Emitter -> renderEmitter(viewState)
+ is LocationLiveMessageBannerViewState.Watcher -> renderWatcher(viewState)
+ }
+
+ GlideApp.with(context)
+ .load(ColorDrawable(ThemeUtils.getColor(context, android.R.attr.colorBackground)))
+ .transform(GranularRoundedCorners(0f, 0f, viewState.bottomEndCornerRadiusInDp, viewState.bottomStartCornerRadiusInDp))
+ .into(background)
+ }
+
+ private fun renderEmitter(viewState: LocationLiveMessageBannerViewState.Emitter) {
+ stopButton.isVisible = true
+ title.text = context.getString(R.string.location_share_live_enabled)
+
+ countDownTimer?.cancel()
+ viewState.remainingTimeInMillis
+ .takeIf { it >= 0 }
+ ?.let {
+ countDownTimer = object : CountDownTimer(it, REMAINING_TIME_COUNTER_INTERVAL_IN_MS) {
+ override fun onTick(millisUntilFinished: Long) {
+ val duration = Duration.ofMillis(millisUntilFinished.coerceAtLeast(0L))
+ subTitle.text = context.getString(
+ R.string.location_share_live_remaining_time,
+ TextUtils.formatDurationWithUnits(context, duration)
+ )
+ }
+
+ override fun onFinish() {
+ subTitle.text = context.getString(
+ R.string.location_share_live_remaining_time,
+ TextUtils.formatDurationWithUnits(context, Duration.ofMillis(0L))
+ )
+ }
+ }
+ countDownTimer?.start()
+ }
+
+ val rootLayout: ConstraintLayout? = (binding.root as? ConstraintLayout)
+ rootLayout?.let { parentLayout ->
+ val constraintSet = ConstraintSet()
+ constraintSet.clone(rootLayout)
+
+ if (viewState.isStopButtonCenteredVertically) {
+ constraintSet.connect(
+ R.id.locationLiveMessageBannerStop,
+ ConstraintSet.BOTTOM,
+ R.id.locationLiveMessageBannerBackground,
+ ConstraintSet.BOTTOM,
+ 0
+ )
+ } else {
+ constraintSet.clear(R.id.locationLiveMessageBannerStop, ConstraintSet.BOTTOM)
+ }
+
+ constraintSet.applyTo(parentLayout)
+ }
+ }
+
+ private fun renderWatcher(viewState: LocationLiveMessageBannerViewState.Watcher) {
+ stopButton.isVisible = false
+ title.text = context.getString(R.string.location_share_live_view)
+ subTitle.text = context.getString(R.string.location_share_live_until, viewState.formattedLocalTimeOfEndOfLive)
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerViewState.kt b/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerViewState.kt
new file mode 100644
index 0000000000..976085386b
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerViewState.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.location.live
+
+sealed class LocationLiveMessageBannerViewState(
+ open val bottomStartCornerRadiusInDp: Float,
+ open val bottomEndCornerRadiusInDp: Float,
+) {
+
+ data class Emitter(
+ override val bottomStartCornerRadiusInDp: Float,
+ override val bottomEndCornerRadiusInDp: Float,
+ val remainingTimeInMillis: Long,
+ val isStopButtonCenteredVertically: Boolean
+ ) : LocationLiveMessageBannerViewState(bottomStartCornerRadiusInDp, bottomEndCornerRadiusInDp)
+
+ data class Watcher(
+ override val bottomStartCornerRadiusInDp: Float,
+ override val bottomEndCornerRadiusInDp: Float,
+ val formattedLocalTimeOfEndOfLive: String,
+ ) : LocationLiveMessageBannerViewState(bottomStartCornerRadiusInDp, bottomEndCornerRadiusInDp)
+}
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 c9cb4612a8..1b08d2a86f 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
@@ -36,7 +36,7 @@ import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.ensureTrailingSlash
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
-import org.matrix.android.sdk.api.MatrixPatterns.getDomain
+import org.matrix.android.sdk.api.MatrixPatterns.getServerName
import org.matrix.android.sdk.api.auth.AuthenticationService
import org.matrix.android.sdk.api.auth.HomeServerHistoryService
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
@@ -607,7 +607,7 @@ class LoginViewModel @AssistedInject constructor(
identityServerUri = wellKnownPrompt.identityServerUrl?.let { Uri.parse(it) }
)
?: HomeServerConnectionConfig(
- homeServerUri = Uri.parse("https://${action.username.getDomain()}"),
+ homeServerUri = Uri.parse("https://${action.username.getServerName()}"),
homeServerUriBase = Uri.parse(wellKnownPrompt.homeServerUrl),
identityServerUri = wellKnownPrompt.identityServerUrl?.let { Uri.parse(it) }
)
diff --git a/vector/src/main/java/im/vector/app/features/login2/LoginViewModel2.kt b/vector/src/main/java/im/vector/app/features/login2/LoginViewModel2.kt
index e72e3a1790..6b9d255937 100644
--- a/vector/src/main/java/im/vector/app/features/login2/LoginViewModel2.kt
+++ b/vector/src/main/java/im/vector/app/features/login2/LoginViewModel2.kt
@@ -38,7 +38,7 @@ import im.vector.app.features.login.LoginMode
import im.vector.app.features.login.ReAuthHelper
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
-import org.matrix.android.sdk.api.MatrixPatterns.getDomain
+import org.matrix.android.sdk.api.MatrixPatterns.getServerName
import org.matrix.android.sdk.api.auth.AuthenticationService
import org.matrix.android.sdk.api.auth.HomeServerHistoryService
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
@@ -640,7 +640,7 @@ class LoginViewModel2 @AssistedInject constructor(
}
viewEvent?.let { _viewEvents.post(it) }
- val urlFromUser = action.username.getDomain()
+ val urlFromUser = action.username.getServerName()
setState {
copy(
isLoading = false,
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 7cc42ec57f..0f921ab80a 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
@@ -317,6 +317,7 @@ class DefaultNavigator @Inject constructor(
if (context is AppCompatActivity) {
if (context !is MatrixToBottomSheet.InteractionListener) {
fatalError("Caller context should implement MatrixToBottomSheet.InteractionListener", vectorPreferences.failFast())
+ return
}
// TODO check if there is already one??
MatrixToBottomSheet.withLink(link, origin)
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 d03fcadcfa..abfca1a64c 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
@@ -233,6 +233,7 @@ class NotificationUtils @Inject constructor(
* Build a polling thread listener notification.
*
* @param subTitleResId subtitle string resource Id of the notification
+ * @param withProgress true to show indeterminate progress on the notification
* @return the polling thread listener notification
*/
@SuppressLint("NewApi")
@@ -298,10 +299,8 @@ class NotificationUtils @Inject constructor(
* Build an incoming call notification.
* This notification starts the VectorHomeActivity which is in charge of centralizing the incoming call flow.
*
- * @param isVideo true if this is a video call, false for voice call
- * @param roomName the room name in which the call is pending.
- * @param matrixId the matrix id
- * @param callId the call id.
+ * @param call information about the call
+ * @param title title of the notification
* @param fromBg true if the app is in background when posting the notification
* @return the call notification.
*/
@@ -430,11 +429,8 @@ class NotificationUtils @Inject constructor(
/**
* Build a pending call notification.
*
- * @param isVideo true if this is a video call, false for voice call
- * @param roomName the room name in which the call is pending.
- * @param roomId the room Id
- * @param matrixId the matrix id
- * @param callId the call id.
+ * @param call information about the call
+ * @param title title of the notification
* @return the call notification.
*/
@SuppressLint("NewApi")
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/DirectLoginUseCase.kt b/vector/src/main/java/im/vector/app/features/onboarding/DirectLoginUseCase.kt
index 171d8f7bb5..3014b199b4 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/DirectLoginUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/DirectLoginUseCase.kt
@@ -20,7 +20,7 @@ import im.vector.app.R
import im.vector.app.core.extensions.andThen
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.onboarding.OnboardingAction.LoginOrRegister
-import org.matrix.android.sdk.api.MatrixPatterns.getDomain
+import org.matrix.android.sdk.api.MatrixPatterns.getServerName
import org.matrix.android.sdk.api.auth.AuthenticationService
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
import org.matrix.android.sdk.api.auth.wellknown.WellknownResult
@@ -75,7 +75,7 @@ class DirectLoginUseCase @Inject constructor(
)
private fun fallbackConfig(action: LoginOrRegister, wellKnownPrompt: WellknownResult.Prompt) = HomeServerConnectionConfig(
- homeServerUri = uriFactory.parse("https://${action.username.getDomain()}"),
+ homeServerUri = uriFactory.parse("https://${action.username.getServerName()}"),
homeServerUriBase = uriFactory.parse(wellKnownPrompt.homeServerUrl),
identityServerUri = wellKnownPrompt.identityServerUrl?.let { uriFactory.parse(it) }
)
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 b7ce7ffdb4..e2c1aaa2a4 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
@@ -156,13 +156,16 @@ class BugReporter @Inject constructor(
/**
* Send a bug report.
*
- * @param reportType The report type (bug, suggestion, feedback)
- * @param withDevicesLogs true to include the device log
- * @param withCrashLogs true to include the crash logs
+ * @param reportType The report type (bug, suggestion, feedback)
+ * @param withDevicesLogs true to include the device log
+ * @param withCrashLogs true to include the crash logs
* @param withKeyRequestHistory true to include the crash logs
- * @param withScreenshot true to include the screenshot
+ * @param withScreenshot true to include the screenshot
* @param theBugDescription the bug description
- * @param listener the listener
+ * @param serverVersion version of the server
+ * @param canContact true if the user opt in to be contacted directly
+ * @param customFields fields which will be sent with the report
+ * @param listener the listener
*/
@SuppressLint("StaticFieldLeak")
fun sendBugReport(reportType: ReportType,
@@ -287,7 +290,8 @@ class BugReporter @Inject constructor(
.addFormDataPart("app_language", VectorLocale.applicationLocale.toString())
.addFormDataPart("default_app_language", systemLocaleProvider.getSystemLocale().toString())
.addFormDataPart("theme", ThemeUtils.getApplicationTheme(context))
- .addFormDataPart("server_version", serverVersion).apply {
+ .addFormDataPart("server_version", serverVersion)
+ .apply {
customFields?.forEach { (name, value) ->
addFormDataPart(name, value)
}
@@ -678,7 +682,7 @@ class BugReporter @Inject constructor(
/**
* Retrieves the logs.
*
- * @param streamWriter the stream writer
+ * @param streamWriter the stream writer
* @param isErrorLogCat true to save the error logs
*/
private fun getLogCatError(streamWriter: OutputStreamWriter, isErrorLogCat: Boolean) {
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 a530b6e667..72cc63e5c7 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
@@ -39,7 +39,7 @@ public class BugReporterMultipartBody extends RequestBody {
/**
* Upload listener
*
- * @param totalWritten total written bytes
+ * @param totalWritten total written bytes
* @param contentLength content length
*/
void onWrite(long totalWritten, long contentLength);
@@ -296,4 +296,4 @@ public class BugReporterMultipartBody extends RequestBody {
return new BugReporterMultipartBody(boundary, parts);
}
}
-}
\ No newline at end of file
+}
diff --git a/vector/src/main/java/im/vector/app/features/rageshake/VectorUncaughtExceptionHandler.kt b/vector/src/main/java/im/vector/app/features/rageshake/VectorUncaughtExceptionHandler.kt
index bc78b84088..5496ff4a94 100644
--- a/vector/src/main/java/im/vector/app/features/rageshake/VectorUncaughtExceptionHandler.kt
+++ b/vector/src/main/java/im/vector/app/features/rageshake/VectorUncaughtExceptionHandler.kt
@@ -57,7 +57,7 @@ class VectorUncaughtExceptionHandler @Inject constructor(
/**
* An uncaught exception has been triggered.
*
- * @param thread the thread
+ * @param thread the thread
* @param throwable the throwable
* @return the exception description
*/
diff --git a/vector/src/main/java/im/vector/app/features/raw/wellknown/ElementWellKnownExt.kt b/vector/src/main/java/im/vector/app/features/raw/wellknown/ElementWellKnownExt.kt
index 4dd5a68673..91269cb114 100644
--- a/vector/src/main/java/im/vector/app/features/raw/wellknown/ElementWellKnownExt.kt
+++ b/vector/src/main/java/im/vector/app/features/raw/wellknown/ElementWellKnownExt.kt
@@ -16,14 +16,14 @@
package im.vector.app.features.raw.wellknown
-import org.matrix.android.sdk.api.MatrixPatterns.getDomain
+import org.matrix.android.sdk.api.MatrixPatterns.getServerName
import org.matrix.android.sdk.api.auth.data.SessionParams
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.raw.RawService
suspend fun RawService.getElementWellknown(sessionParams: SessionParams): ElementWellKnown? {
// By default we use the domain of the userId to retrieve the .well-known data
- val domain = sessionParams.userId.getDomain()
+ val domain = sessionParams.userId.getServerName()
return tryOrNull { getWellknown(domain) }
?.let { ElementWellKnownMapper.from(it) }
}
diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt
index 1994de396f..f1306d9851 100644
--- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt
@@ -35,7 +35,7 @@ import im.vector.app.features.raw.wellknown.getElementWellknown
import im.vector.app.features.raw.wellknown.isE2EByDefault
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
-import org.matrix.android.sdk.api.MatrixPatterns.getDomain
+import org.matrix.android.sdk.api.MatrixPatterns.getServerName
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.raw.RawService
@@ -98,7 +98,7 @@ class CreateRoomViewModel @AssistedInject constructor(
private fun initHomeServerName() {
setState {
copy(
- homeServerName = session.myUserId.getDomain()
+ homeServerName = session.myUserId.getServerName()
)
}
}
diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryListCreator.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryListCreator.kt
index 90283de77c..a168ea749c 100644
--- a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryListCreator.kt
+++ b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryListCreator.kt
@@ -20,7 +20,7 @@ import im.vector.app.R
import im.vector.app.core.resources.StringArrayProvider
import im.vector.app.features.roomdirectory.RoomDirectoryData
import im.vector.app.features.roomdirectory.RoomDirectoryServer
-import org.matrix.android.sdk.api.MatrixPatterns.getDomain
+import org.matrix.android.sdk.api.MatrixPatterns.getServerName
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol
import javax.inject.Inject
@@ -37,7 +37,7 @@ class RoomDirectoryListCreator @Inject constructor(
val protocols = ArrayList()
// Add user homeserver name
- val userHsName = session.myUserId.getDomain()
+ val userHsName = session.myUserId.getServerName()
// Add default protocol
protocols.add(
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt
index 2641eb4184..a66ef9a659 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt
@@ -31,7 +31,7 @@ import im.vector.app.features.powerlevel.PowerLevelsFlowFactory
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
-import org.matrix.android.sdk.api.MatrixPatterns.getDomain
+import org.matrix.android.sdk.api.MatrixPatterns.getServerName
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
@@ -96,7 +96,7 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo
private fun initHomeServerName() {
setState {
copy(
- homeServerName = session.myUserId.getDomain()
+ homeServerName = session.myUserId.getServerName()
)
}
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/FontScale.kt b/vector/src/main/java/im/vector/app/features/settings/FontScale.kt
index c4ea730afd..a1acef7d35 100644
--- a/vector/src/main/java/im/vector/app/features/settings/FontScale.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/FontScale.kt
@@ -78,6 +78,7 @@ object FontScale {
/**
* Store the font scale value.
*
+ * @param context the Android context
* @param fontScaleValue the font scale value to store
*/
private fun saveFontScaleValue(context: Context, fontScaleValue: FontScaleValue) {
diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorLocale.kt b/vector/src/main/java/im/vector/app/features/settings/VectorLocale.kt
index 3fb3d3f7c8..326f20845f 100644
--- a/vector/src/main/java/im/vector/app/features/settings/VectorLocale.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/VectorLocale.kt
@@ -120,8 +120,8 @@ object VectorLocale {
/**
* Get String from a locale.
*
- * @param context the context
- * @param locale the locale
+ * @param context the context
+ * @param locale the locale
* @param resourceId the string resource id
* @return the localized string
*/
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 c841c6a0af..72f6080417 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
@@ -203,6 +203,7 @@ class VectorPreferences @Inject constructor(
private const val TAKE_PHOTO_VIDEO_MODE = "TAKE_PHOTO_VIDEO_MODE"
private const val SETTINGS_LABS_RENDER_LOCATIONS_IN_TIMELINE = "SETTINGS_LABS_RENDER_LOCATIONS_IN_TIMELINE"
+ private const val SETTINGS_LABS_ENABLE_LIVE_LOCATION = "SETTINGS_LABS_ENABLE_LIVE_LOCATION"
// This key will be used to identify clients with the old thread support enabled io.element.thread
const val SETTINGS_LABS_ENABLE_THREAD_MESSAGES_OLD_CLIENTS = "SETTINGS_LABS_ENABLE_THREAD_MESSAGES"
@@ -492,7 +493,7 @@ class VectorPreferences @Inject constructor(
/**
* Update the notification ringtone.
*
- * @param uri the new notification ringtone, or null for no RingTone
+ * @param uri the new notification ringtone, or null for no RingTone
*/
fun setNotificationRingTone(uri: Uri?) {
defaultPrefs.edit {
@@ -635,7 +636,7 @@ class VectorPreferences @Inject constructor(
/**
* Tells if the application is started on boot.
*
- * @param value true to start the application on boot
+ * @param value true to start the application on boot
*/
fun setAutoStartOnBoot(value: Boolean) {
defaultPrefs.edit {
@@ -655,7 +656,7 @@ class VectorPreferences @Inject constructor(
/**
* Updates the selected saving period.
*
- * @param index the selected period index
+ * @param index the selected period index
*/
fun setSelectedMediasSavingPeriod(index: Int) {
defaultPrefs.edit {
@@ -1041,6 +1042,10 @@ class VectorPreferences @Inject constructor(
return defaultPrefs.getBoolean(SETTINGS_LABS_RENDER_LOCATIONS_IN_TIMELINE, true)
}
+ fun labsEnableLiveLocation(): Boolean {
+ return defaultPrefs.getBoolean(SETTINGS_LABS_ENABLE_LIVE_LOCATION, false)
+ }
+
/**
* Indicates whether or not thread messages are enabled.
*/
diff --git a/vector/src/main/java/im/vector/app/features/spaces/LeaveSpaceBottomSheet.kt b/vector/src/main/java/im/vector/app/features/spaces/LeaveSpaceBottomSheet.kt
deleted file mode 100644
index a292b64ddd..0000000000
--- a/vector/src/main/java/im/vector/app/features/spaces/LeaveSpaceBottomSheet.kt
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright (c) 2021 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.spaces
-
-import android.app.Activity
-import android.graphics.Typeface
-import android.os.Bundle
-import android.os.Parcelable
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.core.text.toSpannable
-import androidx.core.view.isInvisible
-import androidx.core.view.isVisible
-import androidx.lifecycle.lifecycleScope
-import com.airbnb.mvrx.Fail
-import com.airbnb.mvrx.Loading
-import com.airbnb.mvrx.args
-import com.airbnb.mvrx.parentFragmentViewModel
-import com.airbnb.mvrx.withState
-import dagger.hilt.android.AndroidEntryPoint
-import im.vector.app.R
-import im.vector.app.core.error.ErrorFormatter
-import im.vector.app.core.extensions.registerStartForActivityResult
-import im.vector.app.core.extensions.setTextOrHide
-import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
-import im.vector.app.core.resources.ColorProvider
-import im.vector.app.core.utils.styleMatchingText
-import im.vector.app.databinding.BottomSheetLeaveSpaceBinding
-import im.vector.app.features.displayname.getBestName
-import im.vector.app.features.spaces.leave.SpaceLeaveAdvancedActivity
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.parcelize.Parcelize
-import me.gujun.android.span.span
-import org.matrix.android.sdk.api.util.toMatrixItem
-import reactivecircus.flowbinding.android.widget.checkedChanges
-import javax.inject.Inject
-
-@AndroidEntryPoint
-class LeaveSpaceBottomSheet : VectorBaseBottomSheetDialogFragment() {
-
- val settingsViewModel: SpaceMenuViewModel by parentFragmentViewModel()
-
- override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetLeaveSpaceBinding {
- return BottomSheetLeaveSpaceBinding.inflate(inflater, container, false)
- }
-
- @Inject lateinit var colorProvider: ColorProvider
- @Inject lateinit var errorFormatter: ErrorFormatter
-
- @Parcelize
- data class Args(
- val spaceId: String
- ) : Parcelable
-
- override val showExpanded = true
-
- private val spaceArgs: SpaceBottomSheetSettingsArgs by args()
-
- private val cherryPickLeaveActivityResult = registerStartForActivityResult { activityResult ->
- if (activityResult.resultCode == Activity.RESULT_OK) {
- // nothing actually?
- } else {
- // move back to default
- settingsViewModel.handle(SpaceLeaveViewAction.SetAutoLeaveAll)
- }
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- views.autoLeaveRadioGroup.checkedChanges()
- .onEach {
- when (it) {
- views.leaveAll.id -> {
- settingsViewModel.handle(SpaceLeaveViewAction.SetAutoLeaveAll)
- }
- views.leaveNone.id -> {
- settingsViewModel.handle(SpaceLeaveViewAction.SetAutoLeaveNone)
- }
- views.leaveSelected.id -> {
- settingsViewModel.handle(SpaceLeaveViewAction.SetAutoLeaveSelected)
- // launch dedicated activity
- cherryPickLeaveActivityResult.launch(
- SpaceLeaveAdvancedActivity.newIntent(requireContext(), spaceArgs.spaceId)
- )
- }
- }
- }
- .launchIn(viewLifecycleOwner.lifecycleScope)
-
- views.leaveButton.debouncedClicks {
- settingsViewModel.handle(SpaceLeaveViewAction.LeaveSpace)
- }
-
- views.cancelButton.debouncedClicks {
- dismiss()
- }
- }
-
- override fun invalidate() = withState(settingsViewModel) { state ->
- super.invalidate()
-
- val spaceSummary = state.spaceSummary ?: return@withState
- val bestName = spaceSummary.toMatrixItem().getBestName()
- val commonText = getString(R.string.space_leave_prompt_msg_with_name, bestName)
- .toSpannable().styleMatchingText(bestName, Typeface.BOLD)
-
- val warningMessage: CharSequence = if (spaceSummary.otherMemberIds.isEmpty()) {
- span {
- +commonText
- +"\n\n"
- span(getString(R.string.space_leave_prompt_msg_only_you)) {
- textColor = colorProvider.getColorFromAttribute(R.attr.colorError)
- }
- }
- } else if (state.isLastAdmin) {
- span {
- +commonText
- +"\n\n"
- span(getString(R.string.space_leave_prompt_msg_as_admin)) {
- textColor = colorProvider.getColorFromAttribute(R.attr.colorError)
- }
- }
- } else if (!spaceSummary.isPublic) {
- span {
- +commonText
- +"\n\n"
- span(getString(R.string.space_leave_prompt_msg_private)) {
- textColor = colorProvider.getColorFromAttribute(R.attr.colorError)
- }
- }
- } else {
- commonText
- }
-
- views.bottomLeaveSpaceWarningText.setTextOrHide(warningMessage)
-
- views.inlineErrorText.setTextOrHide(null)
- if (state.leavingState is Loading) {
- views.leaveButton.isInvisible = true
- views.cancelButton.isInvisible = true
- views.leaveProgress.isVisible = true
- } else {
- views.leaveButton.isInvisible = false
- views.cancelButton.isInvisible = false
- views.leaveProgress.isVisible = false
- if (state.leavingState is Fail) {
- views.inlineErrorText.setTextOrHide(errorFormatter.toHumanReadable(state.leavingState.error))
- }
- }
-
- val hasChildren = (spaceSummary.spaceChildren?.size ?: 0) > 0
- if (hasChildren) {
- views.autoLeaveRadioGroup.isVisible = true
- when (state.leaveMode) {
- SpaceMenuState.LeaveMode.LEAVE_ALL -> {
- views.autoLeaveRadioGroup.check(views.leaveAll.id)
- }
- SpaceMenuState.LeaveMode.LEAVE_NONE -> {
- views.autoLeaveRadioGroup.check(views.leaveNone.id)
- }
- SpaceMenuState.LeaveMode.LEAVE_SELECTED -> {
- views.autoLeaveRadioGroup.check(views.leaveSelected.id)
- }
- }
- } else {
- views.autoLeaveRadioGroup.isVisible = false
- }
- }
-
- companion object {
-
- fun newInstance(spaceId: String): LeaveSpaceBottomSheet {
- return LeaveSpaceBottomSheet().apply {
- setArguments(SpaceBottomSheetSettingsArgs(spaceId))
- }
- }
- }
-}
diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceSettingsMenuBottomSheet.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceSettingsMenuBottomSheet.kt
index 78eab5b97f..94aa7e19b8 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/SpaceSettingsMenuBottomSheet.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceSettingsMenuBottomSheet.kt
@@ -35,6 +35,7 @@ import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.navigation.Navigator
import im.vector.app.features.rageshake.BugReporter
import im.vector.app.features.roomprofile.RoomProfileActivity
+import im.vector.app.features.spaces.leave.SpaceLeaveAdvancedActivity
import im.vector.app.features.spaces.manage.ManageType
import im.vector.app.features.spaces.manage.SpaceManageActivity
import kotlinx.parcelize.Parcelize
@@ -109,7 +110,7 @@ class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment() {
interface InteractionListener {
+ fun onFilterQueryChanged(query: String?)
fun onButtonClick(spaceChildInfo: SpaceChildInfo)
fun onSpaceChildClick(spaceChildInfo: SpaceChildInfo)
fun onRoomClick(spaceChildInfo: SpaceChildInfo)
@@ -62,6 +65,7 @@ class SpaceDirectoryController @Inject constructor(
}
var listener: InteractionListener? = null
+ private val matchFilter = SpaceChildInfoMatchFilter()
override fun buildModels(data: SpaceDirectoryState?) {
val host = this
@@ -76,7 +80,7 @@ class SpaceDirectoryController @Inject constructor(
val failure = results.error
if (failure is Failure.ServerError && failure.error.code == M_UNRECOGNIZED) {
genericPillItem {
- id("HS no Support")
+ id("hs_no_support")
imageRes(R.drawable.error)
tintIcon(false)
text(
@@ -132,43 +136,52 @@ class SpaceDirectoryController @Inject constructor(
}
}
} else {
- flattenChildInfo.forEach { info ->
- val isSpace = info.roomType == RoomType.SPACE
- val isJoined = data?.joinedRoomsIds?.contains(info.childRoomId) == true
- val isLoading = data?.changeMembershipStates?.get(info.childRoomId)?.isInProgress() ?: false
- val error = (data?.changeMembershipStates?.get(info.childRoomId) as? ChangeMembershipState.FailedJoining)?.throwable
- // if it's known use that matrixItem because it would have a better computed name
- val matrixItem = data?.knownRoomSummaries?.find { it.roomId == info.childRoomId }?.toMatrixItem()
- ?: info.toMatrixItem()
+ matchFilter.filter = data?.currentFilter ?: ""
+ val filteredChildInfo = flattenChildInfo.filter { matchFilter.test(it) }
- spaceChildInfoItem {
- id(info.childRoomId)
- matrixItem(matrixItem)
- avatarRenderer(host.avatarRenderer)
- topic(info.topic)
- suggested(info.suggested.orFalse())
- errorLabel(
- error?.let {
- host.stringProvider.getString(R.string.error_failed_to_join_room, host.errorFormatter.toHumanReadable(it))
+ if (filteredChildInfo.isEmpty()) {
+ spaceDirectoryFilterNoResults {
+ id("no_results")
+ }
+ } else {
+ filteredChildInfo.forEach { info ->
+ val isSpace = info.roomType == RoomType.SPACE
+ val isJoined = data?.joinedRoomsIds?.contains(info.childRoomId) == true
+ val isLoading = data?.changeMembershipStates?.get(info.childRoomId)?.isInProgress() ?: false
+ val error = (data?.changeMembershipStates?.get(info.childRoomId) as? ChangeMembershipState.FailedJoining)?.throwable
+ // if it's known use that matrixItem because it would have a better computed name
+ val matrixItem = data?.knownRoomSummaries?.find { it.roomId == info.childRoomId }?.toMatrixItem()
+ ?: info.toMatrixItem()
+
+ spaceChildInfoItem {
+ id(info.childRoomId)
+ matrixItem(matrixItem)
+ avatarRenderer(host.avatarRenderer)
+ topic(info.topic)
+ suggested(info.suggested.orFalse())
+ errorLabel(
+ error?.let {
+ host.stringProvider.getString(R.string.error_failed_to_join_room, host.errorFormatter.toHumanReadable(it))
+ }
+ )
+ memberCount(info.activeMemberCount ?: 0)
+ loading(isLoading)
+ buttonLabel(
+ when {
+ error != null -> host.stringProvider.getString(R.string.global_retry)
+ isJoined -> host.stringProvider.getString(R.string.action_open)
+ else -> host.stringProvider.getString(R.string.action_join)
+ }
+ )
+ apply {
+ if (isSpace) {
+ itemClickListener { host.listener?.onSpaceChildClick(info) }
+ } else {
+ itemClickListener { host.listener?.onRoomClick(info) }
}
- )
- memberCount(info.activeMemberCount ?: 0)
- loading(isLoading)
- buttonLabel(
- when {
- error != null -> host.stringProvider.getString(R.string.global_retry)
- isJoined -> host.stringProvider.getString(R.string.action_open)
- else -> host.stringProvider.getString(R.string.action_join)
- }
- )
- apply {
- if (isSpace) {
- itemClickListener { host.listener?.onSpaceChildClick(info) }
- } else {
- itemClickListener { host.listener?.onRoomClick(info) }
}
+ buttonClickListener { host.listener?.onButtonClick(info) }
}
- buttonClickListener { host.listener?.onButtonClick(info) }
}
}
}
diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt
index e59087778f..ed0bbdd911 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt
@@ -23,6 +23,7 @@ import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
+import androidx.appcompat.widget.SearchView
import androidx.core.text.toSpannable
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
@@ -44,7 +45,6 @@ import im.vector.app.core.utils.openUrlInExternalBrowser
import im.vector.app.databinding.FragmentSpaceDirectoryBinding
import im.vector.app.features.analytics.plan.MobileScreen
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
-import im.vector.app.features.matrixto.SpaceCardRenderer
import im.vector.app.features.permalink.PermalinkHandler
import im.vector.app.features.spaces.manage.ManageType
import im.vector.app.features.spaces.manage.SpaceAddRoomSpaceChooserBottomSheet
@@ -63,7 +63,6 @@ data class SpaceDirectoryArgs(
class SpaceDirectoryFragment @Inject constructor(
private val epoxyController: SpaceDirectoryController,
private val permalinkHandler: PermalinkHandler,
- private val spaceCardRenderer: SpaceCardRenderer,
private val colorProvider: ColorProvider
) : VectorBaseFragment(),
SpaceDirectoryController.InteractionListener,
@@ -123,9 +122,6 @@ class SpaceDirectoryFragment @Inject constructor(
}
}
- views.spaceCard.matrixToCardMainButton.isVisible = false
- views.spaceCard.matrixToCardSecondaryButton.isVisible = false
-
// Hide FAB when list is scrolling
views.spaceDirectoryList.addOnScrollListener(
object : RecyclerView.OnScrollListener() {
@@ -167,18 +163,37 @@ class SpaceDirectoryFragment @Inject constructor(
// it's the root
toolbar?.setTitle(R.string.space_explore_activity_title)
} else {
- toolbar?.title = state.currentRootSummary?.name
+ val spaceName = state.currentRootSummary?.name
?: state.currentRootSummary?.canonicalAlias
- ?: getString(R.string.space_explore_activity_title)
+
+ if (spaceName != null) {
+ toolbar?.title = spaceName
+ toolbar?.subtitle = getString(R.string.space_explore_activity_title)
+ } else {
+ toolbar?.title = getString(R.string.space_explore_activity_title)
+ }
}
- spaceCardRenderer.render(state.currentRootSummary, emptyList(), this, views.spaceCard, showDescription = false)
views.addOrCreateChatRoomButton.isVisible = state.canAddRooms
}
override fun onPrepareOptionsMenu(menu: Menu) = withState(viewModel) { state ->
menu.findItem(R.id.spaceAddRoom)?.isVisible = state.canAddRooms
menu.findItem(R.id.spaceCreateRoom)?.isVisible = false // Not yet implemented
+
+ menu.findItem(R.id.spaceSearch)?.let { searchItem ->
+ val searchView = searchItem.actionView as SearchView
+ searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
+ override fun onQueryTextSubmit(query: String?): Boolean {
+ return true
+ }
+
+ override fun onQueryTextChange(newText: String?): Boolean {
+ onFilterQueryChanged(newText)
+ return true
+ }
+ })
+ }
super.onPrepareOptionsMenu(menu)
}
@@ -198,6 +213,10 @@ class SpaceDirectoryFragment @Inject constructor(
return super.onOptionsItemSelected(item)
}
+ override fun onFilterQueryChanged(query: String?) {
+ viewModel.handle(SpaceDirectoryViewAction.FilterRooms(query))
+ }
+
override fun onButtonClick(spaceChildInfo: SpaceChildInfo) {
viewModel.handle(SpaceDirectoryViewAction.JoinOrOpen(spaceChildInfo))
}
diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewAction.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewAction.kt
index 2166a7e306..1d180eea4f 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewAction.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewAction.kt
@@ -22,6 +22,7 @@ import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
sealed class SpaceDirectoryViewAction : VectorViewModelAction {
data class ExploreSubSpace(val spaceChildInfo: SpaceChildInfo) : SpaceDirectoryViewAction()
data class JoinOrOpen(val spaceChildInfo: SpaceChildInfo) : SpaceDirectoryViewAction()
+ data class FilterRooms(val query: String?) : SpaceDirectoryViewAction()
data class ShowDetails(val spaceChildInfo: SpaceChildInfo) : SpaceDirectoryViewAction()
data class NavigateToRoom(val roomId: String) : SpaceDirectoryViewAction()
object CreateNewRoom : SpaceDirectoryViewAction()
diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt
index 2ddcb42e2a..7ae2feebcf 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt
@@ -225,9 +225,16 @@ class SpaceDirectoryViewModel @AssistedInject constructor(
_viewEvents.post(SpaceDirectoryViewEvents.NavigateToCreateNewRoom(state.currentRootSummary?.roomId ?: initialState.spaceId))
}
}
+ is SpaceDirectoryViewAction.FilterRooms -> {
+ filter(action.query)
+ }
}
}
+ private fun filter(query: String?) {
+ setState { copy(currentFilter = query.orEmpty()) }
+ }
+
private fun handleBack() = withState { state ->
if (state.hierarchyStack.isEmpty()) {
_viewEvents.post(SpaceDirectoryViewEvents.Dismiss)
diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewAction.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewAction.kt
index 68b313ec7f..a25476bff9 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewAction.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewAction.kt
@@ -21,6 +21,9 @@ import im.vector.app.core.platform.VectorViewModelAction
sealed class SpaceLeaveAdvanceViewAction : VectorViewModelAction {
data class ToggleSelection(val roomId: String) : SpaceLeaveAdvanceViewAction()
data class UpdateFilter(val filter: String) : SpaceLeaveAdvanceViewAction()
+ data class SetFilteringEnabled(val isEnabled: Boolean) : SpaceLeaveAdvanceViewAction()
object DoLeave : SpaceLeaveAdvanceViewAction()
object ClearError : SpaceLeaveAdvanceViewAction()
+ object SelectAll : SpaceLeaveAdvanceViewAction()
+ object SelectNone : SpaceLeaveAdvanceViewAction()
}
diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewState.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewState.kt
index b8dcd3f7a2..fce5f4efa1 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewState.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewState.kt
@@ -28,8 +28,11 @@ data class SpaceLeaveAdvanceViewState(
val allChildren: Async> = Uninitialized,
val selectedRooms: List = emptyList(),
val currentFilter: String = "",
- val leaveState: Async = Uninitialized
+ val leaveState: Async = Uninitialized,
+ val isFilteringEnabled: Boolean = false,
+ val isLastAdmin: Boolean = false
) : MavericksState {
+
constructor(args: SpaceBottomSheetSettingsArgs) : this(
spaceId = args.spaceId
)
diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedFragment.kt
index 53c7481acb..308572a30f 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedFragment.kt
@@ -18,20 +18,23 @@ package im.vector.app.features.spaces.leave
import android.os.Bundle
import android.view.LayoutInflater
+import android.view.Menu
+import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
-import androidx.lifecycle.lifecycleScope
+import androidx.appcompat.widget.SearchView
+import androidx.coordinatorlayout.widget.CoordinatorLayout
+import androidx.core.view.isVisible
+import com.airbnb.mvrx.Success
import com.airbnb.mvrx.activityViewModel
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.platform.VectorBaseFragment
+import im.vector.app.core.utils.ToggleableAppBarLayoutBehavior
import im.vector.app.databinding.FragmentSpaceLeaveAdvancedBinding
-import kotlinx.coroutines.flow.debounce
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
import org.matrix.android.sdk.api.session.room.model.RoomSummary
-import reactivecircus.flowbinding.appcompat.queryTextChanges
import javax.inject.Inject
class SpaceLeaveAdvancedFragment @Inject constructor(
@@ -44,11 +47,33 @@ class SpaceLeaveAdvancedFragment @Inject constructor(
val viewModel: SpaceLeaveAdvancedViewModel by activityViewModel()
+ override fun getMenuRes() = R.menu.menu_space_leave
+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- setupToolbar(views.toolbar)
- .allowBack()
+
controller.listener = this
+
+ withState(viewModel) { state ->
+ setupToolbar(views.toolbar)
+ .setSubtitle(state.spaceSummary?.name)
+ .allowBack()
+
+ state.spaceSummary?.let { summary ->
+ val warningMessage: CharSequence? = when {
+ summary.otherMemberIds.isEmpty() -> getString(R.string.space_leave_prompt_msg_only_you)
+ state.isLastAdmin -> getString(R.string.space_leave_prompt_msg_as_admin)
+ !summary.isPublic -> getString(R.string.space_leave_prompt_msg_private)
+ else -> null
+ }
+
+ views.spaceLeavePromptDescription.isVisible = warningMessage != null
+ views.spaceLeavePromptDescription.text = warningMessage
+ }
+
+ views.spaceLeavePromptTitle.text = getString(R.string.space_leave_prompt_msg_with_name, state.spaceSummary?.name ?: "")
+ }
+
views.roomList.configureWith(controller)
views.spaceLeaveCancel.debouncedClicks { requireActivity().finish() }
@@ -56,12 +81,23 @@ class SpaceLeaveAdvancedFragment @Inject constructor(
viewModel.handle(SpaceLeaveAdvanceViewAction.DoLeave)
}
- views.publicRoomsFilter.queryTextChanges()
- .debounce(100)
- .onEach {
- viewModel.handle(SpaceLeaveAdvanceViewAction.UpdateFilter(it.toString()))
- }
- .launchIn(viewLifecycleOwner.lifecycleScope)
+ views.spaceLeaveSelectGroup.setOnCheckedChangeListener { _, optionId ->
+ when (optionId) {
+ R.id.spaceLeaveSelectAll -> viewModel.handle(SpaceLeaveAdvanceViewAction.SelectAll)
+ R.id.spaceLeaveSelectNone -> viewModel.handle(SpaceLeaveAdvanceViewAction.SelectNone)
+ }
+ }
+ }
+
+ override fun onPrepareOptionsMenu(menu: Menu) {
+ menu.findItem(R.id.menu_space_leave_search)?.let { searchItem ->
+ searchItem.bind(
+ onExpanded = { viewModel.handle(SpaceLeaveAdvanceViewAction.SetFilteringEnabled(isEnabled = true)) },
+ onCollapsed = { viewModel.handle(SpaceLeaveAdvanceViewAction.SetFilteringEnabled(isEnabled = false)) },
+ onTextChanged = { viewModel.handle(SpaceLeaveAdvanceViewAction.UpdateFilter(it)) }
+ )
+ }
+ super.onPrepareOptionsMenu(menu)
}
override fun onDestroyView() {
@@ -72,10 +108,63 @@ class SpaceLeaveAdvancedFragment @Inject constructor(
override fun invalidate() = withState(viewModel) { state ->
super.invalidate()
+
+ if (state.isFilteringEnabled) {
+ views.appBarLayout.setExpanded(false)
+ }
+
+ updateAppBarBehaviorState(state)
+ updateRadioButtonsState(state)
+
controller.setData(state)
}
override fun onItemSelected(roomSummary: RoomSummary) {
viewModel.handle(SpaceLeaveAdvanceViewAction.ToggleSelection(roomSummary.roomId))
}
+
+ private fun updateAppBarBehaviorState(state: SpaceLeaveAdvanceViewState) {
+ val behavior = (views.appBarLayout.layoutParams as CoordinatorLayout.LayoutParams).behavior as ToggleableAppBarLayoutBehavior
+ behavior.isEnabled = !state.isFilteringEnabled
+ }
+
+ private fun updateRadioButtonsState(state: SpaceLeaveAdvanceViewState) {
+ (state.allChildren as? Success)?.invoke()?.size?.let { allChildrenCount ->
+ when (state.selectedRooms.size) {
+ 0 -> views.spaceLeaveSelectNone.isChecked = true
+ allChildrenCount -> views.spaceLeaveSelectAll.isChecked = true
+ else -> views.spaceLeaveSelectSemi.isChecked = true
+ }
+ }
+ }
+
+ private fun MenuItem.bind(
+ onExpanded: () -> Unit,
+ onCollapsed: () -> Unit,
+ onTextChanged: (String) -> Unit) {
+ setOnActionExpandListener(object : MenuItem.OnActionExpandListener {
+ override fun onMenuItemActionExpand(item: MenuItem?): Boolean {
+ onExpanded()
+ return true
+ }
+
+ override fun onMenuItemActionCollapse(item: MenuItem?): Boolean {
+ onCollapsed()
+ return true
+ }
+ })
+
+ val searchView = actionView as SearchView
+
+ searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
+ override fun onQueryTextSubmit(query: String?): Boolean {
+ return false
+ }
+
+ override fun onQueryTextChange(newText: String?): Boolean {
+ onTextChanged(newText ?: "")
+ return true
+ }
+ })
+ }
}
diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt
index 3f5a27f696..2ab417ac55 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt
@@ -36,9 +36,14 @@ import okhttp3.internal.toImmutableList
import org.matrix.android.sdk.api.query.ActiveSpaceFilter
import org.matrix.android.sdk.api.query.RoomCategoryFilter
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.getRoom
-import org.matrix.android.sdk.api.session.getRoomSummary
+import org.matrix.android.sdk.api.session.room.getStateEvent
import org.matrix.android.sdk.api.session.room.model.Membership
+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.room.powerlevels.Role
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
import org.matrix.android.sdk.flow.flow
import org.matrix.android.sdk.flow.unwrap
@@ -50,52 +55,24 @@ class SpaceLeaveAdvancedViewModel @AssistedInject constructor(
private val appStateHandler: AppStateHandler
) : VectorViewModel(initialState) {
- override fun handle(action: SpaceLeaveAdvanceViewAction) = withState { state ->
- when (action) {
- is SpaceLeaveAdvanceViewAction.ToggleSelection -> {
- val existing = state.selectedRooms.toMutableList()
- if (existing.contains(action.roomId)) {
- existing.remove(action.roomId)
- } else {
- existing.add(action.roomId)
- }
- setState {
- copy(
- selectedRooms = existing.toImmutableList()
- )
- }
- }
- is SpaceLeaveAdvanceViewAction.UpdateFilter -> {
- setState { copy(currentFilter = action.filter) }
- }
- SpaceLeaveAdvanceViewAction.DoLeave -> {
- setState { copy(leaveState = Loading()) }
- viewModelScope.launch {
- try {
- state.selectedRooms.forEach {
- try {
- session.roomService().leaveRoom(it)
- } catch (failure: Throwable) {
- // silently ignore?
- Timber.e(failure, "Fail to leave sub rooms/spaces")
- }
- }
+ init {
+ val space = session.getRoom(initialState.spaceId)
+ val spaceSummary = space?.roomSummary()
- session.spaceService().leaveSpace(initialState.spaceId)
- // We observe the membership and to dismiss when we have remote echo of leaving
- } catch (failure: Throwable) {
- setState { copy(leaveState = Fail(failure)) }
- }
- }
- }
- SpaceLeaveAdvanceViewAction.ClearError -> {
- setState { copy(leaveState = Uninitialized) }
+ val powerLevelsEvent = space?.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS)
+ powerLevelsEvent?.content?.toModel()?.let { powerLevelsContent ->
+ val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent)
+ val isAdmin = powerLevelsHelper.getUserRole(session.myUserId) is Role.Admin
+ val otherAdminCount = spaceSummary?.otherMemberIds
+ ?.map { powerLevelsHelper.getUserRole(it) }
+ ?.count { it is Role.Admin }
+ ?: 0
+ val isLastAdmin = isAdmin && otherAdminCount == 0
+ setState {
+ copy(isLastAdmin = isLastAdmin)
}
}
- }
- init {
- val spaceSummary = session.getRoomSummary(initialState.spaceId)
setState { copy(spaceSummary = spaceSummary) }
session.getRoom(initialState.spaceId)?.let { room ->
room.flow().liveRoomSummary()
@@ -127,6 +104,62 @@ class SpaceLeaveAdvancedViewModel @AssistedInject constructor(
}
}
+ override fun handle(action: SpaceLeaveAdvanceViewAction) {
+ when (action) {
+ is SpaceLeaveAdvanceViewAction.UpdateFilter -> setState { copy(currentFilter = action.filter) }
+ SpaceLeaveAdvanceViewAction.ClearError -> setState { copy(leaveState = Uninitialized) }
+ SpaceLeaveAdvanceViewAction.SelectNone -> setState { copy(selectedRooms = emptyList()) }
+ is SpaceLeaveAdvanceViewAction.SetFilteringEnabled -> setState { copy(isFilteringEnabled = action.isEnabled) }
+ is SpaceLeaveAdvanceViewAction.ToggleSelection -> handleSelectionToggle(action)
+ SpaceLeaveAdvanceViewAction.DoLeave -> handleLeave()
+ SpaceLeaveAdvanceViewAction.SelectAll -> handleSelectAll()
+ }
+ }
+
+ private fun handleSelectAll() = withState { state ->
+ val filteredRooms = (state.allChildren as? Success)?.invoke()?.filter {
+ it.name.contains(state.currentFilter, true)
+ }
+ filteredRooms?.let {
+ setState { copy(selectedRooms = it.map { it.roomId }) }
+ }
+ }
+
+ private fun handleLeave() = withState { state ->
+ setState { copy(leaveState = Loading()) }
+ viewModelScope.launch {
+ try {
+ state.selectedRooms.forEach {
+ try {
+ session.roomService().leaveRoom(it)
+ } catch (failure: Throwable) {
+ // silently ignore?
+ Timber.e(failure, "Fail to leave sub rooms/spaces")
+ }
+ }
+
+ session.spaceService().leaveSpace(initialState.spaceId)
+ // We observe the membership and to dismiss when we have remote echo of leaving
+ } catch (failure: Throwable) {
+ setState { copy(leaveState = Fail(failure)) }
+ }
+ }
+ }
+
+ private fun handleSelectionToggle(action: SpaceLeaveAdvanceViewAction.ToggleSelection) = withState { state ->
+ val existing = state.selectedRooms.toMutableList()
+ if (existing.contains(action.roomId)) {
+ existing.remove(action.roomId)
+ } else {
+ existing.add(action.roomId)
+ }
+ setState {
+ copy(
+ selectedRooms = existing.toImmutableList(),
+ )
+ }
+ }
+
@AssistedFactory
interface Factory : MavericksAssistedViewModelFactory {
override fun create(initialState: SpaceLeaveAdvanceViewState): SpaceLeaveAdvancedViewModel
diff --git a/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt b/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt
index 3b1e8240fa..3d1a224d0c 100644
--- a/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt
+++ b/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt
@@ -108,6 +108,7 @@ object ThemeUtils {
/**
* Update the application theme.
*
+ * @param context the Android context
* @param aTheme the new theme
*/
fun setApplicationTheme(context: Context, aTheme: String) {
@@ -126,9 +127,11 @@ object ThemeUtils {
}
/**
- * Set the activity theme according to the selected one.
+ * Set the activity theme according to the selected one. Default is Light, so if this is the current
+ * theme, the theme is not changed.
*
* @param activity the activity
+ * @param otherThemes themes to apply for dark and black theme
*/
fun setActivityTheme(activity: Activity, otherThemes: ActivityOtherThemes) {
when (getApplicationTheme(activity)) {
@@ -143,7 +146,7 @@ object ThemeUtils {
/**
* Translates color attributes to colors.
*
- * @param c Context
+ * @param c Context
* @param colorAttribute Color Attribute
* @return Requested Color
*/
@@ -175,8 +178,8 @@ object ThemeUtils {
/**
* Tint the drawable with a theme attribute.
*
- * @param context the context
- * @param drawable the drawable to tint
+ * @param context the context
+ * @param drawable the drawable to tint
* @param attribute the theme color
* @return the tinted drawable
*/
@@ -188,7 +191,7 @@ object ThemeUtils {
* Tint the drawable with a color integer.
*
* @param drawable the drawable to tint
- * @param color the color
+ * @param color the color
* @return the tinted drawable
*/
fun tintDrawableWithColor(drawable: Drawable, @ColorInt color: Int): Drawable {
diff --git a/vector/src/main/java/im/vector/app/features/webview/WebViewEventListener.kt b/vector/src/main/java/im/vector/app/features/webview/WebViewEventListener.kt
index bd77283029..2f00ad07b9 100644
--- a/vector/src/main/java/im/vector/app/features/webview/WebViewEventListener.kt
+++ b/vector/src/main/java/im/vector/app/features/webview/WebViewEventListener.kt
@@ -48,8 +48,8 @@ interface WebViewEventListener {
/**
* Triggered when an error occurred while loading a page.
*
- * @param url The url that failed.
- * @param errorCode The error code.
+ * @param url The url that failed.
+ * @param errorCode The error code.
* @param description The error description.
*/
fun onPageError(url: String, errorCode: Int, description: String) {
@@ -59,8 +59,8 @@ interface WebViewEventListener {
/**
* Triggered when an error occurred while loading a page.
*
- * @param url The url that failed.
- * @param errorCode The error code.
+ * @param url The url that failed.
+ * @param errorCode The error code.
* @param description The error description.
*/
fun onHttpError(url: String, errorCode: Int, description: String) {
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 3c88ea65a3..fc73e71b51 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
@@ -110,6 +110,7 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
/**
* Retrieve the latest botOptions event.
*
+ * @param widgetPostAPIMediator the post api mediator
* @param eventData the modular data
*/
private fun getBotOptions(widgetPostAPIMediator: WidgetPostAPIMediator, eventData: JsonDict) {
@@ -171,6 +172,7 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
/**
* Provides the membership state.
*
+ * @param widgetPostAPIMediator the post api mediator
* @param eventData the modular data
*/
private fun getMembershipState(widgetPostAPIMediator: WidgetPostAPIMediator, eventData: JsonDict) {
@@ -190,6 +192,7 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
/**
* Request the latest joined room event.
*
+ * @param widgetPostAPIMediator the post api mediator
* @param eventData the modular data
*/
private fun getJoinRules(widgetPostAPIMediator: WidgetPostAPIMediator, eventData: JsonDict) {
@@ -208,6 +211,7 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
/**
* Provide the widgets list.
*
+ * @param widgetPostAPIMediator the post api mediator
* @param eventData the modular data
*/
private fun getWidgets(widgetPostAPIMediator: WidgetPostAPIMediator, eventData: JsonDict) {
@@ -228,6 +232,7 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
/**
* Set a new widget.
*
+ * @param widgetPostAPIMediator the post api mediator
* @param eventData the modular data
*/
private fun setWidget(widgetPostAPIMediator: WidgetPostAPIMediator, eventData: JsonDict) {
@@ -303,6 +308,7 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
/**
* Update the 'plumbing state".
*
+ * @param widgetPostAPIMediator the post api mediator
* @param eventData the modular data
*/
private fun setPlumbingState(widgetPostAPIMediator: WidgetPostAPIMediator, eventData: JsonDict) {
@@ -328,6 +334,7 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
/**
* Update the bot options.
*
+ * @param widgetPostAPIMediator the post api mediator
* @param eventData the modular data
*/
@Suppress("UNCHECKED_CAST")
@@ -353,6 +360,7 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
/**
* Update the bot power levels.
*
+ * @param widgetPostAPIMediator the post api mediator
* @param eventData the modular data
*/
private fun setBotPower(widgetPostAPIMediator: WidgetPostAPIMediator, eventData: JsonDict) {
@@ -375,6 +383,7 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
/**
* Invite an user to this room.
*
+ * @param widgetPostAPIMediator the post api mediator
* @param eventData the modular data
*/
private fun inviteUser(widgetPostAPIMediator: WidgetPostAPIMediator, eventData: JsonDict) {
@@ -397,6 +406,7 @@ class WidgetPostAPIHandler @AssistedInject constructor(@Assisted private val roo
/**
* Provides the number of members in the rooms.
*
+ * @param widgetPostAPIMediator the post api mediator
* @param eventData the modular data
*/
private fun getMembershipCount(widgetPostAPIMediator: WidgetPostAPIMediator, eventData: JsonDict) {
diff --git a/vector/src/main/res/drawable-hdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-hdpi/bg_no_location_map.webp
index 23a45700f0..3241b5dc82 100644
Binary files a/vector/src/main/res/drawable-hdpi/bg_no_location_map.webp and b/vector/src/main/res/drawable-hdpi/bg_no_location_map.webp differ
diff --git a/vector/src/main/res/drawable-mdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-mdpi/bg_no_location_map.webp
index a6130fba78..03f9ba5062 100644
Binary files a/vector/src/main/res/drawable-mdpi/bg_no_location_map.webp and b/vector/src/main/res/drawable-mdpi/bg_no_location_map.webp differ
diff --git a/vector/src/main/res/drawable-night-hdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-night-hdpi/bg_no_location_map.webp
new file mode 100644
index 0000000000..76e0a75dd6
Binary files /dev/null and b/vector/src/main/res/drawable-night-hdpi/bg_no_location_map.webp differ
diff --git a/vector/src/main/res/drawable-night-mdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-night-mdpi/bg_no_location_map.webp
new file mode 100644
index 0000000000..79900cec1b
Binary files /dev/null and b/vector/src/main/res/drawable-night-mdpi/bg_no_location_map.webp differ
diff --git a/vector/src/main/res/drawable-night-xhdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-night-xhdpi/bg_no_location_map.webp
new file mode 100644
index 0000000000..14f7e0e44c
Binary files /dev/null and b/vector/src/main/res/drawable-night-xhdpi/bg_no_location_map.webp differ
diff --git a/vector/src/main/res/drawable-night-xxhdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-night-xxhdpi/bg_no_location_map.webp
new file mode 100644
index 0000000000..91cb7c8eb6
Binary files /dev/null and b/vector/src/main/res/drawable-night-xxhdpi/bg_no_location_map.webp differ
diff --git a/vector/src/main/res/drawable-night-xxxhdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-night-xxxhdpi/bg_no_location_map.webp
new file mode 100644
index 0000000000..e4864a9eb2
Binary files /dev/null and b/vector/src/main/res/drawable-night-xxxhdpi/bg_no_location_map.webp differ
diff --git a/vector/src/main/res/drawable-xhdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-xhdpi/bg_no_location_map.webp
index e908191371..513089b55b 100644
Binary files a/vector/src/main/res/drawable-xhdpi/bg_no_location_map.webp and b/vector/src/main/res/drawable-xhdpi/bg_no_location_map.webp differ
diff --git a/vector/src/main/res/drawable-xxhdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-xxhdpi/bg_no_location_map.webp
index e062178367..50284965a7 100644
Binary files a/vector/src/main/res/drawable-xxhdpi/bg_no_location_map.webp and b/vector/src/main/res/drawable-xxhdpi/bg_no_location_map.webp differ
diff --git a/vector/src/main/res/drawable-xxxhdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-xxxhdpi/bg_no_location_map.webp
index 8b110d33fe..881af0055a 100644
Binary files a/vector/src/main/res/drawable-xxxhdpi/bg_no_location_map.webp and b/vector/src/main/res/drawable-xxxhdpi/bg_no_location_map.webp differ
diff --git a/vector/src/main/res/drawable/thread_filter_badge.xml b/vector/src/main/res/drawable/thread_filter_badge.xml
new file mode 100644
index 0000000000..c9a01197c8
--- /dev/null
+++ b/vector/src/main/res/drawable/thread_filter_badge.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/bottom_sheet_leave_space.xml b/vector/src/main/res/layout/bottom_sheet_leave_space.xml
deleted file mode 100644
index 9e5a7c7ebf..0000000000
--- a/vector/src/main/res/layout/bottom_sheet_leave_space.xml
+++ /dev/null
@@ -1,105 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/vector/src/main/res/layout/fragment_space_directory.xml b/vector/src/main/res/layout/fragment_space_directory.xml
index bc77bb1474..8bc53b5243 100644
--- a/vector/src/main/res/layout/fragment_space_directory.xml
+++ b/vector/src/main/res/layout/fragment_space_directory.xml
@@ -11,35 +11,12 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
-
-
-
-
-
-
-
-
-
-
+ android:layout_height="?attr/actionBarSize"
+ app:contentInsetStart="0dp">
+
@@ -57,7 +34,7 @@
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginEnd="16dp"
- android:layout_marginBottom="16dp "
+ android:layout_marginBottom="16dp"
android:contentDescription="@string/a11y_create_room"
android:scaleType="center"
android:src="@drawable/ic_fab_add"
diff --git a/vector/src/main/res/layout/fragment_space_leave_advanced.xml b/vector/src/main/res/layout/fragment_space_leave_advanced.xml
index 6216636458..67d9f044da 100644
--- a/vector/src/main/res/layout/fragment_space_leave_advanced.xml
+++ b/vector/src/main/res/layout/fragment_space_leave_advanced.xml
@@ -16,41 +16,107 @@
tools:listitem="@layout/item_room_to_add_in_space" />
+ android:layout_height="wrap_content"
+ app:layout_behavior="im.vector.app.core.utils.ToggleableAppBarLayoutBehavior">
-
-
+ android:layout_height="match_parent"
+ app:contentScrim="?android:colorBackground"
+ app:layout_scrollFlags="scroll|exitUntilCollapsed|enterAlways|snap"
+ app:scrimAnimationDuration="250"
+ app:scrimVisibleHeightTrigger="120dp"
+ app:titleEnabled="false"
+ app:toolbarId="@id/toolbar">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:padding="8dp"
+ app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior">
-
-
\ No newline at end of file
+
diff --git a/vector/src/main/res/layout/fragment_timeline.xml b/vector/src/main/res/layout/fragment_timeline.xml
index fcded76268..ea35d82886 100644
--- a/vector/src/main/res/layout/fragment_timeline.xml
+++ b/vector/src/main/res/layout/fragment_timeline.xml
@@ -37,13 +37,6 @@
-
-
+ app:layout_constraintTop_toBottomOf="@id/timelineRecyclerView" />
+ app:layout_constraintStart_toStartOf="parent"
+ tools:layout_height="300dp" />
+ app:layout_constraintStart_toStartOf="parent"
+ tools:visibility="visible" />
+ app:constraint_referenced_ids="composerLayout,notificationAreaView,failedMessagesWarningStub" />
diff --git a/vector/src/main/res/layout/item_explore_space_child.xml b/vector/src/main/res/layout/item_explore_space_child.xml
index 8f984e1b92..eef664664d 100644
--- a/vector/src/main/res/layout/item_explore_space_child.xml
+++ b/vector/src/main/res/layout/item_explore_space_child.xml
@@ -136,8 +136,8 @@
android:layout_height="1dp"
android:background="?vctr_list_separator_system"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="@id/joinSuggestedRoomButton"
+ app:layout_constraintStart_toStartOf="@id/roomNameView"
app:layout_constraintTop_toBottomOf="@id/inlineErrorText" />
diff --git a/vector/src/main/res/layout/item_search_result.xml b/vector/src/main/res/layout/item_search_result.xml
index 3264a7d230..6f6528c93b 100644
--- a/vector/src/main/res/layout/item_search_result.xml
+++ b/vector/src/main/res/layout/item_search_result.xml
@@ -63,7 +63,7 @@
tools:text="@sample/messages.json/data/message" />
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/vector/src/main/res/layout/item_thread.xml b/vector/src/main/res/layout/item_thread.xml
index 921f0663b1..9199a72628 100644
--- a/vector/src/main/res/layout/item_thread.xml
+++ b/vector/src/main/res/layout/item_thread.xml
@@ -92,7 +92,7 @@
android:maxWidth="496dp"
android:minWidth="144dp"
android:paddingTop="8dp"
- android:paddingBottom="8dp"
+ android:paddingBottom="12dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/threadSummaryTitleTextView"
app:layout_constraintTop_toBottomOf="@id/threadSummaryRootMessageTextView"
@@ -108,4 +108,4 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/threadSummaryConstraintLayout"
app:layout_constraintTop_toBottomOf="@id/threadSummaryConstraintLayout" />
-
\ No newline at end of file
+
diff --git a/vector/src/main/res/layout/item_timeline_event_live_location_inactive_stub.xml b/vector/src/main/res/layout/item_timeline_event_live_location_inactive_stub.xml
new file mode 100644
index 0000000000..d5a0cefb28
--- /dev/null
+++ b/vector/src/main/res/layout/item_timeline_event_live_location_inactive_stub.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/item_timeline_event_live_location_start_stub.xml b/vector/src/main/res/layout/item_timeline_event_live_location_start_stub.xml
index b81a6cc0e9..1726928721 100644
--- a/vector/src/main/res/layout/item_timeline_event_live_location_start_stub.xml
+++ b/vector/src/main/res/layout/item_timeline_event_live_location_start_stub.xml
@@ -19,8 +19,8 @@
android:id="@+id/locationLiveStartBanner"
android:layout_width="0dp"
android:layout_height="48dp"
- android:alpha="0.85"
- android:src="?colorSurface"
+ android:alpha="0.75"
+ android:src="?android:colorBackground"
app:layout_constraintBottom_toBottomOf="@id/locationLiveStartMap"
app:layout_constraintEnd_toEndOf="@id/locationLiveStartMap"
app:layout_constraintStart_toStartOf="@id/locationLiveStartMap"
@@ -28,9 +28,10 @@
+ app:layout_constraintTop_toTopOf="@id/staticMapImageView"
+ app:layout_constraintVertical_bias="1.0" />
+
+
+
+
diff --git a/vector/src/main/res/layout/item_timeline_event_view_stubs_container.xml b/vector/src/main/res/layout/item_timeline_event_view_stubs_container.xml
index 355d5fa7fe..0d45a48b9b 100644
--- a/vector/src/main/res/layout/item_timeline_event_view_stubs_container.xml
+++ b/vector/src/main/res/layout/item_timeline_event_view_stubs_container.xml
@@ -59,12 +59,24 @@
android:layout_height="wrap_content"
android:layout="@layout/item_timeline_event_location_stub" />
+
+
+
+
diff --git a/vector/src/main/res/layout/typing_message_layout.xml b/vector/src/main/res/layout/typing_message_layout.xml
index c8b334c628..a4a2cd3305 100644
--- a/vector/src/main/res/layout/typing_message_layout.xml
+++ b/vector/src/main/res/layout/typing_message_layout.xml
@@ -1,38 +1,42 @@
+ android:layout_height="wrap_content"
+ android:paddingTop="2dp">
+
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toEndOf="@id/typingUserAvatars"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:text="@sample/users.json/data/displayName" />
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toEndOf="@id/typingUserText"
+ app:layout_constraintTop_toTopOf="parent" />
-
\ No newline at end of file
+
diff --git a/vector/src/main/res/layout/view_location_live_message_banner.xml b/vector/src/main/res/layout/view_location_live_message_banner.xml
new file mode 100644
index 0000000000..35924541d1
--- /dev/null
+++ b/vector/src/main/res/layout/view_location_live_message_banner.xml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/view_thread_list_filter.xml b/vector/src/main/res/layout/view_thread_list_filter.xml
new file mode 100644
index 0000000000..7bdc994f43
--- /dev/null
+++ b/vector/src/main/res/layout/view_thread_list_filter.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/view_thread_room_summary.xml b/vector/src/main/res/layout/view_thread_room_summary.xml
index 6eeb62974d..e432f0fa35 100644
--- a/vector/src/main/res/layout/view_thread_room_summary.xml
+++ b/vector/src/main/res/layout/view_thread_room_summary.xml
@@ -21,7 +21,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
- android:minEms="1"
android:textColor="?vctr_content_secondary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/messageThreadSummaryImageView"
diff --git a/vector/src/main/res/menu/menu_space_directory.xml b/vector/src/main/res/menu/menu_space_directory.xml
index c95fb846af..395b271f1a 100644
--- a/vector/src/main/res/menu/menu_space_directory.xml
+++ b/vector/src/main/res/menu/menu_space_directory.xml
@@ -1,6 +1,16 @@
-
\ No newline at end of file
+
diff --git a/vector/src/main/res/values-ar/strings.xml b/vector/src/main/res/values-ar/strings.xml
index 1e619580f2..9e5e7025da 100644
--- a/vector/src/main/res/values-ar/strings.xml
+++ b/vector/src/main/res/values-ar/strings.xml
@@ -473,7 +473,6 @@
رجاءً افحص بريدك وانقر على الرابط في الرسالة. ما إن تفعل انقر ”تابع“.عنوان البريد مستخدم بالفعلرقم الهاتف مستخدم بالفعل
- الميلمعطّلمزعجلا ترسل من هذا الجهاز الرسائل المعمّاة إلى الأجهزة غير المؤكّدة
@@ -1139,4 +1138,4 @@
رابط صورتك الرمزيةاسمك العلنيافتح في المتصفح
-
\ No newline at end of file
+
diff --git a/vector/src/main/res/values-bg/strings.xml b/vector/src/main/res/values-bg/strings.xml
index 91e7635378..eff33e3abf 100644
--- a/vector/src/main/res/values-bg/strings.xml
+++ b/vector/src/main/res/values-bg/strings.xml
@@ -390,7 +390,6 @@
\n
\nИмайте предвид, че това действие ще рестартира приложението, което може да отнеме известно време.Избор на държава
- Значка3 дни1 седмица1 месец
@@ -1773,4 +1772,4 @@
• Сървърите съдържащи %s са блокирани.Настроихте сървърните разрешения (ACL) за тази стая.%s настрой разрешенията (ACL) за тази стая.
-
\ No newline at end of file
+
diff --git a/vector/src/main/res/values-bn-rBD/strings.xml b/vector/src/main/res/values-bn-rBD/strings.xml
index e1a36b5e28..5913cb5e11 100644
--- a/vector/src/main/res/values-bn-rBD/strings.xml
+++ b/vector/src/main/res/values-bn-rBD/strings.xml
@@ -472,7 +472,6 @@
১ মাস১ সপ্তা৩ দিন
- ফ্লেয়ারশাটার শব্দ চালানচয়নডিফল্ট মিডিয়া উৎস
@@ -873,4 +872,4 @@
%1$s কক্ষটি তৈরি করেছেনআপনার আমন্ত্রণ%s এর আমন্ত্রণ
-
\ No newline at end of file
+
diff --git a/vector/src/main/res/values-bn-rIN/strings.xml b/vector/src/main/res/values-bn-rIN/strings.xml
index 9de30c448a..18f122b2cd 100644
--- a/vector/src/main/res/values-bn-rIN/strings.xml
+++ b/vector/src/main/res/values-bn-rIN/strings.xml
@@ -661,8 +661,6 @@
চয়নশাটার শব্দ চালান
- ফ্লেয়ার
-
৩ দিন১ সপ্তা১ মাস
diff --git a/vector/src/main/res/values-ca/strings.xml b/vector/src/main/res/values-ca/strings.xml
index 833a0c6404..157396781a 100644
--- a/vector/src/main/res/values-ca/strings.xml
+++ b/vector/src/main/res/values-ca/strings.xml
@@ -20,11 +20,11 @@
%1$s ha tret el vet a %2$s%1$s ha vetat %2$s%1$s ha retirat la invitació de %2$s
- %1$s ha canviat la seva icona
+ %1$s ha canviat la seva foto%1$s ha establert la visibilitat de l\'històric futur de la sala a %2$stots els participants de la sala, des de que s\'hi uneixen.qualsevol.
- (també ha canviat la icona)
+ (també ha canviat la foto)%1$s ha eliminat el nom de la sala%1$s ha eliminat el tema de la sala%1$s ha enviat una invitació a %2$s perquè s\'uneixi a la sala
@@ -39,7 +39,6 @@
Convida a la sala%1$s i %2$sSala buida
-
%s s\'ha actualitzat aquí.Ho has actualitzat aquí.Has activat el xifrat d\'extrem a extrem (algorisme %1$s no reconegut).
@@ -116,7 +115,7 @@
Sincronització inicial:
\nImportant comunitatsSincronització inicial:
-\nImportant sales que deixat
+\nImportant sales que n\'has marxatSincronització inicial:
\nImportant compte…Sincronització inicial:
@@ -124,9 +123,10 @@
Sincronització inicial:
\nImportant salesSincronització inicial:
-\nImportant sales on hi estàs convidat
+\nImportant sales que se t\'ha convidatSincronització inicial:
-\nImportant sales on hi estàs unit
+\nCarregant les teves converses
+\nSi t\'has unit a moles sales, això pot tardar una estona%1$s de %2$s a %3$s%1$s ha canviat el nivell d\'autoritat de %2$s.Has canviat el nivell d\'autoritat de %1$s.
@@ -145,8 +145,8 @@
Has convidat a %1$s%1$s ha convidat a %2$sHas enviat una invitació a %1$s perquè s\'uneixi a la sala
- Has eliminat la icona de la sala
- %1$s ha eliminat la icona de la sala
+ Has eliminat la foto de la sala
+ %1$s ha eliminat la foto de la salaHas eliminat el tema de la salaHas eliminat el nom de la salaHas actualitzat aquesta sala.
@@ -161,13 +161,13 @@
Has realitzat una trucada de veu.Has realitzat una videotrucada.Has canviat el nom de la sala a: %1$s
- Has canviat la icona de la sala
- %1$s ha canviat la icona de la sala
+ Has canviat la foto de la sala
+ %1$s ha canviat la foto de la salaHas canviat el tema a: %1$sHas eliminat el teu àlies (era %1$s)Has canviat el teu àlies de %1$s a %2$sHas canviat el teu àlies a %1$s
- Has canviat la teva icona
+ Has canviat la teva fotoHas retirat la invitació de %1$sHas tret el vet a %1$sHas rebutjat la invitació
@@ -180,16 +180,16 @@
Has creat la sala%1$s ha creat la salaLa teva invitació
- • Servidors coincidents amb literals IP ara estan vetats.
- • Servidors coincidents amb literals IP ara estan permesos.
- • Servidors coincidents amb %s han estat eliminats de la llista de permesos.
- • Servidors coincidents amb %s ara estan permesos.
- • Servidors coincidents amb %s han estat eliminats del llista de vetats.
- • Servidors coincidents amb %s ara estan vetats.
- • Servidors coincidents amb literals IP estan vetats.
- • Servidors coincidents amb literals IP estan permesos.
+ • Els servidors coincidents amb literals IP ara estan vetats.
+ • Els servidors coincidents amb literals IP ara estan permesos.
+ • Els servidors coincidents amb %s s\'han eliminat de la llista de permesos.
+ • Els servidors coincidents amb %s ara estan permesos.
+ • Els servidors coincidents amb %s s\'han tret del llista de vetats.
+ • Els servidors coincidents amb %s ara estan vetats.
+ • Els servidors coincidents amb literals IP estan vetats.
+ • Els servidors coincidents amb literals IP estan permesos.• Servidors coincidents amb %s estan permesos.
- • Servidors coincidents amb %s estan vetats.
+ • Els servidors coincidents amb %s estan vetats.Has canviat les adreces d\'aquesta sala.%1$s ha canviat les adreces d\'aquesta sala.Has canviat l\'adreça principal i les adreces alternatives d\'aquesta sala.
@@ -247,7 +247,6 @@
EliminaCanvia el nomInforma del contingut
-
oConvidaTanca la sessió
@@ -270,7 +269,6 @@
Només contactes de MatrixSense resultatsSales
-
ComunitatsEnvia els registresEnvia els registres de fallada
@@ -303,11 +301,9 @@
"El correu electrònic no és vàlid"Aquest correu electrònic ja existeix.Contrasenya oblidada\?
-
- Aquest servidor es vol assegurar que no sou un robot
+ Aquest servidor vol assegurar-se que no ets cap robotHeu d\'introduir el correu electrònic associat al vostre compte.No s\'ha pogut verificar l\'adreça del correu electrònic: assegureu-vos que heu fet clic a l\'enllaç del correu electrònic
-
Introduïu una URL vàlidaJSON mal formatNo contenia un JSON vàlid
@@ -324,14 +320,10 @@
Trucada en curs…No s\'està responent a la trucada.Informació
-
-
Per tal de fer trucades de veu, ${app_name} necessita permís per accedir al micròfon.
-
Per tal de fer videotrucades, ${app_name} necessita permís per accedir a la càmera i al micròfon.
\n
\nPermet-li l\'accés en la següent finestra emergent per tal de poder fer la trucada.
-
SÍNOContinua
@@ -339,7 +331,6 @@
Uneix-teRebutjaVés fins al primer no llegit
-
Marxa de la salaEstàs segur que vols marxar de la sala\?Xats personals
@@ -351,7 +342,7 @@
MencionaNo podràs desfer aquest canvi ja que estàs donant a l\'usuari el mateix nivell d\'autoritat que el teu.
\nN\'estàs segur\?
- Si vetes un usuari, se l\'expulsarà d\'aquesta sala i no podrà tornar a unir-s\'hi.
+ Si vetes un usuari, se l\'expulsarà de la sala i no s\'hi podrà tornar a unir.%s està escrivint…%1$s & %2$s estan escrivint…%1$s & %2$s & altres estan escrivint…
@@ -367,7 +358,6 @@
El certificat ha canviat respecte aquell en el qual el telefon confia. Això NO ÉS GENS HABITUAL. Es recomana que NO ACCEPTEU el certificat nou.El certificat en el que confiàveu ha canviat per un en el que no confieu. El servidor pot haver renovat el certificat. Contacteu amb l\'administrador del servidor per saber l\'empremta digital esperada.Només accepteu el certificat si l\'administrador del servidor ha publicat una empremta digital que coincideixi amb l\'anterior.
-
CercaFiltra els participants de la salaNo hi ha resultats
@@ -439,11 +429,8 @@
Contrasenya novaNo s\'ha pogut actualitzar la contrasenyaLa contrasenya s\'ha actualitzat
- Mostra tots els missatges de %s\?
-\n
-\nTingues en compte que aquesta acció reiniciarà l\'aplicació i pot trigar una estona.
+ Mostrar tots els missatges de %s\?Escull un país
- InsígniaTres diesUna setmanaUn mes
@@ -462,7 +449,6 @@
Aquestes característiques són experimentals i poden fallar de forma inesperada. Useu amb precaució.Estableix com a adreça principalTreu com a adreça principal
-
TemaError al desxifrarNom públic
@@ -474,7 +460,6 @@
ExportaIntroduïu una contrasenyaConfirmeu la contrasenya
-
Importa les claus E2E de la salaImporta les claus de la salaImporta les claus de la sala des d\'un fitxer local
@@ -486,9 +471,8 @@
VerificaVerifica comparant el següent amb la configuració d\'usuari de la teva altra sessió:Si no coincideixen pot ser que la seguretat de la comunicació estigui compromesa.
-
Tria un directori de sales
- URL del servidor base
+ URL del servidorTotes les sales del servidor %sTotes les sales natives de %sMida de la font
@@ -528,8 +512,7 @@
Carregant…Tots els missatgesSacseja el dispositiu amb ràbia per informar d\'un error
- Llista de membres
-
+ Membres%d membre%d membres
@@ -538,13 +521,10 @@
%d missatge nou%d missatges nous
-
-
%d sala%d sales
-
%d canvi de membres%d canvis de membres
@@ -571,10 +551,6 @@
Encara no tens cap paquet d\'adhesius activat.
\n
\nEn vols afegir algun\?
-
-
-
-
%d seleccionat%d seleccionats
@@ -594,16 +570,14 @@
Defineix el nivell d\'autoritat d\'un usuariBaixa el nivell d\'autoritat de l\'usuari amb l\'ID proporcionatConvida a la sala actual l\'usuari amb l\'ID proporcionat
- Entra a la sala amb l\'àlies donat
+ T\'uneix a sales amb l\'adreça corresponentMarxa de la salaDefineix el tema de la salaExpulsa l\'usuari amb l\'ID proporcionatCanvia l\'àliesActiva/Desactiva el markdownArreglar la gestió de les Apps de Matrix
-
-
- Icona
+ FotoPer poder continuar utilitzant el servidor local %1$s has de revisar i acceptar els termes i condicions.Revisa araDesactiva el compte
@@ -640,7 +614,7 @@
Feu saber a altres usuaris que esteu escrivint.Format en MarkdownDóna format a missatges utilitzant la sintaxi Markdown abans d\'enviar-los. Això et permet l\'ús de format avançat com ara l\'ús d\'asteriscs per mostrar un text en cursiva.
- No afecta invitacions, expulsions i bloquejos.
+ No afecta a invitacions, expulsions i vetos.Mostra els esdeveniments del compteReviseu i accepteu les polítiques d\'aquest servidor base:Videotrucada en procés…
@@ -667,8 +641,7 @@
Optimització de bateriaEl ${app_name} no està afectat per l\'optimització de bateria.Mostra els esdeveniments d\'unió i sortida
- Inclou els canvis d\'icona i d\'àlies.
-
+ Inclou els canvis de fotos i d\'àlies.ContrasenyaS\'ha habilitat el Markdown.S\'ha inhabilitat el Markdown.
@@ -702,8 +675,8 @@
\nAquest error està fora del control d\'${app_name} i, segons Google, aquest error indica que aquest dispositiu té massa aplicacions registrades amb FCM. L\'error només ocorre en casos en què hi ha un nombre extrem d\'aplicacions i, per tant, no hauria d\'afectar els usuari normals.Afegeix un compteRegistre de token
- El token FCM s\'ha registrat correctament al servidor local.
- No s\'ha pogut registrar el token FCM al servidor local:
+ El token FCM s\'ha registrat correctament al servidor.
+ No s\'ha pogut registrar el token FCM al servidor:
\n%1$sEl servei s\'iniciarà quan es reiniciï el dispositiu.El servei no s\'iniciarà quan el dispositiu es reiniciï, per tant, no rebràs notificacions fins que ${app_name} no s\'hagi obert per primera vegada.
@@ -732,7 +705,6 @@
FetDesa la clau de recuperacióDesa com a un fitxer
-
Feu una còpiaComparteix la clau de recuperació amb…Clau de recuperació
@@ -831,7 +803,6 @@
S\'estan baixant les claus…S\'estan important les claus…Per utilitzar la còpia de seguretat de clau en aquesta sessió, recupera amb la frase o la clau de recuperació, ara.
-
IgnoraInicia sessió amb la inscripció única (SSO)Envia missatges amb retorn
@@ -872,16 +843,13 @@
CompartirSol·licituds d\'intercanvi de clausIgnorar
- Ja existeix una còpia de seguretat al teu servidor local
+ Ja existeix una còpia de seguretat al teu servidorSembla que ja has configurat una còpia de seguretat de claus des d\'una altra sessió. Vols reemplaçar-la amb la que estàs creant\?ReemplaçarAturarComprovant l\'estat de la còpia de seguretat
-
Verificat!Entesos
-
-
Sol·licitud de verificació%s vol verificar la teva sessióError desconegut
@@ -961,7 +929,7 @@
\n%sNo hi ha ginys actius%1$s s %2$s i %3$s
- Si deixes d\'ignorar aquest usuari, tornaràs a veure tots els seus missatges.
+ Si deixes d\'ignorar aquest usuari, es tornaran a veure tots els seus missatges.Deixa d\'ignorarSi ignores aquest usuari s\'eliminaran els seus missatges de les sales que compartiu.
\n
@@ -1157,14 +1125,13 @@
Utilitza la comanda /confetti o envia un missatge amb ❄️ or 🎉Mostra els efectes del xatUtilitza bots, enllaços, ginys i paquets d\'adhesius
- Utilitza els gestors d\'integracions per a gestionar bots, enllaços, ginys i paquets d\'adhesius.
+ Utilitza un gestor d\'integracions per a configurar bots, enllaços, ginys i paquets d\'adhesius.
\nEls gestors d\'integracions reben dades de configuració i poden modificar ginys, enviar invitacions a sales i configurar els nivells d\'autoritat per tu.Integracions%d segon%d segons
-
No se\'t notificarà de missatges entrants quan l\'aplicació es trobi en segon pla.Sense sincronització en segon pla${app_name} farà la sincronització en segon pla periòdicament en instants de temps precisos (configurable).
@@ -1211,7 +1178,7 @@
Canviar la visibilitat de l\'històricActivar el xifrat de la salaCanviar l\'adreça principal de la sala
- Canviar la icona de la sala
+ Canvia la foto de la salaModificar ginysNotificar a tothomEliminar missatges enviats per altres
@@ -1232,7 +1199,6 @@
Aquesta sala no és pública. No podràs tornar-t\'hi a unir sense una invitació.No s\'ha pogut establir una connexió en temps real.
\nDemana a l\'administrador del servidor local que configuri un servidor TURN perquè les trucades funcionin correctament.
-
Xifrat activatLes notificacions estan desactivadesSe t\'ha desconnectat de totes les teves sessions i no rebràs més notificacions. Per reactivar les notificacions, torna a iniciar sessió a cada dispositiu.
@@ -1302,7 +1268,6 @@
No s\'ha pogut trobar un servidor local vàlid. Comprova l\'identificadorAccepta els termes de servei del servidor d\'identitat (%s) per poder ser trobat mitjançant l\'adreça de correu electrònic o el número de telèfon.Introdueix l\'URL d\'un servidor d\'identitat
-
Has donat el teu consentiment per poder enviar correus electrònics i números de telèfon a aquest servidor d\'identitat per trobar altres usuaris dels teus contactes.Números de telèfon perquè et trobinSi et desconnectes del servidor d\'identitat no podràs ser trobat per altres usuaris ni convidar-los mitjançant el correu electrònic o el número de telèfon.
@@ -1371,7 +1336,6 @@
Enviar esdeveniments m.room.server_aclCanviar els permisosCanviar el nom de la sala
-
Aquest contingut ha estat com a inadequat.
\n
\nSi no vols veure cap més contingut d\'aquest usuari pots ignorar-lo per ocultar els seus missatges.
@@ -1420,8 +1384,8 @@
Treu el vet a l\'usuariapp_display_name:El teu àlies
- Estableix la icona
- URL de la icona
+ Estableix la foto
+ URL de la teva fotoRolDefineix rolPersonalitzat (%1$d) a %2$s
@@ -1503,7 +1467,7 @@
S\'ha perdut la connexió amb el servidorNoSí
- Quasi bé ja has acabat! %s mostra el mateix escut\?
+ Quasi bé ja has acabat! %s mostra un símbol de verificació\?Sol·licituds de clausFallada ràpidaSi actives aquesta opció, s\'afegirà el FLAG_SECURE a totes les activitats de l\'aplicació. Reinicia l\'aplicació per aplicar els canvis.
@@ -1532,7 +1496,7 @@
Clau de missatgeFrase de recuperacióVerificació cancel·lada
- Verifica els teus dispositius des de la configuració.
+ S\'ha cancel·lat la verificació. Pots iniciar-la una altra vegada.Alguna de les següents pot haver estat compromesa:
\n
\n- La teva contrasenya
@@ -1669,9 +1633,9 @@
Comparteix aquest codi amb la gent perquè puguin escanejar-lo, afegir-te i començar a xatejar amb tu.L\'usuari no s\'ha acceptat el consentiment.Envia el missatge proporcionat amb confetis
- Envia el missatge proporcionat amb neu
+ Envia el missatge proporcionat nevantenvia confetis 🎉
- envia neu ❄️
+ envia una nevada❄️El meu codiComparteix el meu codiEscaneja un codi QR
@@ -1725,7 +1689,7 @@
No s\'han pogut importar les clausEsperant %s…Ja gairebé has acabat! Esperant la confirmació…
- Ja gairebé has acabat! Veus el mateix escut a l\'altre dispositiu\?
+ Ja gairebé has acabat! Veus el mateix símbol de verificació a l\'altre dispositiu\?T\'hi has unit.%s s\'ha unit.Has creat i configurat la sala.
@@ -1735,7 +1699,6 @@
Això pot tardar uns segons, un moment, si us plau.%1$s (%2$s) ha entrat amb una nova sessió:No és de confiança
-
Ha fallat l\'autenticacióAtenció! Últim intent disponible abans de tancar la sessió!
@@ -1900,7 +1863,6 @@
Si t\'atures ara, pot ser que perdis dades i missatges xifrats en cas de que perdis l\'accés a les teves sessions.
\n
\nTambé pots configurar una copia de seguretat de seguretat i gestionar les teves claus a la configuració.
-
La teva %2$s i %1$s s\'han configurat.
\n
\nGuarda-les en un lloc segur! Les necessitaràs per desbloquejar missatges xifrats i informació protegida en cas de que perdis l\'accés a totes les teves sessions actives.
@@ -1912,8 +1874,6 @@
TransferènciaConnectaConsulta primer
-
-
Trucada activa (%1$s)S\'ha produït un error al cercar el número de telèfonTeclat
@@ -1975,9 +1935,9 @@
Nivell de confiança d\'alertaNivell de confiança predeterminatSeleccionats
- La sala conté un esborrany no enviat
- Elimina la icona
- Canvia la icona
+ conté un esborrany no enviat
+ Elimina la foto
+ Canvia la fotoEl servidor local accepta fitxers adjunts (fotos, fitxers, etc) de fins a una mida de %s.Directori de salesMostra totes les sales al directori de sales, incloses aquelles amb contingut explícit.
@@ -1999,4 +1959,96 @@
Qualsevol pot demanar entrar a la sala, els participants poden acceptar-ho o rebutjar-hoPermet als convidats unir-seSales suggerides
-
\ No newline at end of file
+ Activa les notificacions per correu de %s
+ Notificacions per correu
+ Mostra tots els fils en que has participat
+ Els meus fils
+ Mostra tots els fils de la sala actual
+ Tots els fils
+ Filtra
+ Fils
+ Fil
+ Actualitza l\'espai
+ Canvia el nom de l\'espài
+ Activa els xifrat de l\'espai
+ Crea espai
+ Descripció
+ Creant espai…
+ Aleatori
+ General
+ Crea un espai
+ Jo i els meus col·legues
+ Només jo
+ Amb qui estàs treballant\?
+ Per unir-te a un espai, necessites una invitació.
+ Pots canviar-ho més tard
+ Quin tipus d\'espai vols crear\?
+ Crea un espai
+ Creant espai…
+ Adreça de l\'espai
+ Crea un nou espai
+ Els espais són una nova manera d\'agrupar sales i gent.
+ Canvia la teva foto només en la sala actual
+ Canvia la foto de la sala actual
+ Canvia la foto de l\'espai
+ Selecciona els rols necessaris per modificar diferents parts d\'aquest espai
+ Visualitza i actualitza els rols necessaris per modificar diferents parts de l\'espai.
+ Permisos de l\'espai
+ El xifrat no s\'ha configurat correctament per tant no pots enviar missatges. Clica per obrir la configuració.
+ El xifrat no s\'ha configurat correctament per tant no pots enviar missatges. Contacta amb l\'administrador o restableix el xifrat a un estat vàlid.
+ %1$s, %2$s i altres
+ %1$s i %2$s
+ Si deixes de vetar un usuari, es podrà tornar a unir a l\'espai.
+ Si vetes un usuari, se l\'expulsarà de l\'espai i no s\'hi podrà tornar a unir.
+ L\'usuari s\'eliminarà d\'aquest espai.
+\n
+\nPer evitar que tornin a unir-se, els hauries de vetar.
+ Trucant…
+ Finalitzant trucada…
+ Sense resposta
+ L\'usuari que has trucat està ocupat.
+ Usuari ocupat
+ Trucada amb %s
+ Videotrucada amb %s
+
+ Videotrucada perduda
+ %d videotrucades perdudes
+
+
+ Trucada perduda
+ %d trucades perdudes
+
+ Tria el servidor
+ No s\'ha pogut arribar a cap servidor a l\'URL %s. Comprova l\'enllaç o escull un servidor manualment.
+ Utilitza com a predeterminat i no ho preguntis més
+ Pregunta sempre
+ Deixa de compartir pantalla
+ Comparteix pantalla
+ URL de l\'API del servidor
+ Espais
+ Copia l\'enllaç al fil
+ Mostra a la sala
+ Més informació
+ Provar-ho
+ Ara, no
+ Desactiva
+ Activa
+ Mostra els fils
+ Falten permisos
+ Per enviar missatges de veu, concedeix el permís per poder utilitzar el micròfon.
+ Per dur a terme aquesta acció, concedeix el permís de càmera des de la configuració del sistema.
+ Falten alguns permisos per dur a terme aquesta acció, concedeix-los des de la configuració del sistema.
+ Espais
+ Escoltant notificacions
+ - Alguns usuaris ja no s\'ignoren
+ ${app_name} ha d\'esborrar la memòria cau per actualitzar-se, pel motiu següent:
+\n%s
+\n
+\nTingues en compte que aquesta acció reiniciarà l\'aplicació i pot tardar una estona.
+ Sol·licitud de sincronització inicial
+ No tens permís per unir-te a aquesta sala
+
+ %d canvi a l\'ACL del servidor
+ %d canvis a l\'ACL del servidor
+
+
diff --git a/vector/src/main/res/values-cs/strings.xml b/vector/src/main/res/values-cs/strings.xml
index 61be21b7bf..1ecc1c26f6 100644
--- a/vector/src/main/res/values-cs/strings.xml
+++ b/vector/src/main/res/values-cs/strings.xml
@@ -610,9 +610,7 @@
Aktualizace hesla se nezdařilaHeslo není správnéVaše heslo bylo aktualizováno
- Ukázat všechny zprávy od %s\?
-\n
-\nPřipomínám, že tato akce restartuje aplikaci a může chvíli trvat.
+ Ukázat všechny zprávy od %s\?Vybrat zemiMédiaVýchozí komprese
@@ -620,7 +618,6 @@
Výchozí zdroj médiíVybratPřehrát zvuk uzávěrky
- Příslušnost ke skupinám3 dny1 týden1 měsíc
@@ -1566,9 +1563,7 @@
\n
\nMůžete tuto akci kdykoli zvrátit v obecných nastaveních.Odignorovat uživatele
- Zrušení ignorování tohoto uživatele opět ukáže všechny jejich zprávy.
-\n
-\nTato akce povede k restartování aplikace a může trvat nějakou dobu.
+ Zrušení ignorování tohoto uživatele opět ukáže všechny jejich zprávy.Zrušit pozvánkuJste si jisti, že chcete zrušit pozvánku tomuto uživateli\?Vykopnout uživatele
@@ -2395,8 +2390,6 @@
Otevřít v${app_name} nemohl získat přístup k vaší poloze. Zkuste to prosím později.${app_name} nemohl získat přístup k vaší poloze
- Sdílet polohu
- Sdílet polohuPolohaSdílet polohuVýsledky se zobrazí až po ukončení hlasování
@@ -2534,4 +2527,14 @@
Vlákna BetaZjistit víceVyzkoušet to
-
\ No newline at end of file
+ Probíhá sdílení obrazovky
+ Sdílení obrazovky aplikace ${app_name}
+ Zastavit sdílení obrazovky
+ Sdílet obrazovku
+ - Některý uživatelům bylo zrušeno ignorování
+ ${app_name} potřebuje vymazat mezipaměť, aby byla aktuální, a to z následujícího důvodu:
+\n%s
+\n
+\nTato akce povede k restartování aplikace a může trvat nějakou dobu.
+ Požadavek na počáteční synchronizaci
+
diff --git a/vector/src/main/res/values-de/strings.xml b/vector/src/main/res/values-de/strings.xml
index d463276bb1..9796c7bef6 100644
--- a/vector/src/main/res/values-de/strings.xml
+++ b/vector/src/main/res/values-de/strings.xml
@@ -403,9 +403,7 @@
Neues PasswortÄndern des Passworts fehlgeschlagenDein Passwort wurde aktualisiert
- Alle Nachrichten von %s anzeigen\?
-\n
-\nBeachte: Diese Aktion wird die App neu starten und einige Zeit brauchen.
+ Alle Nachrichten von %s anzeigen\?Wähle ein LandThemaLesbarkeit des Chatverlaufs
@@ -516,9 +514,8 @@
Du wurdest von %2$s aus %1$s verbanntGrund: %1$sZum Startbildschirm hinzufügen
- Community-AvatareSchütteln, um einen Fehler zu melden
- Mitglieder auflisten
+ Mitglieder%d Mitglied%d Mitglieder
@@ -1983,7 +1980,7 @@
Verlasse den Raum mit der angegebenen ID (oder den aktuellen Raum, wenn keine ID angegeben wird)Name suchenDu wurdest eingeladen
- Space verlassen
+ VerlassenRäume hinzufügenRäume erkundenTrotzdem beitreten
@@ -2342,8 +2339,6 @@
Verschlüsselung wiederherstellenStandort freigeben
- Standort freigeben
- Standort freigebenStandort freigebenStandortfreigabe aktivierenÖffnen mit
@@ -2380,7 +2375,7 @@
Organisiere Diskussionen mit ThreadsThreads im Raum filternMöchtest du einem existierenden Server beitreten\?
- Gemeinschaften
+ CommunitiesTeamsWir helfen dir, in Verbindung zu kommen.Mit wem wirst du am meisten chatten\?
@@ -2429,4 +2424,11 @@
Live-Standort teilenThreads nähern sich der Beta 🎉Deaktivieren
-
\ No newline at end of file
+ BETA
+ Feedback geben
+ BETA
+ Threads Beta
+ Threads Beta
+ Bildschirm teilen
+ Ausprobieren
+
diff --git a/vector/src/main/res/values-eo/strings.xml b/vector/src/main/res/values-eo/strings.xml
index cf61116477..466d0b2e93 100644
--- a/vector/src/main/res/values-eo/strings.xml
+++ b/vector/src/main/res/values-eo/strings.xml
@@ -37,7 +37,6 @@
TelefonnumeroInvito al ĉambro%1$s kaj %2$s
-
Malplena ĉambroKomenca spegulado:
\nEnportante konton…
@@ -46,7 +45,8 @@
Komenca spegulado:
\nEnportante ĉambrojnKomenca spegulado:
-\nEnportante aliĝitajn ĉambrojn
+\nEnportante viajn konversaciojn
+\nSe vi aliĝis tre multaj ĉambroj, tiu eble daŭras longeKomenca spegulado:
\nEnportante ĉambrojn de invitojKomenca spegulado:
@@ -204,9 +204,8 @@
Vidi malĉifritan fontonForigiAlinomi
- Raporti enhavon
+ Raporti EnhavonRaporti eraron
-
aŭInvitiVoĉvoko
@@ -252,7 +251,6 @@
InterparolojNeniuj rezultojĈambroj
-
KomunumojSendi protokolonSendi protokolon pri fiasko
@@ -273,7 +271,6 @@
Sendi voĉonĈu vi certe volas komenci novan voĉvokon\?Ĉu vi certe volas komenci novan vidvokon\?
-
Sendi dosierojnSendi glumarkonFoti aŭ filmi
@@ -289,11 +286,9 @@
Ĉi tio ne ŝajnas esti valida retpoŝtadresoĈi tiu retpoŝtadreso jam estas difinita.Ĉu vi forgesis pasvorton\?
-
Ĉi tiu hejmservilo volas certiĝi, ke vi ne estas robotoNecesas enigi la retpoŝtadreson ligitan al via konto.Malsukcesis kontroli retpoŝtadreson: certiĝu, ke vi klakis la ligilon en la retletero
-
Bonvolu tralegi kaj akcepti la politikojn de ĉi tiu hejmservilo:OriginalaGranda
@@ -304,11 +299,9 @@
%d elektita%d elektitaj
-
SerĉiFiltri ĉambranojnNeniuj rezultoj
-
Ĉiuj mesaĝojKiam mi estas invitita al ĉambroInvitoj al vokoj
@@ -325,7 +318,6 @@
Vi ne sciiĝos pri envenaj mesaĝoj dum la aplikaĵo estas fone.Ruliĝi je eko de sistemoTempolimo de petoj por spegulado
-
Prokrasto inter ĉiu speguladoVersioVersio de olm
@@ -398,14 +390,10 @@
Voko progresas…Vidvoko progresas…Informoj
-
-
${app_name} bezonas permeson aliri vian mikrofonon por fari voĉvokojn.
-
${app_name} bezonas premeson aliri viajn filmilon kaj mikrofonon por fari vidvokojn.
\n
\nBonvolu permesi aliron per la sekva ŝprucpeto, por ebligi la vokon.
-
JESNEDaŭrigi
@@ -413,15 +401,10 @@
AliĝiRifuziListigi ĉambranojn
-
%d ĉambrano%d ĉambranoj
-
-
-
-
Foriri de ĉambroĈu vi certe volas foriri de la ĉambro\?Inviti
@@ -496,7 +479,6 @@
\nViaj mesaĝoj estas sekurigitaj per seruroj, kaj nur vi kaj la adresato havas la unikajn ŝlosilojn por ilin malŝlosi.Mesaĝoj ĉi tie ne estas tutvoje ĉifrataj.Mesaĝoj en ĉi tiu ĉambro ne estas tutvoje ĉifrataj.
-
Atendante je %s…%s kontroliĝisKontroli %s
@@ -683,7 +665,6 @@
Ĉiuj mesaĝojĈiuj mesaĝoj (laŭte)Malatenti uzanton
-
Ĉi tiu enhavo estis raportita kiel maltaŭga.
\n
\nSe vi ne plu volas vidi enhavon de ĉi tiu uzanto, vi povas malatenti ĝin por kaŝi ĝiajn mesaĝojn.
@@ -843,8 +824,6 @@
Nekonata eraro%s volas kontroli vian salutaĵonKontrolpeto
-
-
KompreniteKontrolite!Subskribo
@@ -863,7 +842,6 @@
Neniam perdu ĉifritajn mesaĝojnMalhelpu perdon de aliro al ĉifritaj mesaĝoj kaj datumojSekura savkopio
-
Ĉu forigi viajn savkopiitajn ĉifrajn ŝlosilojn de la servilo\? Vi ne plu povos uzi vian rehavan ŝlosilon por legi historion de ĉifritaj mesaĝoj.Forigi savkopionKontrolante staton de savkopio
@@ -917,7 +895,6 @@
Ŝajnas, ke vi jam agordis savkopiadon de ŝlosiloj el alia salutaĵo. Ĉu vi volas anstataŭigi ĝin per tiu, kiun vi nun kreas\?Savkopio jam ekzistas en via hejmserviloLa rehava ŝlosilo estas konservita.
-
Konservi kiel dosieronHavigiKonservi rehavan ŝlosilon
@@ -975,8 +952,6 @@
Kialo: %1$s%2$s vin forbaris de %1$s%2$s vin forpelis de %1$s
-
-
Ŝanĝas vian prezentan nomonForpelas uzanton kun la donita identigiloAgordi temon por la ĉambro
@@ -1085,7 +1060,6 @@
%d ĉambro%d ĉambroj
-
%d nelegita mesaĝo sciigita%d nelegitaj mesaĝoj sciigitaj
@@ -1093,7 +1067,6 @@
Ĉiuj propraj ĉambroj de %sĈiuj ĉambroj de servilo %sNomo de servilo
-
Se ili ne akordas, la sekureco de via komunikado eble estas rompita.Konfirmu per komparo de la jeno kun la agordoj de uzanto en alia via salutaĵo:Kontroli
@@ -1109,7 +1082,6 @@
EnportiEnporti la ŝlosilojn el loka dosieroEnporti ŝlosilojn de ĉambroj
-
Enporti tutvoje ĉifrajn ŝlosilojn de ĉambrojAdministri savkopiadon de ŝlosilojRehavo de ĉifritaj mesaĝoj
@@ -1124,7 +1096,6 @@
Publika nomoEraris malĉifradoHaŭto
-
session_name:app_display_name:push_key:
@@ -1151,7 +1122,6 @@
1 monato1 semajno3 tagoj
- InsignoLudi sonon de fotkovriloElektiImplicita fonto de vidaŭdaĵoj
@@ -1185,7 +1155,6 @@
HejmserviloSalutinta kielAŭtentikigo
-
%1$s @ %2$sLastatempe viditaĜisdatigi publikan nomon
@@ -1194,7 +1163,6 @@
${app_name} kolektas sennomajn analizojn por helpi al ni plibonigi la aplikaĵon.Sendi datumojn de analizoAnalizo
-
Administri viajn agordojn de trovado.TrovadoMalŝalti mian konton
@@ -1333,7 +1301,6 @@
Aldoni retpoŝtadresonPrezenta nomoProfilbildo
-
Filtri forbaritajn uzantojnAkceptu la atestilon nur se administranto de la servilo publikigis fingrospuron akordan kun tiu ĉi-supre.La atestilo ŝanĝiĝis de antaŭe fidata al alia, nefidata. Eble la servilo renovigis sian atestilon. Kontaktu la administranton de la servilo por ricevi la ĝustan fingrospuron.
@@ -1364,7 +1331,6 @@
Ĉu malaltigi vian propran povnivelon\?Nuligi invitonIndividuaj ĉambroj
-
La alia flanko ne respondis la vokon.Voko finiĝisKonektante vokon…
@@ -1701,7 +1667,6 @@
Se vi nuligos nun, vi eble perdos ĉifritajn mesaĝojn kaj datumojn, se vi perdos aliron al viaj salutoj.
\n
\nVi povas agordi sekuran savkopiadon kaj administri viajn ŝlosilojn per la agordoj.
-
Kopiu ĝin al via persona fora deponejoKonservu ĝin en USB-memorilo aŭ savkopia diskoPresu ĝin kaj deponu ĝin en sekura loko
@@ -1730,7 +1695,6 @@
TemoTemo de ĉambro (malnepra)Nomo de ĉambro
-
Elektu la rolojn bezonatajn por ŝanĝi diversajn partojn de la ĉambroPermesojMontri kaj ĝisdatigi la rolojn bezonatajn por ŝanĝi diversajn partojn de la ĉambro.
@@ -1804,7 +1768,7 @@
Komenca spegulado:
\nElŝutante datumojn…
- Komenca spegulado:
+ Komenca Spegulado:
\nAtendante respondon de servilo…Malplena ĉambro (estis %s)
@@ -1833,8 +1797,6 @@
Eraris transdonado de vokoTransdonuUnue konsulti
-
-
Aktiva voko (%1$s)Eraris serĉado de la telefonnumeroCiferplato
@@ -1913,7 +1875,6 @@
Oni ne povas antaŭrigardi ĉi tiun ĉambron. Ĉu vi volas eniri\?Ĉi tiu ĉambro nun ne estas disponebla.
\nReprovu poste, aŭ petu administranton de ĉambro kontroli, ĉu vi rajtas aliri.
-
KonsentiNuligi mian konsentonVi konsentis sendi retpoŝtadresojn kaj telefonnumerojn al ĉi tiu identiga servilo por trovi aliajn uzantojn el viaj kontaktoj.
@@ -2220,7 +2181,6 @@
Sciigu min priVI ne ricevos sciigojn al poŝtelefono pri mencioj kaj ĉefvortoj en ĉifritaj ĉambroj.Ĉefvortoj
- Ĉefvortoj ne povas enhavi «%s»Ĉefvortoj ne povas eki per «.»Aldoni novan ĉefvorton
@@ -2252,4 +2212,5 @@
Vidvoko kun %sSonorante…Aroj
-
\ No newline at end of file
+ - Iom uzantoj reatentita
+
diff --git a/vector/src/main/res/values-es/strings.xml b/vector/src/main/res/values-es/strings.xml
index a4ab637dae..ba5a8dfc02 100644
--- a/vector/src/main/res/values-es/strings.xml
+++ b/vector/src/main/res/values-es/strings.xml
@@ -400,9 +400,7 @@
Contraseña nuevaNo se pudo actualizar la contraseñaTu contraseña ha sido actualizada
- ¿Mostrar todos los mensajes de %s\?
-\n
-\nTen en cuenta que esta acción reiniciará la aplicación y puede tomar algo de tiempo.
+ ¿Mostrar todos los mensajes de %s\?Elige un paísTemaLegibilidad del Historial de la Sala
@@ -506,7 +504,6 @@
Añadir a la Pantalla de InicioVistas previas de URL en líneaVibrar al mencionar un usuario
- InsigniaCrearInicioSalas
@@ -1306,9 +1303,7 @@
\n
\n Puede revertir esta acción en cualquier momento en la configuración general.Dejar de ignorar al usuario
- Si deja de ignorar a este usuario, se mostrarán todos sus mensajes nuevamente.
-\n
-\nTome nota de que esta acción reiniciará la aplicación y podría tardar algún tiempo.
+ Si ignora a este usuario, se mostrarán todos sus mensajes nuevamente.¿Estás seguro de que deseas cancelar la invitación de este usuario\?Patear usuariopatear al usuario los eliminará de esta sala.
@@ -2370,10 +2365,9 @@
Compartir esta ubicaciónCompartir ubicación en tiempo realCompartir mi ubicación actual
- Compartir ubicación${app_name} también es estupenda para el trabajo. Es confiada por las organizaciones más seguras del mundo.Para descubrir contactos ya existentes, tendrás que enviar información de contacto (emails y números de teléfono) a tu servidor de identidad. Aplicamos hashes en tus datos antes de enviarlos por privacidad.
- Los hilos son un trabajo en progreso con nuevas y excitantes características nuevas, como notificaciones mejoradas. ¡Nos encantaría escuchar tus comentarios!
+ Los hilos son un proyecto en progreso con nuevas y excitantes características nuevas, como notificaciones mejoradas. ¡Nos encantaría escuchar tus comentarios!Si se activa, siempre aparecerás como fuera de línea para otros usuarios, incluso cuando uses la aplicación.Los hilos ayudan a mantener tus conversaciones en el asunto y las hacen más fáciles de rastrear. %sHabilitar la función de hilos refrescará la aplicación. Esto podría tardar más en algunas cuentas.Notificación de sala
@@ -2386,8 +2380,8 @@
Abrir con${app_name} no ha podido acceder a tu ubicación. Por favor, inténtalo de nuevo más tarde.${app_name} no ha podido acceder a tu ubicación
- If te gustaría compartir tu ubicación en tiempo real, ${app_name} necesita permiso de ubicación siempre cuando la aplicación esté en segundo plano
-\nSolo acederemos a tu ubicación para la duración que escogas.
+ Si te gustaría compartir tu ubicación en tiempo real, ${app_name} necesita permiso de ubicación siempre y cuando la aplicación esté en segundo plano.
+\nSolo acederemos a tu ubicación durante el tiempo que escogas.Compartir tu ubicación en directo paraCompartir esta ubicaciónCompartir ubicación en directo
@@ -2395,13 +2389,12 @@
Hacer zoom a ubicación actualPin de ubicación selecionada en el mapaMapa
- Compartir ubicaciónUbicaciónCompartir ubicaciónLos resultados solo se revelan cuando finalices la encuestaEncuesta cerradaLos votantes verán los resultados tan pronto como hayan votado
- Abrir encuesta
+ Encuesta abiertaTipo de encuestaEditar encuesta¿Estás seguro de que quieres eliminar esta encuesta\? No podrás recuperarla cuando se elimine.
@@ -2483,4 +2476,14 @@
BETAComentarios de la beta de hilosBeta de hilos
-
\ No newline at end of file
+ - Algunos usuarios han sido dejados de ignorar
+ La compartición de pantalla está en progreso
+ ${app_name} Compartición de pantalla
+ Dejar de compartir pantalla
+ Compartir pantalla
+ ${app_name} necesita realizar una limpieza de caché para mantenerse actualizado por la siguiente razón:
+\n%s
+\n
+\nTen en cuenta que esta acción reiniciará la aplicación y podría tardar algo de tiempo.
+ Petición inicial de sincronización
+
diff --git a/vector/src/main/res/values-et/strings.xml b/vector/src/main/res/values-et/strings.xml
index 79eee81fa9..8aa1dfb09b 100644
--- a/vector/src/main/res/values-et/strings.xml
+++ b/vector/src/main/res/values-et/strings.xml
@@ -595,7 +595,6 @@
Vaikimisi meediaallikasValiTee katiku klõpsu
- Kogukonna rinnasilt3 päeva1 nädal1 kuu
@@ -743,9 +742,7 @@
\nÜldistest seadistustest saad seda alati muuta.Eira selle kasutaja sõnumeidLõpeta selle kasutaja eiramine
- Selle kasutaja eiramise lõpetamine teeb tema sõnumid uuesti nähtavaks.
-\n
-\nPalun arvesta, et samaga käivitud rakendub uuesti ning andmete sünkroniseerimiseks kulub natuke aega.
+ Selle kasutaja eiramise lõpetamine teeb tema sõnumid uuesti nähtavaks.Lõpeta eiramineTühista kutseKas oled kindel et sa soovid tühistada kutse sellele kasutajale\?
@@ -971,9 +968,7 @@
Salasõna uuendamine ei õnnestunudSalasõna ei ole sobilikSinu salasõna on muudetud
- Kas sa soovid näha kasutaja %s kõiki sõnumeid\?
-\n
-\nPane tähele, et antud toiming taaskäivitab rakenduse ja see võib võtta veidi aega.
+ Kas sa soovid näha kasutaja %s kõiki sõnumeid\?Vali riikEksporditavate võtmete krüptimiseks palun sisesta paroolifraas. Võtmete importimisel pead kasutama sama paroolifraasi.Võtmete eksportimine õnnestus
@@ -2350,8 +2345,6 @@
Ava muu rakendusega${app_name} ei saanud asukohta tuvastada. Palun proovi hiljem uuesti.${app_name} ei saanud asukohta tuvastada
- Jaga asukohta
- Jaga asukohtaAsukohtJaga asukohtaTulemusi kuvame vaid siis, kui küsitlus on lõppenud
@@ -2487,4 +2480,14 @@
Proovi nüüdJutulõngad on hetkel arendusjärgus funktsionaalsus ning samaga lisandub ka senisest parem versioon teavitustest. Me hea meelega tahaksime kuulda, mida sa nendest muutustest arvad!Jutulõngad aitavad hoida sinu vestlusi teemakohastena ning kergesti jälgitavatena.%sJutulõngade kasutusele võtmisel laadime rakenduse uuesti. Kui sul on väga mahukad kontod, siis võib natuke aega kuluda.
-
\ No newline at end of file
+ Ekraanijagamine on hetkel kasutusel
+ ${app_name} ekraanijagamine
+ Lõpeta ekraani jagamine
+ Jaga ekraani
+ - Sa oled lõpetanud mõnede kasutajate eiramise
+ ${app_name} peab andmete korrektsuse tagamiseks kustutama puhverdatud teabe. Toimingu põhjus on:
+\n%s
+\n
+\nPalun arvesta, et selle käigus rakendus käivitub uuesti ja see võib aega võtta.
+ Alusandmete sünkroniseerimise päring
+
diff --git a/vector/src/main/res/values-eu/strings.xml b/vector/src/main/res/values-eu/strings.xml
index 555b4780d4..81f0772111 100644
--- a/vector/src/main/res/values-eu/strings.xml
+++ b/vector/src/main/res/values-eu/strings.xml
@@ -534,7 +534,6 @@ Kontuan izan ekintza honek aplikazioa berrabiaraziko duela eta denbora bat behar
%1$s gelatik kanporatu zaitu %2$s erabiltzaileak%1$s gelatik debekatu zaitu %2$s erabiltzaileakArrazoia: %1$s
- IkurraAstindu amorruz akatsaren berri emateko
diff --git a/vector/src/main/res/values-fa/strings.xml b/vector/src/main/res/values-fa/strings.xml
index 4d01fe269b..171a145eb6 100644
--- a/vector/src/main/res/values-fa/strings.xml
+++ b/vector/src/main/res/values-fa/strings.xml
@@ -662,9 +662,7 @@
حسابم را غیرفعال کنکشفمدیریت تنظیمات کشفتان.
- نمایش همهٔ پیامها از %s؟
-\n
-\nبه خاطر داشته باشید این عمل، کاره را دوباره شروع خواهد کرد و ممکن است کمی زمان ببرد.
+ نمایش همهٔ پیامها از %s؟رایانامهها و شماره تلفنهامدیریت رایانامهها و شماره تلفنهای پیوسته به حساب ماتریکستان۳ روز
@@ -1256,9 +1254,7 @@
اگر مدیر کارساز خبر از مورد انتظار بودنش داده، مطمئن شوید که اثر انگشت زیر با اثر انگشت ارائه شده به دستشان مطابق است.میتواند به این معنی باشد که کسی شدامدتان را بدخواهانه دستکاری کرده یا تلفنتان، به گواهی فراهمشده به دست کارساز دوردست، اطمینان ندارد.با لغو مسدودیت، کاربر میتواند مجددا به اتاق اضافه شود.
- ناچشمپوشی این کاربر موجب نمایانی تمامی پیامها از سویش خواهد شد.
-\n
-\nبه خاطر داشته باشید که این کنش کاره را دوباره آغاز خواهد کرد و ممکن است مدّتی زمان ببرد.
+ ناچشمپوشی این کاربر موجب نمایانی تمامی پیامها از سویش خواهد شد.نادیدهگرفتن این کاربر پیامهایش را از اتاقهای مشترکتان حذف خواهد کرد.
\n
\nهرگاه که بخواهید میتوانید این کنش را در تنظیمات کلی لغو کنید.
@@ -1662,7 +1658,6 @@
کلیدها با موفقیت بر روی دستگاه استخراج شدندلطفاً یک رمز برای رمزنگاری کلیدها وارد کنید. ورود این رمز برای بارگذاری کلیدها ضروری خواهد بود.
- اجتماعپخش صدای شاترگزینشمنبع رسانهٔ پیشگزیده
@@ -2345,8 +2340,6 @@
همرسانی مکانبه کار انداختن همرسانی مکانگشودن با
- همرسانی مکان
- همرسانی مکانمکانهمرسانی مکاننظرسنجی بسته
@@ -2378,7 +2371,7 @@
شکست در بار کردن نقشهنقشهنکته: کاره دوباره آغاز خواهد شد
- به کار انداختن ویامهای رشتهای
+ به کار انداختن پیامهای رشتهایوصل شدن به کارسازدنبال پیوستن به کارسازهای موجودید؟از این پرسش بگذرید
@@ -2487,4 +2480,14 @@
رشتههای آزمایشیبیش تر بدانیدبیازماییدش
-
\ No newline at end of file
+ لازم است به این دلیل، ${app_name} انباره را برای بهروز بودن خالی کند:
+\n%s
+\n
+\nبه خاطر داشته باشید این عمل، کاره را دوباره شروع خواهد کرد و ممکن است کمی زمان ببرد.
+ همرسانی صفحه در حال پیشرفت است
+ همرسانی صفحهٔ ${app_name}
+ توقّف همرسانی صفحه
+ همرسانی صفحه
+ - برخی کاربران ناچشمپوشی شدهاند
+ درخواست همگام سازی نخستین
+
diff --git a/vector/src/main/res/values-fi/strings.xml b/vector/src/main/res/values-fi/strings.xml
index 9d084fa8ba..3a889ec0c9 100644
--- a/vector/src/main/res/values-fi/strings.xml
+++ b/vector/src/main/res/values-fi/strings.xml
@@ -455,7 +455,6 @@
Lisää aloitusruudulleLinkkien esikatseluVärise, kun käyttäjä mainitaan
- TyyliLuoKotiHuoneet
@@ -1957,7 +1956,6 @@
Ääni annettuKartta
- Jaa sijaintiSijaintiJaa sijaintiSuljettu kysely
@@ -1970,7 +1968,6 @@
Avaa kameraNäytä viestikuplatKartan lataaminen epäonnistui
- Jaa sijaintiLuo kyselyJaa sijaintiNäytä vähemmän
@@ -2103,4 +2100,4 @@
\n
\nLue käyttöehdot %s.Auta parantamaan ${app_name}iä
-
\ No newline at end of file
+
diff --git a/vector/src/main/res/values-fr-rCA/strings.xml b/vector/src/main/res/values-fr-rCA/strings.xml
index c54c7c6222..9a357e08a0 100644
--- a/vector/src/main/res/values-fr-rCA/strings.xml
+++ b/vector/src/main/res/values-fr-rCA/strings.xml
@@ -825,7 +825,6 @@
1 mois1 semaine3 jours
- BadgeJouer le son de l’obturateurChoisirSource de médias par défaut
@@ -2074,4 +2073,4 @@
Permissions manquantesPour envoyer des messages vocaux, veuillez accorder la permission Microphone.Pour effectuer cette action, veuillez autoriser la permission Caméra depuis les réglages système.
-
\ 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 ce69d574ba..fc41b217c1 100644
--- a/vector/src/main/res/values-fr/strings.xml
+++ b/vector/src/main/res/values-fr/strings.xml
@@ -491,7 +491,6 @@
%2$s vous a expulsé de %1$s%2$s vous a banni de %1$sMotif : %1$s
- BadgeSecouer avec agacement pour signaler une anomalie%d membre
@@ -2350,8 +2349,6 @@
Ouvrir avec${app_name} n\'a pas pu accéder à votre localisation. Veuillez réessayer plus tard.${app_name} n\'a pas pu accéder à votre localisation
- Partager la localisation
- Partager la localisationLocalisationPartager la localisationLes résultats ne sont dévoilés que lorsque vous terminez le sondage
@@ -2457,4 +2454,4 @@
\nPour nous y préparer, nous avons besoin de faire certains changements : les fils créés avant maintenant seront affichés comme des réponses classiques.
\n
\nCette transition sera unique, maintenant que les fils de discussions ont intégré la spécification de Matrix.
-
\ No newline at end of file
+
diff --git a/vector/src/main/res/values-gl/strings.xml b/vector/src/main/res/values-gl/strings.xml
index 62b77eadaf..295f0ebafb 100644
--- a/vector/src/main/res/values-gl/strings.xml
+++ b/vector/src/main/res/values-gl/strings.xml
@@ -369,7 +369,6 @@
Xa se está a usar este teléfono.novo contrasinalEscolla un país
- Aura3 días1 semana1 mes
@@ -531,4 +530,4 @@
Non tes permisos para comezar unha chamada de conferencia nesta salaNon tes permisos para comezar unha chamada de conferencia nesta salaMensaxe enviada
-
\ No newline at end of file
+
diff --git a/vector/src/main/res/values-hr/strings.xml b/vector/src/main/res/values-hr/strings.xml
index 79997f508a..dd7e0d7deb 100644
--- a/vector/src/main/res/values-hr/strings.xml
+++ b/vector/src/main/res/values-hr/strings.xml
@@ -582,7 +582,6 @@
Koristite upravitelja integracijama kako biste rukovali botovima, mostovima, grafičkim elementima i paketima naljepnica.
\nUpravitelji integracijama primaju konfiguracijske podatke i mogu mijenjati grafičke elemente, slati pozivnice za pridruživanje u sobe i postavljati razine upravljanja u Vaše ime.
- SklonostČitljivost sobne povijestiTko može čitati povijest\?Bilo tko
@@ -854,4 +853,4 @@
Ukloni…Potvrda uklanjanjaJeste li sigurni da želite ukloniti (izbrisati) ovaj događaj\? Ukoliko izbrišete naziv sobe ili promjenu teme, to može poništiti promjenu.
-
\ No newline at end of file
+
diff --git a/vector/src/main/res/values-hu/strings.xml b/vector/src/main/res/values-hu/strings.xml
index 3a8184cef7..07d97a8c86 100644
--- a/vector/src/main/res/values-hu/strings.xml
+++ b/vector/src/main/res/values-hu/strings.xml
@@ -336,9 +336,7 @@
Új jelszóJelszó frissítése sikertelenA jelszavad sikeresen frissítésre került
- "Az összes üzenet mutatása %s -tól/-től?
-
-Vedd figyelembe, hogy az alkalmazás újraindul ami sok időt vehet igénybe."
+ Az összes üzenet mutatása %s -tól/-től\?Válassz országotTémaÜzenet előzmények láthatósága
@@ -450,7 +448,6 @@ Vedd figyelembe, hogy az alkalmazás újraindul ami sok időt vehet igénybe."<
Ki lettél tiltva innen: %1$s, %2$s általOk: %1$sURL előnézet
- KitűzőKezdőlapRázd meg az eszközt, ha hibát szeretnél bejelenteni
@@ -1486,9 +1483,7 @@ A Visszaállítási Kulcsot tartsd biztonságos helyen, mint pl. egy jelszókeze
\n
\nEzt a műveletet bármikor visszavonhatja az általános beállításokban.Felhasználó figyelembe vétele
- A felhasználó figyelembe vétele után újra meg fog jelenni az összes üzenete.
-\n
-\nEz a művelet újraindítja az alkalmazást ami sokáig tarthat.
+ A felhasználó figyelembe vétele után újra meg fog jelenni az összes üzenete.Meghívás visszavonásaBiztos, hogy visszavonja a felhasználó meghívását\?a felhasználó kirúgása eltávolítja a szobából.
@@ -2350,8 +2345,6 @@ A Visszaállítási Kulcsot tartsd biztonságos helyen, mint pl. egy jelszókeze
Megnyitás ezzelAz ${app_name} nem fér hozzá a tartózkodási helyedhez. Próbáld újra később.Az ${app_name} nem tudott hozzáférni a tartózkodási helyedhez
- Tartózkodási hely megosztása
- Tartózkodási hely megosztásaFöldrajzi helyzetTartózkodási hely megosztásaAz eredmény csak a szavazás végeztével válik láthatóvá
@@ -2487,4 +2480,14 @@ A Visszaállítási Kulcsot tartsd biztonságos helyen, mint pl. egy jelszókeze
Béta ÜzenetszálakTudj meg többetPróbáld ki
-
\ No newline at end of file
+ Képernyőmegosztás folyamatban
+ ${app_name} képernyő megosztás
+ Képernyőmegosztás megállítása
+ Képernyő megosztása
+ - Néhány felhasználó újra figyelembe vannak véve
+ ${app_name} alkalmazásnak az alábbi okokból törölnie kell a gyorsítótárat:
+\n%s
+\n
+\nFigyelem, ez az alkalmazást újra is indítja, ami eltarthat egy darabig.
+ Kezdő szinkronizálási kérés
+
diff --git a/vector/src/main/res/values-in/strings.xml b/vector/src/main/res/values-in/strings.xml
index f0533eba10..a8a457b4af 100644
--- a/vector/src/main/res/values-in/strings.xml
+++ b/vector/src/main/res/values-in/strings.xml
@@ -361,11 +361,8 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.Sandi baruGagal memperbaharui kata sandiKata sandi Anda telah diperbaharui
- Tunjukkan semua pesan dari %s\?
-\n
-\nMohon perhatikan bahwa tindakan ini akan me-restart aplikasi dan mungkin akan membutuhkan waktu.
+ Tampilkan semua pesan dari %s\?Pilih negara
- Flair3 hari1 minggu1 bulan
@@ -590,9 +587,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.
Keluarkan penggunaApakah Anda yakin ingin membatalkan undangan untuk pengguna ini\?Batalkan undangan
- Membatalkan abaian pengguna ini akan menampilkan semua pesan dari mereka.
-\n
-\nDicatat bahwa tindakan ini akan memulai ulang aplikasi dan mungkin membutuhkan beberapa waktu.
+ Membatalkan abaian pengguna ini akan menampilkan semua pesan dari mereka.Batal pengabaian penggunaMengabaikan pengguna ini akan menghilangkan pesan mereka dari ruangan yang Anda bagikan.
\n
@@ -2307,8 +2302,6 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.Buka dengan${app_name} tidak dapat mengakses lokasi Anda. Silakan coba lagi nanti.${app_name} tidak dapat mengakses lokasi Anda
- Bagikan lokasi
- Bagikan lokasiLokasiBagikan lokasiHasil akan ditampilkan ketika Anda mengakhiri poll-nya
@@ -2442,4 +2435,14 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.Utasan BetaPelajari lebih lanjutCoba
-
\ No newline at end of file
+ Sedang membagikan layar
+ Pembagian Layar ${app_name}
+ Berhenti membagikan layar
+ Bagikan layar
+ - Beberapa pengguna telah dibatalkan pengabaiannya
+ ${app_name} membutuhkan sebuah penghapusan cache supaya bisa diperbarui, untuk alasan berikut:
+\n%s
+\n
+\nDicatat bahwa tindakan ini akan memulai ulang aplikasinya dan mungkin membutuhkan beberapa waktu.
+ Permintaan sinkronisasi awal
+
diff --git a/vector/src/main/res/values-is/strings.xml b/vector/src/main/res/values-is/strings.xml
index f052e88ee5..892d56b795 100644
--- a/vector/src/main/res/values-is/strings.xml
+++ b/vector/src/main/res/values-is/strings.xml
@@ -179,9 +179,7 @@
Nýtt lykilorðMistókst að uppfæra lykilorðLykilorðið þitt hefur verið uppfært
- Sýna öll skilaboð frá %s\?
-\n
-\nAthugaðu að þessi aðgerð mun endurræsa forritið og það getur tekið nokkurn tíma.
+ Sýna öll skilaboð frá %s\?Veldu land3 dagar1 vika
@@ -251,7 +249,6 @@
Nota innbyggða myndavélMinnst áTitra þegar minnst er á
- HlutverksmerkiLesanleiki ferilskrár spjallrásarFlytja út E2E dulritunarlykla spjallrásarFlytja út dulritunarlykla spjallrásar
@@ -1026,9 +1023,7 @@
Senda myndir og myndskeiðOpna myndavélOpna með
- Deila staðsetninguLandakort
- Deila staðsetninguStaðsetningDeila staðsetninguTegund könnunar
@@ -1981,4 +1976,4 @@
Spjallþræðir í bráðlegri Beta-útgáfu 🎉Ráðfæri við %1$sRáðfæra fyrst
-
\ No newline at end of file
+
diff --git a/vector/src/main/res/values-it/strings.xml b/vector/src/main/res/values-it/strings.xml
index 66fff2077e..3eec2c5f14 100644
--- a/vector/src/main/res/values-it/strings.xml
+++ b/vector/src/main/res/values-it/strings.xml
@@ -410,9 +410,7 @@
Nuova passwordL\'aggiornamento della password è fallitoLa tua password è stata aggiornata
- Mostra tutti i messaggi di %s\?
-\n
-\nTieni presente che questa azione riavvierà l\'app e ciò potrebbe richiedere molto tempo.
+ Mostrare tutti i messaggi di %s\?Scegli un paese3 giorni1 settimana
@@ -539,7 +537,6 @@
%2$s ti ha bannato da %1$sMotivo: %1$sAvatar
- PredisposizioneIl livello di potere deve essere un intero positivo.%d cambio d\'appartenenza
@@ -1519,9 +1516,7 @@
\n
\nPuoi annullare questa azione in qualsiasi momento nelle impostazioni generali.Non ignorare più
- Se non ignori più l\'utente vedrai di nuovo tutti i suoi messaggi.
-\n
-\nNota che quest\'azione riavvierà l\'app e potrebbe richiedere del tempo.
+ Se non ignori più l\'utente vedrai di nuovo tutti i suoi messaggi.Annulla invitoSei sicuro di voler annullare l\'invito per questo utente\?Butta fuori l\'utente
@@ -2341,8 +2336,6 @@
Apri con${app_name} non ha potuto rilevare la tua posizione. Riprova più tardi.${app_name} non ha potuto rilevare la tua posizione
- Condividi posizione
- Condividi posizionePosizioneCondividi posizioneI risultati verranno rivelati solo quando termini il sondaggio
@@ -2478,4 +2471,14 @@
Beta conversazioniMaggiori informazioniProvalo
-
\ No newline at end of file
+ Stai condividendo lo schermo
+ ${app_name} - Condivisione schermo
+ Ferma condivisione schermo
+ Condividi schermo
+ - Alcuni utenti non vengono più ignorati
+ ${app_name} ha bisogno di eseguire una pulizia della cache per aggiornarsi, per il seguente motivo:
+\n%s
+\n
+\nNota che questa azione riavvierà l\'app e potrebbe richiedere del tempo.
+ Richiesta di sincronizzazione iniziale
+
diff --git a/vector/src/main/res/values-iw/strings.xml b/vector/src/main/res/values-iw/strings.xml
index d4eb42a095..2a40246e3b 100644
--- a/vector/src/main/res/values-iw/strings.xml
+++ b/vector/src/main/res/values-iw/strings.xml
@@ -589,7 +589,6 @@
חודש 1שבוע 13 ימים
- כשרוןהשמע צליל תריסבחרומקור מדיה ברירת מחדל
@@ -1862,7 +1861,6 @@
כל חברי החדר.כל חברי החדר, מהרגע שבו הצטרפו.מפה
- שתף מיקוםמיקוםשתף מיקוםהתוצאות נחשפות רק לאחר מסיום הסקר
@@ -1890,7 +1888,6 @@
פתח באמצעות${app_name} לא הצליח לגשת למיקום שלך. אנא נסה שוב מאוחר יותר.${app_name} לא הצליח לגשת למיקום שלך
- שתף מיקוםאפשר התראת דוא\"ל עבור %sמילות מפתח לא יכולות להכיל \'%s\'מילות מפתח אינן יכולות להתחיל ב-\'.\'
@@ -2527,4 +2524,4 @@
\nבזמן שאנו מתכוננים לכך, עלינו לבצע מספר שינויים: שרשורים שנוצרו לפני נקודה זו יוצגו כתשובות רגילות.
\n
\nזה יהיה מעבר חד פעמי שכן שרשורים הם כעת חלק ממפרט Matrix.
-
\ 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 49335373a0..8852358966 100644
--- a/vector/src/main/res/values-ja/strings.xml
+++ b/vector/src/main/res/values-ja/strings.xml
@@ -341,7 +341,6 @@
全てのメッセージホーム画面にショートカットを作成インラインURLプレビュー
- コミュニティーのアバター暗号鍵を要求している新しいセッション \'%s\' を追加しました。未認証のセッション \'%s\' が暗号鍵を要求しています。作成
@@ -512,7 +511,7 @@
セキュリティーとプライバシーヘルプと概要ダイレクトメッセージ
- (編集した)
+ (編集済)会話を検索…全てのメッセージ (音量大)全てのメッセージ
@@ -1538,12 +1537,10 @@
選択肢%1$d選択肢を作成位置情報
- 位置情報を共有位置情報の共有を有効にする地図を読み込めませんでしたスレッドで返信位置情報を共有
- 位置情報を共有カメラを開く画像と動画を送信ファイルをアップロード
@@ -2372,4 +2369,9 @@
\nスレッドはMatrixの仕様の一部になったため、これは一度限りの変更です。スレッドはベータ版になります 🎉無効にする
+ スレッドについてのフィードバック
+ フィードバックを送信
+ ベータ版
+ ベータ版
+ 試す
diff --git a/vector/src/main/res/values-kab/strings.xml b/vector/src/main/res/values-kab/strings.xml
index 83a81a5f54..4e69d707dc 100644
--- a/vector/src/main/res/values-kab/strings.xml
+++ b/vector/src/main/res/values-kab/strings.xml
@@ -284,7 +284,6 @@
Awal uffir amaynutFrenFren
- LbennaYal tikkeltAnwa i izemren ad d-iɣer amazray\?Yal yiwen
@@ -1812,4 +1811,4 @@
%1$s t•yedda-d ɣer texxamtTesnulfaḍ-d adiwenni%1$s t•yesnulfa-d adiwenni
-
\ No newline at end of file
+
diff --git a/vector/src/main/res/values-ko/strings.xml b/vector/src/main/res/values-ko/strings.xml
index b57bfd40d7..1f6585ad52 100644
--- a/vector/src/main/res/values-ko/strings.xml
+++ b/vector/src/main/res/values-ko/strings.xml
@@ -414,7 +414,6 @@
기본 미디어 소스선택셔터 소리 재생하기
- 재능3일1주1달
@@ -965,4 +964,4 @@
방 이름을 바꾸었습니다: %1$s방 사진을 바꾸었습니다%1$s님이 방 사진을 바꾸었습니다
-
\ No newline at end of file
+
diff --git a/vector/src/main/res/values-lo/strings.xml b/vector/src/main/res/values-lo/strings.xml
index 72b2ae1d21..3d12927ac7 100644
--- a/vector/src/main/res/values-lo/strings.xml
+++ b/vector/src/main/res/values-lo/strings.xml
@@ -169,9 +169,7 @@
ທ່ານແນ່ໃຈບໍ່ວ່າຕ້ອງການຍົກເລີກການເຊີນຜູ້ໃຊ້ນີ້\?ຍົກເລີກການເຊີນຍົກເລີກການບໍ່ສົນໃຈ
- ການບໍ່ສົນໃຈຜູ້ໃຊ້ນີ້ຈະສະແດງຂໍ້ຄວາມທັງໝົດຈາກເຂົາເຈົ້າອີກຄັ້ງ.
-\n
-\nກະລຸນາຮັບຊາບວ່າຄຳສັ່ງນີ້ຈະປິດເປີດແອັບຄືນໃໝ່ ແລະ ມັນອາດຈະໃຊ້ເວລາຄາວໜຶ່ງ.
+ ການຍົກເລີກບໍ່ສົນໃຈຜູ້ໃຊ້ນີ້ຈະສະແດງຂໍ້ຄວາມທັງໝົດຈາກພວກເຂົາອີກຄັ້ງ.ບໍ່ສົນໃຈຜູ້ໃຊ້ບໍ່ສົນໃຈການບໍ່ສົນໃຈຜູ້ໃຊ້ນີ້ຈະລຶບຂໍ້ຄວາມຂອງເຂົາເຈົ້າອອກຈາກຫ້ອງທີ່ທ່ານແບ່ງປັນ.
@@ -747,10 +745,10 @@
ສ້າງຫ້ອງໃຫມ່ເຫດການບໍ່ຖືກຕ້ອງ, ບໍ່ສາມາດສະແດງໄດ້ຄວບຄຸມເຫດການໂດຍຜູ້ເບິ່ງແຍງຫ້ອງ
- ເຫດການຖືກລຶບໂດຍຜູ້ໃຊ້
+ ເຫດການທີ່ຖືກລຶບໂດຍຜູ້ໃຊ້ສະແດງຕົວຍຶດສໍາລັບຂໍ້ຄວາມທີ່ຖືກລົບອອກສະແດງຂໍ້ຄວາມທີ່ຖືກລືບອອກ
- ຂໍ້ຄວາມຖືກລຶບ
+ ລຶບຂໍ້ຄວາມອອກແລ້ວການໂຕ້ຕອບເບິ່ງການໂຕ້ຕອບເພີ່ມປະຕິກິລິຍາ
@@ -996,7 +994,6 @@
1 ເດືອນ1 ອາທິດ3 ມື້
- ໄຫວພິບຫຼິ້ນສຽງ shutterເລືອກແຫຼ່ງສື່
@@ -1006,9 +1003,7 @@
ເລືອກປະເທດຈັດການອີເມວ ແລະເບີໂທລະສັບທີ່ເຊື່ອມຕໍ່ກັບບັນຊີ Matrix ຂອງທ່ານອີເມວ ແລະເບີໂທລະສັບ
- ສະແດງຂໍ້ຄວາມທັງໝົດຈາກ %s ບໍ\?
-\n
-\nກະລຸນາຮັບຊາບວ່າຄຳສັ່ງນີ້ຈະປິດເປີດແອັບຄືນໃໝ່ ແລະ ມັນອາດຈະໃຊ້ເວລາຄາວໜຶ່ງ.
+ ສະແດງຂໍ້ຄວາມທັງໝົດຈາກ %s \?ລະຫັດຜ່ານຂອງທ່ານໄດ້ຮັບການປັບປຸງລະຫັດຜ່ານບໍ່ຖືກຕ້ອງອັບເດດລະຫັດຜ່ານບໍ່ສຳເລັດ
@@ -1449,7 +1444,7 @@
ກະລຸນາລໍຖ້າ, ມັນອາດຈະໃຊ້ເວລາຄາວໜຶ່ງ.ເຂົ້າຮ່ວມຫ້ອງແທນບໍ່ມີຊື່ຫ້ອງ
- ບາງຫ້ອງອາດຈະຖືກເຊື່ອງໄວ້ເພາະວ່າເປັນສ່ວນຕົວ ແລະ ທ່ານຕ້ອງໄດ້ຮັບການເຊີນ
+ ບາງຫ້ອງອາດຈະຖືກເຊື່ອງໄວ້ເພາະວ່າເປັນສ່ວນຕົວ ແລະ ທ່ານຕ້ອງໄດ້ຮັບການເຊີນ.ບາງຫ້ອງອາດຈະຖືກເຊື່ອງໄວ້ເພາະວ່າເປັນສ່ວນຕົວ ແລະ ທ່ານຕ້ອງການການເຊີນ.
\nທ່ານບໍ່ໄດ້ຮັບອະນຸຍາດໃຫ້ເພີ່ມຫ້ອງ.ພື້ນທີ່ນີ້ບໍ່ມີຫ້ອງ
@@ -1782,7 +1777,7 @@
"ຫນຶ່ງໃນຕໍ່ໄປນີ້ອາດຈະຖືກທໍາລາຍ:
\n
\n - homeserver ຂອງທ່ານ
-\n - homeserver ທີ່ຜູ້ໃຊ້ທີ່ທ່ານກໍາລັງກວດສອບແມ່ນໄດ້ເຊື່ອມຕໍ່ກັບ
+\n - homeserver ທີ່ຜູ້ໃຊ້ທີ່ທ່ານກໍາລັງກວດສອບການເຊື່ອມຕໍ່ຢູ່
\n - ຂອງທ່ານ, ຫຼື ການເຊື່ອມຕໍ່ອິນເຕີເນັດຂອງຜູ້ໃຊ້ອື່ນໆ
\n - ຂອງທ່ານ, ຫຼື ອຸປະກອນຂອງຜູ້ໃຊ້ອື່ນ"ບໍ່ປອດໄພ
@@ -1994,9 +1989,9 @@
ເພີ່ມພື້ນທີ່ພື້ນທີ່ສ່ວນຕົວພື້ນທີ່ສາທາລະນະ
- ທ່ານແນ່ໃຈບໍ່ວ່າຕ້ອງການລຶບຂໍ້ຄວາມທີ່ຍັງບໍ່ໄດ້ສົ່ງທັງໝົດຢູ່ໃນຫ້ອງນີ້\?
- ລຶບຂໍ້ຄວາມທີ່ຍັງບໍ່ໄດ້ສົ່ງ
- ສົ່ງຂໍ້ຄວາມບໍ່ສຳເລັດ
+ ທ່ານແນ່ໃຈບໍ່ວ່າທ່ານຕ້ອງການລຶບຂໍ້ຄວາມທີ່ຍັງບໍ່ໄດ້ສົ່ງທັງໝົດຢູ່ໃນຫ້ອງນີ້\?
+ ລຶບຂໍ້ຄວາມທີ່ຍັງບໍ່ໄດ້ສົ່ງອອກ
+ ບໍ່ສາມາດສົ່ງຂໍ້ຄວາມທ່ານຕ້ອງການຍົກເລີກການສົ່ງຂໍ້ຄວາມບໍ\?ລຶບຂໍ້ຄວາມທີ່ບໍ່ສຳເລັດທັງໝົດອອກບໍ່ສຳເລັດ
@@ -2007,20 +2002,20 @@
ເຂົ້າຮ່ວມພື້ນທີ່ກັບ id ທີ່ກຳນົດໃຫ້ເພີ່ມໃສ່ພື້ນທີ່ທີ່ກຳນົດໃຫ້ສ້າງພື້ນທີ່
- ເນື້ອໃນການນັດໝາຍ
- ສົ່ງການນັດໝາຍຂອງລັດ!
- ສົ່ງນັດໝາຍ!
+ ເນື້ອໃນຂອງເຫດການ
+ ສົ່ງຖະແຫຼງການຂອງເຫດການ!
+ ສົ່ງເຫດການແລ້ວ!ເຫດການຜິດປົກກະຕິບໍ່ມີປະເພດຂໍ້ຄວາມບໍ່ມີເນື້ອຫາ
- ເນື້ອໃນການນັດໝາຍ
+ ເນື້ອໃນຂອງເຫດການສະຖານະຂອງກະເເຈປະເພດ
- ສົ່ງນັດໝາຍຂອງລັດແບບກຳນົດເອງ
+ ສົ່ງຖະແຫຼງການຂອງເຫດການທີ່ກຳນົດເອງແກ້ໄຂເນື້ອຫາ
- ການນັດໝາຍຂອງລັດ
- ສົ່ງນັດມາຍຂອງລັດ
- ສົ່ງນັດໝາຍທີ່ກຳນົດເອງ
+ ຖະແຫຼງການຂອງເຫດການ
+ ສົ່ງສະຖານະຂອງເຫດການ
+ ສົ່ງເຫດການທີ່ກຳນົດເອງສຳຫຼວດສະຖານະຫ້ອງເຄື່ອງມືພັດທະນາບໍ່ສາມາດໃຊ້ໄດ້
@@ -2147,7 +2142,7 @@
${app_name} Androidລະຫັດແມ່ນທັນສະໄຫມແລ້ວ!ເຫດການຖືກກວດສອບໂດຍຜູ້ເບິ່ງແຍງຫ້ອງ, ເຫດຜົນ: %1$s
- ເຫດການທີ່ລຶບໂດຍຜູ້ໃຊ້, ເຫດຜົນ: %1$s
+ ເຫດການທີ່ຖືກລຶບໂດຍຜູ້ໃຊ້, ເຫດຜົນ: %1$sເຫດຜົນສໍາລັບການແກ້ໄຂລວມທັງເຫດຜົນທ່ານແນ່ໃຈບໍ່ວ່າຕ້ອງການລຶບ (ລຶບ) ເຫດການນີ້ອອກ\? ກະລຸນາຮັບຊາບວ່າຖ້າຫາກທ່ານລຶບຊື່ຫ້ອງ ຫຼື ການປ່ຽນຫົວຂໍ້, ມັນສາມາດຍົກເລີກການປ່ຽນແປງໄດ້.
@@ -2236,7 +2231,7 @@
\nການຢັ້ງຢືນຈະຖືກບັນທຶກໄວ້ໃນເຄື່ອງ ແລະ ແບ່ງປັນໃນເວີຊັນຂອງແອັບໃນອະນາຄົດ.ບໍ່ສົນໃຈ${app_name} ປະສົບບັນຫາໃນເວລາສະແດງເນື້ອຫາຂອງເຫດການດ້ວຍ id \'%1$s\'
- ${app_name} ບໍ່ໄດ້ຈັດການເຫດການຂອງປະເພດ \'%1$s\'
+ ${app_name} ບໍ່ໄດ້ຈັດການ ເຫດການຂອງປະເພດ \'%1$s\'ໄປເພື່ອອ່ານຂໍ້ຄວາມໂດຍກົງກຳນົດເອງ (%1$d) ໃນ %2$s
@@ -2320,4 +2315,130 @@
ລາຍງານເນື້ອຫານີ້ລາຍງານທີ່ກຳນົດເອງ…ບໍ່ເຫມາະສົມ
+ ເຫຼືອ %1$ds
+ ເລື່ອນເພື່ອຍົກເລີກ
+ ບັນທຶກຂໍ້ຄວາມສຽງ
+ ຂໍອະໄພ, ເກີດຄວາມຜິດພາດຂຶ້ນໃນຂະນະທີ່ພະຍາຍາມເຂົ້າຮ່ວມ: %s
+ ຍົກລະດັບເປັນເວີຊັນຫ້ອງທີ່ແນະນຳ
+ ຢຸດການແບ່ງປັນໜ້າຈໍ
+ ແບ່ງປັນຫນ້າຈໍ
+ - ຜູ້ໃຊ້ບາງຄົນໄດ້ຖືກສົນໃຈ
+ ${app_name} ຕ້ອງດໍາເນີນການ cache ທີ່ຊັດເຈນເພື່ອໃຫ້ທັນສະໄຫມ, ສໍາລັບເຫດຜົນດັ່ງຕໍ່ໄປນີ້:
+\n%s
+\n
+\nກະລຸນາຮັບຊາບວ່າຄຳສັ່ງນີ້ຈະປິດເປີດແອັບຄືນໃໝ່ ແລະ ມັນອາດຈະໃຊ້ເວລາຄາວໜຶ່ງ.
+ ການຮ້ອງຂໍການຊິງຄ໌ເບື້ອງຕົ້ນ
+ ການແບ່ງປັນໜ້າຈໍກຳລັງດຳເນີນຢູ່
+ ${app_name} ການແບ່ງປັນໜ້າຈໍ
+ ການແຈ້ງເຕືອນຫ້ອງ
+ ຜູ້ໃຊ້
+ ແຈ້ງໃຫ້ຫ້ອງໍທັງຫມົດ
+
+ %1$d ເພີ່ມເຕີມ
+
+ ສະແດງໜ້ອຍລົງ
+ ແບ່ງປັນສະຖານທີ່
+ ສ້າງແບບສຳຫຼວດ
+ ເປີດລາຍຊື່ຜູ້ຕິດຕໍ່
+ ສົ່ງສະຕິກເກີ
+ ອັບໂຫຼດໄຟລ໌
+ ສົ່ງຮູບພາບ ແລະ ວິດີໂອ
+ ເປີດກ້ອງຖ່າຍຮູບ
+ ສະແດງຟອງຂໍ້ຄວາມ
+ ການແບ່ງປັນສະຖານທີ່ກຳລັງດຳເນີນຢູ່
+ ${app_name} ສະຖານທີ່ປັດຈຸບັນ
+ ຢຸດ
+ ກຳລັງໂຫຼດສະຖານທີ່ປັດຈຸບັນ…
+ ເປີດໃຊ້ສະຖານທີ່ປັດຈຸບັນແລ້ວ
+ ໂຫຼດແຜນທີ່ບໍ່ສຳເລັດ
+ ສະແດງສະຖານທີ່ຂອງຜູ້ໃຊ້ໃນທາມລາຍ
+ ເມື່ອເປີດໃຊ້ແລ້ວທ່ານຈະສາມາດສົ່ງສະຖານທີ່ຂອງທ່ານໄປຫາຫ້ອງໃດກໍໄດ້
+ ເປີດໃຊ້ການແບ່ງປັນສະຖານທີ່
+ ເປີດດ້ວຍ
+ ${app_name} ບໍ່ສາມາດເຂົ້າເຖິງສະຖານທີ່ຂອງທ່ານໄດ້. ກະລຸນາລອງໃໝ່ໃນພາຍຫຼັງ.
+ ${app_name} ບໍ່ສາມາດເຂົ້າເຖິງສະຖານທີ່ຂອງທ່ານໄດ້
+ ຖ້າທ່ານຕ້ອງການແບ່ງປັນສະຖານທີ່ປັດຈຸບັນຂອງທ່ານ, ${app_name} ຕ້ອງການການເຂົ້າເຖິງສະຖານທີ່ຕະຫຼອດເວລາທີ່ແອັບຯຢູ່ໃນພື້ນຫຼັງ.
+\nພວກເຮົາຈະເຂົ້າເຖິງສະຖານທີ່ຂອງທ່ານສະເພາະໄລຍະເວລາທີ່ທ່ານເລືອກ.
+ ອະນຸຍາດໃຫ້ເຂົ້າເຖິງ
+ 8 ຊົ່ວໂມງ
+ 1 ຊົ່ວໂມງ
+ 15 ນາທີ
+ ແບ່ງປັນສະຖານທີ່ປັດຈຸບັນຂອງທ່ານສໍາລັບ
+ ແບ່ງປັນສະຖານທີ່ນີ້
+ ແບ່ງປັນສະຖານທີ່ນີ້
+ ແບ່ງປັນສະຖານທີ່ປັດຈຸບັນ
+ ແບ່ງປັນສະຖານທີ່ປັດຈຸບັນ
+ ແບ່ງປັນສະຖານທີ່ປະຈຸບັນຂອງຂ້ອຍ
+ ແບ່ງປັນສະຖານທີ່ປະຈຸບັນຂອງຂ້ອຍ
+ ຂະຫຍາຍໄປຫາສະຖານທີ່ປັດຈຸບັນ
+ ປັກໝຸດຂອງສະຖານທີ່ທີ່ເລືອກຢູ່ໃນແຜນທີ່
+ ແຜນທີ່
+ ສະຖານທີ່
+ ແບ່ງປັນສະຖານທີ່
+ ຜົນໄດ້ຮັບຈະຖືກເປີດເຜີຍເມື່ອທ່ານສິ້ນສຸດແບບສຳຫຼວດເທົ່ານັ້ນ
+ ປິດແບບສຳຫຼວດ
+ ຜູ້ລົງຄະແນນສຽງເຫັນຜົນທັນທີທີ່ເຂົາເຈົ້າລົງຄະແນນສຽງ
+ ເປີດແບບສຳຫຼວດ
+ ປະເພດແບບສຳຫຼວດ
+ ແກ້ໄຂແບບສຳຫຼວດ
+ ທ່ານແນ່ໃຈບໍ່ວ່າຕ້ອງການລຶບແບບສຳຫຼວດນີ້ອອກ\? ທ່ານຈະບໍ່ສາມາດກູ້ມັນຄືນໄດ້ເມື່ອລຶບອອກແລ້ວ.
+ ລຶບການສຳຫຼວດ
+ ແບບສຳຫຼວດສິ້ນສຸດລົງ
+ ລົງຄະແນນສຽງ
+ ສິ້ນສຸດແບບສຳຫຼວດ
+ ອັນນີ້ຈະຢຸດບໍ່ໃຫ້ຜູ້ຄົນສາມາດລົງຄະແນນສຽງໄດ້ ແລະຈະສະແດງຜົນສຸດທ້າຍຂອງການສຳຫຼວດຄວາມຄິດເຫັນ.
+ ສິ້ນສຸດແບບສຳຫຼວດນີ້ບໍ\?
+ ເລືອກຜູ້ຊະນະ
+ ສິ້ນສຸດແບບສຳຫຼວດ
+
+ ຜົນສຸດທ້າຍໂດຍອີງໃສ່ %1$d ຄະແນນສຽງ
+
+
+ %1$dຄະແນນສຽງ. ລົງຄະແນນສຽງເພື່ອເບິ່ງຜົນໄດ້ຮັບ
+
+ ບໍ່ມີການລົງຄະແນນສຽງ
+
+ ອີງຕາມ %1$d ຄະແນນສຽງ
+
+
+ %1$d ຄະແນນສຽງ
+
+
+ ຢ່າງໜ້ອຍຕ້ອງເລືອກ %1$s
+
+ ຄຳຖາມບໍ່ສາມາດຫວ່າງເປົ່າໄດ້
+ ສ້າງແບບສຳຫຼວດ
+ ເພີ່ມຕົວເລືອກ
+ ທາງເລືອກ %1$d
+ ສ້າງທາງເລືອກ
+ ຄໍາຖາມ ຫຼື ຫົວຂໍ້
+ ຄຳຖາມ ຫຼື ຫົວຂໍ້ການສຳຫຼວດ
+ ສ້າງແບບສຳຫຼວດ
+ ປິດເປີດແອັບພລິເຄຊັນຄືນໃໝ່ ເພື່ອໃຫ້ການປ່ຽນແປງ.
+ ເປີດໃຊ້ຄະນິດສາດ LaTeX
+ %s ໃນການຕັ້ງຄ່າເພື່ອຮັບຄຳເຊີນໂດຍກົງໃນ ${app_name}.
+ ເຊື່ອມຕໍ່ອີເມວນີ້ກັບບັນຊີຂອງທ່ານ
+ ການເຊີນໄປຫາພື້ນທີ່ນີ້ຖືກສົ່ງໄປຫາ %s ທີ່ບໍ່ກ່ຽວຂ້ອງກັບບັນຊີຂອງທ່ານ
+ ການເຊີນເຂົ້າຫ້ອງນີ້ຖືກສົ່ງໄປໃຫ້ %s ເຊິ່ງບໍ່ໄດ້ເຊື່ອມໂຍງກັບບັນຊີຂອງທ່ານ
+ ກະລຸນາຮັບຊາບວ່າການຍົກລະດັບຈະເຮັດໃຫ້ຫ້ອງເປັນເວີຊັນໃໝ່. ຂໍ້ຄວາມປັດຈຸບັນທັງໝົດຈະຢູ່ໃນຫ້ອງເກັບມ້ຽນນີ້.
+ ທຸກຄົນທີ່ຢູ່ໃນແຫຼ່ງພື້ນທີ່ຈະສາມາດຊອກຫາ ແລະ ເຂົ້າຮ່ວມຫ້ອງນີ້ໄດ້ - ບໍ່ຈໍາເປັນຕ້ອງເຊີນທຸກຄົນດ້ວຍຕົນເອງ. ທ່ານສາມາດປ່ຽນສີ່ງນີ້ໃນການຕັ້ງຄ່າຫ້ອງໄດ້ທຸກເວລາ.
+ ທຸກຄົນໃນ %s ຈະສາມາດຊອກຫາ ແລະ ເຂົ້າຮ່ວມຫ້ອງນີ້ໄດ້ - ບໍ່ຈໍາເປັນຕ້ອງເຊີນທຸກຄົນດ້ວຍຕົນເອງ. ທ່ານຈະສາມາດປ່ຽນສິ່ງນີ້ໃນການຕັ້ງຄ່າຫ້ອງໄດ້ທຸກເວລາ.
+ (%1$s)
+ %1$s (%2$s)
+ ບໍ່ສາມາດຫຼິ້ນໄດ້ %1$s
+ ຢຸດ %1$s ໄວ້ຊົ່ວຄາວ
+ ຫຼິ້ນ %1$s
+ %1$d ນາທີ %2$d ວິນາທີ
+ %1$s, %2$s, %3$s
+ ຂໍ້ຄວາມສຽງ (%1$s)
+ ບໍ່ສາມາດຕອບ ຫຼື ແກ້ໄຂໄດ້ ໃນຂະນະທີ່ຂໍ້ຄວາມສຽງເປີດຢູ່
+ ບໍ່ສາມາດບັນທຶກຂໍ້ຄວາມສຽງໄດ້
+ ບໍ່ສາມາດຫຼິ້ນຂໍ້ຄວາມສຽງນີ້ໄດ້
+ ແຕະໃສ່ການບັນທຶກຂອງທ່ານເພື່ອຢຸດ ຫຼືຟັງ
+ ກົດຄ້າງໄວ້ເພື່ອບັນທຶກ, ປ່ອຍເພື່ອສົ່ງ
+ ລຶບການບັນທຶກ
+ ການບັນທຶກຂໍ້ຄວາມສຽງ
+ ຢຸດການບັນທຶກ
+ ຢຸດຂໍ້ຄວາມສຽງຊົ່ວຄາວ
+ ຫຼິ້ນຂໍ້ຄວາມສຽງ
diff --git a/vector/src/main/res/values-lv/strings.xml b/vector/src/main/res/values-lv/strings.xml
index f308a32924..0442171c11 100644
--- a/vector/src/main/res/values-lv/strings.xml
+++ b/vector/src/main/res/values-lv/strings.xml
@@ -431,7 +431,6 @@
\n
\nŅemiet vērā, ka ar šo darbību tiks pārstartēta lietotne, un tas var aizņemt kādu laiku.Izvēlies valsti
- Gaidas3 dienas1 nedēļa1 mēnesis
diff --git a/vector/src/main/res/values-ml/strings.xml b/vector/src/main/res/values-ml/strings.xml
index b9bda4fb42..e7e57d9e21 100644
--- a/vector/src/main/res/values-ml/strings.xml
+++ b/vector/src/main/res/values-ml/strings.xml
@@ -701,5 +701,4 @@
നിർദ്ദേശങ്ങൾഇമ്പോർട്ട്എക്സ്പോർട്ട്
- ഫ്ലെയർ
-
\ No newline at end of file
+
diff --git a/vector/src/main/res/values-nb-rNO/strings.xml b/vector/src/main/res/values-nb-rNO/strings.xml
index eedd2fc48c..bb8d6fa43e 100644
--- a/vector/src/main/res/values-nb-rNO/strings.xml
+++ b/vector/src/main/res/values-nb-rNO/strings.xml
@@ -111,7 +111,6 @@
MedierVelgVelg
- Merkeskilt3 dager1 uke1 måned
@@ -1282,4 +1281,4 @@
%1$s endret visningsnavnet sitt til %2$s%1$s utestengte %2$s%ss invitasjon
-
\ No newline at end of file
+
diff --git a/vector/src/main/res/values-nl/strings.xml b/vector/src/main/res/values-nl/strings.xml
index f567323fef..3397415ab7 100644
--- a/vector/src/main/res/values-nl/strings.xml
+++ b/vector/src/main/res/values-nl/strings.xml
@@ -260,9 +260,7 @@
Nieuw wachtwoordBijwerken van wachtwoord is misluktUw wachtwoord is gewijzigd
- Alle berichten van %s tonen\?
-\n
-\nLet op: deze actie zal de app herstarten; dit kan even duren.
+ Alle berichten van %s tonen\?Kies een landOnderwerpToegang tot de gespreksgeschiedenis
@@ -380,7 +378,6 @@
Snelkoppeling aan thuisscherm toevoegenInline URL-voorvertoningTrillen bij vermelden van een persoon
- Badge%d gesprek%d gesprekken
@@ -1244,9 +1241,7 @@
\nOm te voorkomen dat hij/zij opnieuw toetreedt, kun je hem/haar ook verbannen.Reden voor verwijderingWeet u zeker dat u uitnodiging voor deze persoon wilt annuleren\?
- Als u deze persoon niet negeert, worden alle berichten van deze persoon opnieuw weergegeven.
-\n
-\nHoud er rekening mee dat deze actie de app opnieuw zal starten en dat dit enige tijd kan duren.
+ Als u deze persoon niet negeert, worden alle berichten van deze persoon opnieuw weergegeven.Door deze persoon te negeren worden zijn/haar berichten verwijderd uit gesprekken die jullie delen.
\n
\nU kunt deze actie op elk moment ongedaan maken in de algemene instellingen.
@@ -2350,8 +2345,6 @@
Open met${app_name} kan geen toegang krijgen tot uw locatie. Probeer het later opnieuw.${app_name} heeft geen toegang tot uw locatie
- Deel locatie
- Deel locatieLocatieDeel locatieResultaten worden pas onthuld als je de poll beëindigt
@@ -2487,4 +2480,14 @@
Discussies bètaLeer meerProbeer het uit
-
\ No newline at end of file
+ Scherm delen is bezig
+ ${app_name} Scherm delen
+ Stop scherm delen
+ Deel scherm
+ - Sommige personen zijn niet genegeerd
+ ${app_name} moet de cache wissen om up-to-date te zijn om volgende reden een:
+\n%s
+\n
+\nHoud er rekening mee dat deze actie de app opnieuw zal starten en dat dit enige tijd kan duren.
+ Initieel synchronisatieverzoek
+
diff --git a/vector/src/main/res/values-nn/strings.xml b/vector/src/main/res/values-nn/strings.xml
index 86d4104dd5..c7d96037e2 100644
--- a/vector/src/main/res/values-nn/strings.xml
+++ b/vector/src/main/res/values-nn/strings.xml
@@ -291,7 +291,6 @@
Passordet ditt vart oppdatertVis alle meldingane frå %s\? Merk at denne handlinga gjer at applikasjonen startar på nytt og kan taka litt tid.Vel eit land
- Etikett3 dagar1 veke1 månad
@@ -565,4 +564,4 @@ Meldingssynlegheit på Matrix liknar på epost. At vi gløymer meldingane dine t
NullstillFolkFolk
-
\ No newline at end of file
+
diff --git a/vector/src/main/res/values-pl/strings.xml b/vector/src/main/res/values-pl/strings.xml
index f6b714bdc2..d6fef1c620 100644
--- a/vector/src/main/res/values-pl/strings.xml
+++ b/vector/src/main/res/values-pl/strings.xml
@@ -216,9 +216,7 @@
Nowe hasłoNie udało się zmienić hasłaTwoje hasło zostało zmienione
- Pokazywać wszystkie wiadomości od %s\?
-\n
-\nZauważ, że ta czynność spowoduje ponowne uruchomienie aplikacji i może to trochę potrwać.
+ Pokazywać wszystkie wiadomości od %s\?Wybierz kraj3 dni1 tydzień
@@ -387,7 +385,6 @@
Poziom uprawnień musi być liczbą dodatnią.Nie jesteś w tym pokoju.Nie masz uprawnień, aby zrobić to w tym pokoju.
- Wyróżnik społecznościBrakujące room_id w żądaniu.Brakujące user_id w żądaniu.Brakuje wymaganego parametru.
@@ -1523,9 +1520,7 @@
Wykop użytkownikaCzy na pewno anulować zaproszenie dla tego użytkownika\?Anuluj zaproszenie
- Zaprzestanie ignorowania użytkownika spowoduje ponowne wyświetlanie wszystkich jego wiadomości.
-\n
-\nAplikacja uruchomi się ponownie aby zsynchronizować wiadomości, może to potrwać kilka chwil.
+ Zaprzestanie ignorowania użytkownika spowoduje ponowne wyświetlanie wszystkich jego wiadomości.Przestań ignorować użytkownikaZignorowanie tego użytkownika usunie wszystkie jego wiadomości z pokojów, które współdzielicie.
\n
@@ -2429,7 +2424,7 @@
Wątki zbliżają się do wersji beta 🎉Z wątkuWskazówka: naciśnij i przytrzymaj wiadomość i użyj „%s”.
- Dzięki wątkom Twoje rozmowy są aktualne i łatwe do śledzenia.
+ Dzięki wątkom Twoje rozmowy są zorganizowane i łatwe do śledzenia.Organizuj dyskusje za pomocą wątkówPokazuje wszystkie wątki, w których brałeś udziałPokazuje wszystkie wątki z bieżącego pokoju
@@ -2529,9 +2524,7 @@
Udostępnij moją aktualną lokalizacjęPowiększ do bieżącej lokalizacjiPrzypnij na mapie wybraną lokalizację
- Udostępnij lokalizacjęMapa
- Udostępnij lokalizacjęLokalizacjaUdostępnij lokalizacjęWyniki są ujawniane dopiero po zakończeniu ankiety
@@ -2565,4 +2558,29 @@
Wątki (Beta)Dowiedz się więcejWypróbuj
-
\ No newline at end of file
+ ${app_name} musi wykonać czyszczenie pamięci podręcznej, aby była aktualna, z następującego powodu:
+\n%s
+\n
+\nPamiętaj, że ta czynność spowoduje ponowne uruchomienie aplikacji i może to zająć trochę czasu.
+ Udostępnianie ekranu ${app_name}
+ %1$d minut %2$d sekund
+ ${app_name} świetnie sprawdza się również w miejscu pracy. Cieszy się zaufaniem najbezpieczniejszych organizacji na świecie.
+ Trwa udostępnianie ekranu
+ Wczytuję lokalizację na żywo…
+ 8 godzin
+ 1 godzinę
+ 15 minut
+ Udostępnij swoją lokalizację na żywo przez
+ (%1$s)
+ %1$s (%2$s)
+ Nie można odtworzyć %1$s
+ Wstrzymaj %1$s
+ Odtwórz %1$s
+ %1$s, %2$s, %3$s
+ Udostępnił swoją lokalizację na żywo
+ Wątki są w toku produkcyjnym i mają nowe, ekscytujące nadchodzące funkcje, takie jak ulepszone powiadomienia. Chętnie poznamy Twoją opinię!
+ Zatrzymaj udostępnianie ekranu
+ Udostępnij ekran
+ - Niektórzy użytkownicy przestali być ignorowani
+ Początkowe żądanie synchronizacji
+
diff --git a/vector/src/main/res/values-pt-rBR/strings.xml b/vector/src/main/res/values-pt-rBR/strings.xml
index 21ea05a47c..97896dec64 100644
--- a/vector/src/main/res/values-pt-rBR/strings.xml
+++ b/vector/src/main/res/values-pt-rBR/strings.xml
@@ -403,9 +403,7 @@
Senha novaFalha para atualizar senhaSua senha tem sido atualizada
- Mostrar todas as mensagens de %s\?
-\n
-\nNote que esta ação vai recomeçar o app e pode levar algum tempo.
+ Mostrar todas as mensagens de %s\?Escolha um paísTópicoLegibilidade de Histórico de Sala
@@ -490,7 +488,6 @@
Mostrar timestamps em formato de 12 horasVibrar ao mencionar um/uma usuário(a)Analítica
- FlairVocê tem certeza que você quer deletar o widget desta sala\?Incapaz de criar widget.Falha para enviar requisição.
@@ -757,9 +754,7 @@
\n
\nVocê pode reverter esta ação a qualquer momento nas configurações gerais.Designorar usuária(o)
- Designorar esta(e) usuária(o) vai mostrar todas as mensagens dela(e) de novo.
-\n
-\nNote que esta ação vai recomeçar o app e pode levar algum tempo.
+ Designorar esta(e) usuária(o) vai mostrar todas as mensagens dela(e) de novo.Cancelar conviteVocê tem certeza que você quer cancelar o convite para esta(e) usuária(o)\?Expulsar usuária(o)
@@ -2350,8 +2345,6 @@
Abrir com${app_name} não pôde acessar sua localização. Por favor tente de novo mais tarde.${app_name} não pôde acessar sua localização
- Compartilhar localização
- Compartilhar localizaçãoLocalizaçãoCompartilhar localizaçãoResultados são somente revelados quando você termina a sondagem
@@ -2487,4 +2480,14 @@
Threads BetaSaber maisTeste aí
-
\ No newline at end of file
+ Compartilhamento de tela está em progresso
+ ${app_name} Compartilhamento de Tela
+ Parar compartilhamento de tela
+ Compartilhar tela
+ - Algumas(ns) usuárias(os) têm sido designoradas(os)
+ ${app_name} precisa performar um cache limpo para estar atualizado, pela seguinte razão:
+\n%s
+\n
+\nNote que esta ação vai recomeçar o app e pode levar algum tempo.
+ Requisição de sinc inicial
+
diff --git a/vector/src/main/res/values-pt/strings.xml b/vector/src/main/res/values-pt/strings.xml
index d8f6a9c592..c5f6329f3d 100644
--- a/vector/src/main/res/values-pt/strings.xml
+++ b/vector/src/main/res/values-pt/strings.xml
@@ -369,7 +369,6 @@ Adicionar alguns agora?Estatísticas de usoEnviar dados de análise de estatísticasO ${app_name} recolhe dados anónimos de análise de estatísticas para ajudar a melhorar a aplicação.
- InsígniasPor favor, crie uma frase-passe para cifrar as chaves exportadas. Precisará de usar a mesma frase-passe para conseguir importar as chaves.Criar frase-passeAs frase-passes devem coincidir
@@ -521,4 +520,4 @@ Por favor, verifique configurações de aplicaçãoEscolha cor de LED, vibração, som…${app_name} irá sincronizar em fundo periodicamente em tempo preciso (configurável).
\nIsto terá impacto na utilização do rádio e da bateria, será exibida uma notificação permanente declarando que ${app_name} está à escuta dos eventos.
-
\ 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 9a925ecaa7..cbc4bea123 100644
--- a/vector/src/main/res/values-ru/strings.xml
+++ b/vector/src/main/res/values-ru/strings.xml
@@ -570,7 +570,6 @@
%d непрочитанных уведомлений
- ЧутьеОтправить стикерОтправить стикерУ вас сейчас нет доступных стикеров.
@@ -2334,8 +2333,6 @@
Открыть с помощью${app_name} не смог получить доступ к вашему местоположению. Пожалуйста, повторите попытку позже.${app_name} не смог получить доступ к вашему местоположению
- Поделиться местоположением
- Поделиться местоположениемМестоположениеПоделиться местоположениемРезультаты отображаются только после завершения опроса
@@ -2444,7 +2441,7 @@
Не могу связаться с домашним сервером на URL %s. Пожалуйста, проверьте вашу ссылку или выберите домашний сервер вручную.Не сейчасВключить
- Слежка за уведомлениями
+ Поиск уведомленийВам не разрешено подключаться к этой комнатеОрганизуйте обсуждение с помощью ветокПоказать все ветки, в которых вы участвуете
@@ -2497,4 +2494,4 @@
%d изменений ACL сервера%d изменений ACL сервера
-
\ 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 a2c1404d2f..a7a9f152fd 100644
--- a/vector/src/main/res/values-sk/strings.xml
+++ b/vector/src/main/res/values-sk/strings.xml
@@ -368,9 +368,7 @@
Nové hesloNepodarilo sa zmeniť hesloVaše heslo bolo aktualizované
- Zobraziť všetky správy od %s\?
-\n
-\nUpozorňujeme, že táto akcia spôsobí reštart aplikácie a môže chvíľu trvať.
+ Zobraziť všetky správy od %s\?Vyberte si krajinu3 dni1 týždeň
@@ -458,7 +456,6 @@
Pridať na domovskú obrazovkuNáhľady URL adriesVibrovať keď spomeniete iného používateľa
- Príslušnosť ku komunitámVytvoriťDomovMiestnosti
@@ -751,9 +748,7 @@
\n
\nTúto akciu môžete kedykoľvek zmeniť späť vo všeobecných nastaveniach.Neignorovať používateľa
- Zrušením ignorovania tohto používateľa sa opäť zobrazia všetky správy od neho.
-\n
-\nUpozorňujeme, že táto akcia spôsobí reštart aplikácie a môže chvíľu trvať.
+ Zrušením ignorovania tohto používateľa sa opäť zobrazia všetky správy od neho.Zrušiť pozvanieSte si istí, že chcete zrušiť pozvanie tohoto používateľa\?Vykázať používateľa
@@ -1571,8 +1566,6 @@
Zdieľali svoju polohuZdieľať polohuPoloha
- Zdieľať polohu
- Zdieľať polohu${app_name} nemohol získať prístup k vašej polohe${app_name} nemohol získať prístup k vašej polohe. Skúste to prosím neskôr.Otvoriť s
@@ -2534,4 +2527,14 @@
Vlákna BetaZistiť viacVyskúšajte si to
-
\ No newline at end of file
+ Prebieha zdieľanie obrazovky
+ Zdieľanie obrazovky aplikácie ${app_name}
+ Zastaviť zdieľanie obrazovky
+ Zdieľať obrazovku
+ - Pre niektorých používateľov bolo zrušené ignorovanie
+ ${app_name} potrebuje vyčistiť vyrovnávaciu pamäť, aby bola aktuálna, a to z nasledujúceho dôvodu:
+\n%s
+\n
+\nUpozorňujeme, že táto akcia spôsobí reštart aplikácie a môže chvíľu trvať.
+ Úvodná žiadosť o synchronizáciu
+
diff --git a/vector/src/main/res/values-sq/strings.xml b/vector/src/main/res/values-sq/strings.xml
index 18d2b137e1..669cfa6b68 100644
--- a/vector/src/main/res/values-sq/strings.xml
+++ b/vector/src/main/res/values-sq/strings.xml
@@ -621,7 +621,6 @@
Për thirrje ardhëse përdor zilen parazgjedhje të ${app_name}-itZile thirrjesh ardhësePërzgjidhni zile për thirrjet:
- SimbolePranojeJu lutemi, merrni në shqyrtim dhe pranoni rregullat e këtij shërbyesi Home:Diagnostikoni Njoftime
@@ -1389,7 +1388,6 @@
DiagnostikojeE dërgon një mesazh si tekst të thjeshtë, pa interpretuar elementët MarkdownEmër përdoruesi dhe/ose fjalëkalim i pasaktë. Fjalëkalimi i dhënë fillon ose mbaron me hapësirë, ju lutemi, kontrollojeni.
- Po publikohen kyçe të krijuar identitetiMesazh…Ka të gatshëm përmirësim fshehtëzimi
@@ -2333,8 +2331,6 @@
Jini zot i bisedave tuaja.Jepe vendndodhjenAktivizoni dhënie vendndodhjeje
- Jepe vendndodhjen
- Jepe vendndodhjenVendndodhjeJepe vendndodhjenPërfundimet shfaqen vetëm kur përfundoni anketimin
@@ -2473,4 +2469,4 @@
PraniMësoni më tepërProvojeni
-
\ No newline at end of file
+
diff --git a/vector/src/main/res/values-sv/strings.xml b/vector/src/main/res/values-sv/strings.xml
index a7400528fa..5a05f73cf4 100644
--- a/vector/src/main/res/values-sv/strings.xml
+++ b/vector/src/main/res/values-sv/strings.xml
@@ -524,12 +524,9 @@
IdentitetsserverVänligen kolla din e-post klicka på länken i den. När du är klar med detta, klicka på fortsätt.Den här e-postadressen är upptagen.
- Visa alla meddelanden från %s\?
-\n
-\nObservera att den har handlingen kommer att starta om appen, och det kan ta lite tid.
+ Visa alla meddelanden från %s\?MediaFörvald mediakälla
- EmblemAvanceratExperimentÅterställning av krypterade meddelanden
@@ -837,9 +834,7 @@
DegraderaIgnorera användareAvignorera användare
- Att avignorera den här användaren kommer att visa alla meddelanden från denne igen.
-\n
-\nObservera att detta kräver en omstart av appen och kan ta en stund.
+ Att avignorera den här användaren kommer att visa alla meddelanden från denne igen.Avbryt inbjudanÄr du säker på att du vill avbryta inbjudan för den här användaren\?Kicka användaren
@@ -2350,8 +2345,6 @@
Öppna med${app_name} kunde inte komma åt din plats. Försök igen senare.${app_name} kunde inte komma åt din plats
- Dela plats
- Dela platsPlatsDela platsVäljare kan se resultatet så fort de har röstat
@@ -2487,4 +2480,14 @@
Trådar BetaLäs merProva
-
\ No newline at end of file
+ Skärmdelning pågår
+ ${app_name} delar skärm
+ Avsluta skärmdelning
+ Dela skärm
+ - Vissa användare har avignorerats
+ ${app_name} behöver rensa cache för att uppdateras, av följande anledning:
+\n%s
+\n
+\nObservera att detta startar om appen, och kan ta ett tag.
+ Förfrågan om inledande synk
+
diff --git a/vector/src/main/res/values-tr/strings.xml b/vector/src/main/res/values-tr/strings.xml
index 69176ec3a6..64eb85c7f6 100644
--- a/vector/src/main/res/values-tr/strings.xml
+++ b/vector/src/main/res/values-tr/strings.xml
@@ -377,7 +377,6 @@
\n
\nNot bu eylem uygulamayı yeniden başlatacak ve biraz zaman alabilir.Ülke seç
- Kabiliyet3 gün1 hafta1 ay
@@ -1778,4 +1777,4 @@
Konu bağlantısını kopyalaOdada görüntüleKonuları Görüntüle
-
\ 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 85900a392e..d9847bbb81 100644
--- a/vector/src/main/res/values-uk/strings.xml
+++ b/vector/src/main/res/values-uk/strings.xml
@@ -333,9 +333,7 @@
Новий парольНе вдалося оновити парольПароль успішно оновлено
- Показувати всі повідомлення %s\?
-\n
-\nЗауважте, що це перезавантажить застосунок та може тривати деякий час.
+ Показувати всі повідомлення %s\?Оберіть країну3 дні1 тиждень
@@ -451,7 +449,6 @@
Деактивація облікового записуДеактивувати мій обліковий записНадсилання аналітичних даних
- НастрійЩоб надіслати ключ на цей пристрій, запустіть ${app_name} на іншому пристрої, що може розшифрувати повідомлення.Вибачте, жодного зовнішнього застосунку не знайдено для виконання цієї дії.Надіслати голосове повідомлення
@@ -1002,9 +999,7 @@
Вилучити користувачаВи впевнені, що бажаєте скасувати запрошення для цього користувача\?Скасувати запрошення
- Якщо перестати нехтувати цього користувача, усі його повідомлення стануть знову видимими.
-\n
-\nЗауважте, що ця дія перезапустить застосунок, а це може тривати деякий час.
+ Якщо перестати нехтувати цього користувача, усі його повідомлення стануть знову видимими.Рознехтувати користувачаНехтування цього користувача призведе до вилучення його повідомлень з усіх спільних кімнат.
\n
@@ -1243,7 +1238,7 @@
\n
\nВаші повідомлення захищені замками, тож лише ви та отримувачі мають унікальні ключі для їхнього відмикання.Це початок %s.
- %1$s відхиляє цей виклик
+ %1$s відхиляє викликКористувачіПід час переадресації трапилась помилкаПереадресувати
@@ -2438,8 +2433,6 @@
Відкрити за допомогою${app_name} не може отримати доступ до вашого місцеперебування. Спробуйте пізніше.${app_name} не може отримати доступ до вашого місцеперебування
- Поділитися місцеперебуванням
- Поділитися місцеперебуваннямМісцеперебуванняПоділитися місцеперебуваннямРезультати можна переглянути лише після завершення опитування
@@ -2579,4 +2572,14 @@
Треди бетаДокладнішеСпробувати
-
\ No newline at end of file
+ Триває трансляція з екрана
+ Трансляція з екрана ${app_name}
+ Припинити ділитися екраном
+ Поділитися екраном
+ - Деякі користувачі нехтуються
+ Щоб оновитися, ${app_name} потребує очищення кешу з такої причини:
+\n%s
+\n
+\nЗауважте, що ця дія перезапустить застосунок, і це може тривати деякий час.
+ Початковий запит синхронізації
+
diff --git a/vector/src/main/res/values-vi/strings.xml b/vector/src/main/res/values-vi/strings.xml
index 310c35d496..1ca481e798 100644
--- a/vector/src/main/res/values-vi/strings.xml
+++ b/vector/src/main/res/values-vi/strings.xml
@@ -9,7 +9,6 @@
Gửi ảnh chụp màn hìnhGửi lịch sử yêu cầu chia sẻ khóaCộng đồng
-
PhòngKhông còn kết quả nào nữaBỏ qua
@@ -58,7 +57,6 @@
Bạn không có quyền bắt đầu cuộc gọi trong phòng nàyBạn không có quyền bắt đầu cuộc gọi hội thoạiBạn không có quyền bắt đầu cuộc gọi hội thoại trong phòng này
-
Bắt đầu trò chuyệnCài lạiBỏ
@@ -125,7 +123,6 @@
Điện thoạiChọn thiết bị âm thanhCuộc gọi ${app_name} thất bại
-
Bạn có chắc bạn muốn bắt đầu một cuộc gọi video\?Bạn có chắc bạn muốn bắt đầu một cuộc gọi bằng giọng nói\?Gửi giọng nói
@@ -156,7 +153,7 @@
Chương trình đã gặp sự cố lần trước. Bạn có muốn mở trang tường thuật sự cố không\?Tiến độ (%s%%)Gửi nhật ký dừng đột ngột
- Gửi nhật ký
+ Gửi logGửiQuản lý cài đặt khám phá của bạn.Khám phá
@@ -333,7 +330,6 @@
%1$s đã rời phòng. Lý do: %2$sĐịa chỉ email được liên kết đến tài khoản của bạn phải được nhập.Máy chủ nhà này muốn chắc chắn bạn không phải rô bốt
-
Số điện thoại này đã được định nghĩa rồi.Địa chỉ email này đã được định nghĩa rồi.Đăng nhập bằng đăng nhập một lần
@@ -354,7 +350,6 @@
Phục hồi các tin nhắn được mã hoáXuất các mã khoá thành công
-
Vui lòng tạo một mật khẩu để mã hoá các mã khoá được xuất. Bạn sẽ cần nhập mật khẩu đó để có thể nhập các mã khoá đó.XuấtXuất các mã khoá ra tệp cục bộ
@@ -366,24 +361,17 @@
Nhập các mã khoá phòng E2EQuản lý bản sao lưu mã khoáip không xác định
-
Nếu chúng không khớp, sự bảo mật của việc giao tiếp của bạn có thể bị can thiệp.Xác nhận bằng cách so sánh những điều sau đây với Cài đặt người dùng trong phiên làm việc kia của bạn:Xác minhURL máy chủ nhàBạn có chắc bạn muốn rời khỏi phòng không\?Rời khỏi phòng
-
-
-
-
%d thành viên
-
-
Nhảy đến tin nhắn chưa đọc.
- Liệt kê các thành viên
+ Các thành viênTừ chốiTham giaXoá
@@ -392,14 +380,10 @@
CÓCho phép quyền truy cập danh bạ của bạn.Để quét mã QR, bạn cần cho phép quyền truy cập máy ảnh.
-
${app_name} cần quyền truy cập máy ảnh và micro của bạn để thực hiện các cuộc gọi video.
\n
\nVui lòng cho phép quyền truy cập trên các cửa sổ popup tiếp theo để có thể thực hiện cuộc gọi.
-
${app_name} cần quyền truy cập micro của bạn để thực hiện các cuộc gọi âm thanh.
-
-
Thông tinBên kia không thể nhấc máy.Giữ máy
@@ -427,7 +411,6 @@
Không thể kết nối đến máy chủ nhà tại URL này, vui lòng kiểm tra nóVui lòng nhập URL hợp lệVui lòng xem xét và chấp nhận chính sách của máy chủ nhà này:
-
Xác minh địa chỉ email thất bại: hãy chắc chắn là bạn đã nhấn vào liên kết trong emailHiện tất cả phòng trong thư mục phòng, bao gồm cả các phòng có nội dung phản cảm.Hiện các phòng có nội dung phản cảm
@@ -529,7 +512,6 @@
\nĐang đợi phản hồi từ máy chủ…Phòng trống (đã là %s)Phòng trống
-
%1$s, %2$s, %3$s và %4$d người khác
@@ -581,10 +563,10 @@
Được tối ưu cho pinBạn sẽ không nhận được thông báo khi được đề cập tới trong phòng chat mã hóa trên mobile.Nâng cấp phòng
- Tin nhắn gửi bởi bot
- Lời mời cuộc gọi
- Lời mời vào phòng
- Từ khóa
+ Các tin nhắn gửi bởi bot
+ Các lời mời cuộc gọi
+ Các lời mời vào phòng
+ Các từ khóa\@roomTin nhắn trong chat nhóm được mã hóaTin nhắn trong chat nhóm
@@ -721,7 +703,6 @@
Tất cả tin nhắnTất cả tin nhắn (ầm ĩ)Lơ người dùng
-
Nội dung này bị báo cáo không phù hợp.
\n
\nNếu bạn không muốn thấy thêm nội dung từ người dùng này, bạn có thể lơ họ để ẩn nội dung tin nhắn.
@@ -806,7 +787,6 @@
1 tháng1 tuần3 ngày
- Biểu tượngPhát âm thanh shutterChọnNguồn media mặc định
@@ -816,9 +796,7 @@
Chọn quốc giaQuản lý email và số điện thoại liên kết với tài khoản MatrixEmail và số điện thoại
- Hiện tất cả tin nhắn từ %s\?
-\n
-\nLưu ý rằng hành động này sẽ khởi động App và có thể mất nhiều thời gian.
+ Hiện tất cả tin nhắn từ %s\?Mật khẩu của bạn vừa được cập nhậtMật khẩu này không hợp lệCập nhật mật khẩu thất bại
@@ -839,7 +817,6 @@
Máy chủĐăng nhập tài khoảnĐăng nhập
-
%1$s @ %2$sThấy lần cuốiCập nhật Tên công khai
@@ -863,7 +840,7 @@
Rung khi đề cập tên người dùngBao gồm thay đổi tên hiển thị và hình đại diện.Hiện các sự kiện của tài khoản
- Sự kiện mời, loại hoặc cấm thành viên không bị ảnh hưởng.
+ Sự kiện mời, gỡ hoặc cấm thành viên không bị ảnh hưởng.Hiện sự kiện tham gia hoặc rời phòngGõ lệnh /confetti hoặc gửi tin chứa ❄️ hoặc 🎉Hiện hiệu ứng chat
@@ -871,7 +848,7 @@
Hiện thông báo tin đã đọcHiện dấu thời gian theo chuẩn 12-giờHiện Dấu thời gian cho tất cả tin nhắn
- Định dạng tin nhắn theo chuẩn Markdown trước khi gửi.
+ Định dạng tin nhắn bằng cú pháp của markdown trước khi chúng được gửi đi. Điều này cho phép định dạng nâng cao, chẳng hạn như sử dụng dấu hoa thị để hiển thị văn bản in nghiêng.Sử dụng định dạng MarkdownCho người dùng khác biết bạn đã gõ phím.Gửi thông báo đang gõ tin nhắn
@@ -905,7 +882,6 @@
%d giâyThời gian chờ giữa 2 lần đồng bộ
-
Mã nhập vào không hợp lệ. Vui lòng kiểm tra.Chúng tôi vừa gửi email tới %1$s.
\nClick vào đường link trong email để tiếp tục quá trình tạo tài khoản.
@@ -1029,13 +1005,10 @@
KhôngĐược đề cập và đúng từ khóaTất cả tin
-
-
Không có kết quảLọc người dùng bị cấmLọc thành viên phòngTìm kiếm
-
%d được chọn
@@ -1075,7 +1048,7 @@
%1$s & %2$s đang gõ…%s đang gõ…Việc hủy cấm người dùng sẽ cho phép họ tham gia lại phòng.
- Việc cấm người dùng sẽ đá họ ra khỏi phòng này và ngăn họ tham gia lại.
+ Việc cấm người dùng sẽ gỡ họ ra khỏi phòng này và ngăn họ tham gia lại.Hủy cấm người dùngLý do cấmCấm người dùng
@@ -1087,7 +1060,7 @@
Bạn có chắc bạn muốn hủy lời mời đối với người dùng này không\?Hủy lời mờiHủy làm ngơ
- Việc hủy làm ngơ người dùng này sẽ hiện lại tất cả tin nhắn từ họ.
+ Việc bỏ làm ngơ người dùng này sẽ hiện lại tất cả tin nhắn từ họ.Cuộc gọi âm thanh với %sCuộc gọi video với %sCuộc gọi đang reo…
@@ -1095,7 +1068,6 @@
Truy cập không gianAi có quyền truy cập\?Vui lòng kiểm tra email và bấm vào liên kết trong đó. Một khi xong, bấm tiếp tục.
-
Sử dụng trình quản lý chung để quản lý bot, các cầu nối, widget và các gói nhãn dán.
\nTrình quản lý chung sẽ nhận được dữ liệu hiệu chỉnh, và sẽ có thể điều chỉnh các widget, gửi lời mời vào phòng và thiết lập các mốc quyền lợi theo ý bạn.${app_name} sẽ đồng bộ hóa dưới nền trong một khoảng thời gian nhất định (có thể điều chỉnh thời gian).
@@ -1169,11 +1141,10 @@
Chứng chỉ đã bị thay đổi từ một thiết bị được tin cậy của bạn. Điều này RẤT BẤT THƯỜNG. Chúng tôi khuyên bạn KHÔNG NÊN CHẤP NHẬN chứng chỉ mới này.Nếu quản trị viên của máy chủ đã nói rằng điều này có thể xảy ra, hãy chắc chắn rằng dấu vân tay phía dưới trùng với dấu vân tay được họ cung cấp.Hủy cấm người dùng sẽ cho phép họ tham gia không gian này lần nữa.
- Cấm người dùng này sẽ đá họ khỏi không gian này và ngăn chặn họ tiếp tục tham gia.
+ Cấm người dùng này sẽ gỡ họ khỏi không gian này và ngăn chặn họ tiếp tục tham gia.hành động của bạn sẽ xóa họ khỏi không gian này.
\n
\nTrong trường hợp không muốn họ quay lại, bạn nên cấm họ tham gia lần nữa.
-
%d mục
@@ -1202,10 +1173,8 @@
%d khóa mới vừa được thêm vào phiên này.
- Đã khôi phục bản sao lưu với % d chìa khóa.
+ Đã khôi phục bản sao lưu với %d chìa khóa.
-
-
%d widget hoạt động
@@ -1221,7 +1190,6 @@
%d phòng
-
%d tin nhắn được thông báo chưa đọc
@@ -1263,7 +1231,7 @@
Phát Tin nhắn ThoạiVuốt để hủyGhi âm tin nhắn thoại
- Xin lỗi, lỗi đã xảy ra trong khi cố gắng gia nhập: %s
+ Xin lỗi, đã có lỗi đã xảy ra trong khi cố gắng gia nhập: %sNâng cấp lên phiên bản phòng được đề xuấtPhòng này đang chạy phiên bản phòng %s, mà homeerver này đã đánh dấu là không ổn định.Bạn cần sự cho phép để nâng cấp một phòng
@@ -1278,7 +1246,6 @@
Nâng cấpHãy kiên nhẫn, có thể mất một thời gian.Tham gia phòng thay thế
-
Phòng không tênMột số phòng có thể bị ẩn vì chúng riêng tư và bạn cần một lời mời.Một số phòng có thể bị ẩn vì chúng riêng tư và bạn cần một lời mời.
@@ -1311,7 +1278,7 @@
Bạn sẽ không thể tham gia lại trừ khi bạn được mời lại.Bạn là người duy nhất ở đây. Nếu bạn rời đi, sẽ không ai có thể tham gia trong tương lai, kể cả bạnBạn có chắc chắn muốn rời khỏi %s không\?
- Rời khỏi Space
+ Rời khỏiThêm phòngKhám phá phòngKhám phá (%s)
@@ -1320,7 +1287,7 @@
Hoàn tất việc cài đặt khám phá.Hiện tại bạn không sử dụng máy chủ xác thực. Để mời đồng đội và có thể khám phá bởi họ, hãy cấu hình một bên dưới.Tham gia Space
- Tạo Space
+ Tạo spaceBỏ qua ngay bây giờGia nhập Space của tôi %1$s %2$sMời theo tên người dùng hoặc thư
@@ -1351,14 +1318,14 @@
Tôi và các đồng độiMột Space riêng tư để sắp xếp các phòng của bạnChỉ tôi
- Đảm bảo đúng người có quyền truy nhập vào %s. Bạn có thể thay đổi điều này sau.
+ Đảm bảo đúng người có quyền truy nhập vào %s.Bạn làm việc với ai\?Để tham gia một Space hiện có, bạn cần một lời mời.Bạn có thể thay đổi điều này sauBạn muốn tạo ra loại Space nào\?Space riêng tư của bạnSpace công cộng của bạn
- Thêm Space
+ Thêm spaceSpace riêng tưSpace công cộngTin nhắn gửi thất bại
@@ -1541,13 +1508,13 @@
\n${app_name} Android${app_name} Web
\n${app_name} Desktop
- Sử dụng ${app_name} mới nhất trên các thiết bị khác của bạn, Web ${app_name}, Máy tính để bàn ${app_name}, ${app_name} iOS, ${app_name} cho Android hoặc một máy khách Matrix có khả năng xác thực chéo khác
+ Sử dụng ${app_name} mới nhất trên các thiết bị khác của bạn, Web ${app_name}, Máy tính để bàn ${app_name}, iOS ${app_name}, ${app_name} cho Android hoặc một máy khách Matrix có khả năng xác thực chéo khácĐặt mật khẩu tài khoản mới…Không thể lưu tệp MediaBật thiết đặt này thêm FLAG_SECURE cho tất cả các Hoạt động. Khởi động lại ứng dụng để thay đổi có hiệu lực.Ngăn ảnh chụp màn hình của ứng dụngKhóa khôi phục Sao lưu Chính
- Không biết cụm mật khẩu Sao lưu Khóa của bạn, bạn có thể %s.
+ Cụm Mã Khóa Sao lưu của bạn không xác định, bạn có thể %s.sử dụng khóa khôi phục Sao lưu Khóa của bạnNhập chìa khóa Cụm mật khẩu Sao lưu của bạn để tiếp tục.Lưu trữ bí mật khóa sao lưu trong SSSS
@@ -1576,7 +1543,7 @@
Kết quả cuối cùng dựa trên %1$d phiếu bầu
- Đã bỏ %1$d phiếu bầu. Bỏ phiếu để xem kết quả
+ %1$d phiếu bầu. Bỏ phiếu để xem kết quảDựa trên %1$d phiếu bầu
@@ -1593,7 +1560,6 @@
%1$d cuộc gọi đang hoạt động ·
-
Gần xong! Thiết bị khác có hiển thị dấu tick không\?"Chủ đề: "Thêm chủ đề
@@ -1613,7 +1579,6 @@
Nếu bạn hủy ngay bây giờ, bạn có thể mất tin nhắn và dữ liệu được mã hóa nếu bạn mất quyền truy cập vào thông tin đăng nhập của mình.
\n
\nBạn cũng có thể thiết lập Sao lưu Bảo mật và quản lý khóa của mình trong Cài đặt.
-
Sao chép nó vào bộ nhớ đám mây cá nhân của bạnLưu nó trên khóa USB hoặc ổ đĩa sao lưuIn nó và lưu trữ nó ở đâu đó an toàn
@@ -1635,9 +1600,9 @@
Xác thực chéo không được bậtXác thực chéo được kích hoạt.
\nCác khóa không đáng tin cậy
- Xác thực chéo chéo được kích hoạt
-\nChìa khóa được tin cậy.
-\nKhóa riêng tư không được biết
+ Xác thực chéo được kích hoạt
+\nCác mã khóa là đáng tin cậy.
+\nCác khóa riêng tư không xác địnhXác thực chéo được kích hoạt
\nKhóa riêng trên thiết bị.Xác thực chéo
@@ -1691,7 +1656,6 @@
\nTin nhắn của bạn được bảo mật bằng khóa và chỉ có bạn và người nhận có các khóa duy nhất để mở khóa chúng.Tin nhắn ở đây không được mã hóa đầu cuối.Tin nhắn trong phòng này không được mã hóa đầu cuối.
-
Đang chờ %s…Đã xác minh %sXác minh %s
@@ -1749,7 +1713,7 @@
Hiện thông tin debug trên màn hình${app_name} có thể gặp sự cố thường xuyên hơn khi một lỗi bất ngờ xảy raThất bại nhanh
- Chỉ hiển thị kết quả đầu tiên, nhập thêm chữ cái
+ Chỉ hiển thị kết quả đầu tiên, nhập thêm chữ cái…Các phiên khácPhiên hiện tạiCài đặt
@@ -1848,8 +1812,6 @@
Nhập URL máy chủ xác thựcBạn có đồng ý gửi thông tin này không\?Để khám phá các liên hệ hiện có, bạn cần gửi thông tin liên hệ (email và số điện thoại) đến máy chủ nhận dạng của mình. Chúng tôi băm dữ liệu của bạn trước khi gửi cho quyền riêng tư.
-
-
Gửi email và số điện thoại đến %sĐồng ýThu hồi sự đồng ý của tôi
@@ -1873,7 +1835,7 @@
Phiên bản Matrix SDKNhập khóa e2e từ tệp \"%1$s\".Lỗi xảy ra khi nhận được dữ liệu sao lưu khóa
- Lỗi xảy ra khi nhận được thông tin tin cậy
+ Lỗi xảy ra khi nhận được thông tin xác thựcCăn phòng đã được tạo ra, nhưng một số lời mời đã không được gửi vì lý do sau:
\n
\n%s
@@ -1917,11 +1879,8 @@
Lỗi không xác định%s muốn xác minh phiên của bạnYêu cầu xác minh
-
-
Đã nhận đượcĐã xác minh!
-
Chữ kýThuật toánPhiên bản
@@ -1934,7 +1893,6 @@
Không bao giờ mất tin nhắn được mã hóaBảo vệ chống mất quyền truy cập vào tin nhắn và dữ liệu được mã hóaSao lưu An toàn
-
Xóa khóa mã hóa đã sao lưu của bạn khỏi máy chủ\? Bạn sẽ không còn có thể sử dụng khóa khôi phục của mình để đọc lịch sử tin nhắn được mã hóa.Xóa Sao lưuKiểm tra trạng thái sao lưu
@@ -1964,7 +1922,7 @@
Mất chìa khóa phục hồi\? Bạn có thể thiết lập một cái mới trong cài đặt.Nhập Khóa Khôi phụcSử dụng Khóa Khôi phục của bạn để mở khóa lịch sử tin nhắn được mã hóa của bạn
- Không biết cụm mật khẩu phục hồi của bạn, bạn có thể %s.
+ Cụm mật khẩu phục hồi của bạn chưa xác định, bạn có thể %s.sử dụng khóa khôi phục của bạnSử dụng cụm mật khẩu khôi phục của bạn để mở khóa lịch sử tin nhắn được mã hóa của bạnĐang tìm phiên bản sao lưu…
@@ -1980,7 +1938,6 @@
Có vẻ như bạn đã thiết lập bản sao lưu khóa từ một phiên khác. Bạn có muốn thay thế nó bằng cái mà bạn đang tạo ra không\?Một bản sao lưu đã tồn tại trên homeerver của bạnKhóa phục hồi đã được lưu.
-
Lưu dưới dạng TệpChia sẻLưu Khóa Khôi phục
@@ -2051,7 +2008,7 @@
Bạn có chắc chắn muốn loại bỏ (xóa) sự kiện này không\? Lưu ý rằng nếu bạn xóa tên phòng hoặc thay đổi chủ đề, nó có thể hoàn tác thay đổi.Xác nhận Loại bỏGửi media với kích thước ban đầu
- Bạn có muốn gửi phần đính kèm này đến %1$s không\?
+ Bạn có muốn gửi tệp đính kèm này đến %1$s\?Xóa…Không thể tìm thấy bí mật trong khoNếu bạn không thể truy nhập phiên hiện có
@@ -2065,7 +2022,7 @@
Kết nối với máy chủ đã bị mấtKhôngCó
- Gần xong! %s có hiển thị dấu tích không
+ Đã gần xong! %s Có hiển thị dấu tích không\?Mã QRĐặt lại khóaKhởi tạo xác thực chéo
@@ -2202,7 +2159,8 @@
Thu hồi quyền truy cập cho tôiMở trong trình duyệtTải lại widget
- Thất bại trong việc tải widget.%s
+ Thất bại trong việc tải widget.
+\n%sSử dụng nó có thể chia sẻ dữ liệu với %s:Sử dụng nó có thể đặt cookie và chia sẻ dữ liệu với %s:Widget này được thêm vào bởi:
@@ -2319,4 +2277,4 @@
Gửi ảnh với kích thước gốc
-
\ No newline at end of file
+
diff --git a/vector/src/main/res/values-zh-rCN/strings.xml b/vector/src/main/res/values-zh-rCN/strings.xml
index ec3045ec22..0130c20879 100644
--- a/vector/src/main/res/values-zh-rCN/strings.xml
+++ b/vector/src/main/res/values-zh-rCN/strings.xml
@@ -475,7 +475,6 @@
%d 条未读消息显示成员
- 徽章%d 个聊天室
@@ -2294,4 +2293,5 @@
在房间中筛选子区复制子区的链接在房间中查看
-
\ 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 f93374acdb..8fd51266f5 100644
--- a/vector/src/main/res/values-zh-rTW/strings.xml
+++ b/vector/src/main/res/values-zh-rTW/strings.xml
@@ -450,11 +450,8 @@
新密碼更新密碼失敗您的密碼已經更新
- 顯示所有來自 %s 的訊息?
-\n
-\n注意此動作將會重新啟動應用程式,而其可能需要一點時間。
+ 顯示所有來自 %s 的訊息?選擇國家
- 特色3 天1 週1 個月
@@ -1501,9 +1498,7 @@
\n
\n您隨時都可以在一般設定中撤銷此動作。取消忽略使用者
- 取消忽略此使用者將再次顯示從他們而來的所有訊息。
-\n
-\n注意,此動作將會重新啟動應用程式,且可能需要一些時間。
+ 取消忽略此使用者將再次顯示從他們而來的所有訊息。取消邀請您確定您想要取消對此使用者的邀請嗎?踢除使用者
@@ -2305,8 +2300,6 @@
開啟以${app_name} 無法存取您的位置。請稍後再試。${app_name} 無法存取您的位置
- 分享位置
- 分享位置位置分享位置結果僅在您結束投票後顯示
@@ -2440,4 +2433,14 @@
討論串測試版取得更多資訊試試看
-
\ No newline at end of file
+ 正在分享畫面
+ ${app_name} 分享畫面中
+ 停止分享畫面
+ 分享畫面
+ - 部份使用者已被取消忽略
+ ${app_name} 需要執行清除快取以保持最新狀態,原因如下:
+\n%s
+\n
+\n注意,此動作將會重新啟動應用程式,並可能需要一些時間。
+ 初始同步請求
+
diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml
index 3e6bdad70b..63d4730dc5 100644
--- a/vector/src/main/res/values/strings.xml
+++ b/vector/src/main/res/values/strings.xml
@@ -322,6 +322,13 @@
Start ChattingSpaces
+
+ h
+
+ min
+
+ sec
+
Some permissions are missing to perform this action, please grant the permissions from the system settings.To perform this action, please grant the Camera permission from the system settings.
@@ -1056,9 +1063,6 @@
ChoosePlay shutter sound
-
- Flair
-
3 days1 week
@@ -1670,7 +1674,6 @@
You may contact me if you have any follow up questionsThanks, your feedback has been successfully sentThe feedback failed to be sent (%s)
-
Give FeedbackGive FeedbackThreads Beta feedback
@@ -2834,14 +2837,27 @@
Explore roomsAdd roomsLeave
+
+ Things in this space
+ Leave all
+ Leave none
+
Are you sure you want to leave %s?You are the only person here. If you leave, no one will be able to join in the future, including you.You won\'t be able to rejoin unless you are re-invited.You\'re the only admin of this space. Leaving it will mean no one has control over it.
- Leave all rooms and spaces
- Don’t leave any rooms and spaces
- Leave specific rooms and spaces…
- Pick things to leave
+
+
+ Leave all rooms and spaces
+
+ Don’t leave any rooms and spaces
+
+ Leave specific rooms and spaces…
+
+ Pick things to leave
+
+ No results found
+ Some results may be hidden because they’re private and you need an invite to them.Add existing rooms and spaceAdd existing rooms
@@ -2986,11 +3002,7 @@
Share locationLocation
-
- Share locationMap
-
- Share locationPin of selected location on mapZoom to current locationShare my current location
@@ -3014,9 +3026,17 @@
Failed to load mapLive location enabledLoading live location…
+ Live location ended
+ View live location
+
+ Live until %1$sStop
+
+ %1$s left${app_name} Live LocationLocation sharing is in progress
+ Enable Live Location Sharing
+ Temporary implementation: locations persist in room historyShow Message bubbles
diff --git a/vector/src/main/res/xml/vector_settings_labs.xml b/vector/src/main/res/xml/vector_settings_labs.xml
index 8b25a8c287..19ee7e366f 100644
--- a/vector/src/main/res/xml/vector_settings_labs.xml
+++ b/vector/src/main/res/xml/vector_settings_labs.xml
@@ -69,4 +69,10 @@
android:key="SETTINGS_LABS_RENDER_LOCATIONS_IN_TIMELINE"
android:title="@string/labs_render_locations_in_timeline" />
+
+
diff --git a/vector/src/test/java/im/vector/app/features/onboarding/DirectLoginUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/onboarding/DirectLoginUseCaseTest.kt
index 6021b755f4..1fed03b601 100644
--- a/vector/src/test/java/im/vector/app/features/onboarding/DirectLoginUseCaseTest.kt
+++ b/vector/src/test/java/im/vector/app/features/onboarding/DirectLoginUseCaseTest.kt
@@ -27,7 +27,7 @@ import kotlinx.coroutines.test.runTest
import org.amshove.kluent.should
import org.amshove.kluent.shouldBeEqualTo
import org.junit.Test
-import org.matrix.android.sdk.api.MatrixPatterns.getDomain
+import org.matrix.android.sdk.api.MatrixPatterns.getServerName
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
import org.matrix.android.sdk.api.auth.data.WellKnown
import org.matrix.android.sdk.api.auth.wellknown.WellknownResult
@@ -38,7 +38,7 @@ private val A_WELLKNOWN_FAILED_WITH_CONTENT_RESULT = WellknownResult.FailPrompt(
private val A_WELLKNOWN_FAILED_WITHOUT_CONTENT_RESULT = WellknownResult.FailPrompt(null, null)
private val NO_HOMESERVER_CONFIG: HomeServerConnectionConfig? = null
private val A_FALLBACK_CONFIG: HomeServerConnectionConfig = HomeServerConnectionConfig(
- homeServerUri = FakeUri("https://${A_LOGIN_OR_REGISTER_ACTION.username.getDomain()}").instance,
+ homeServerUri = FakeUri("https://${A_LOGIN_OR_REGISTER_ACTION.username.getServerName()}").instance,
homeServerUriBase = FakeUri(A_WELLKNOWN_SUCCESS_RESULT.homeServerUrl).instance,
identityServerUri = null
)