diff --git a/CHANGES.md b/CHANGES.md index 4d2394db1f..b1b0deee2c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,15 @@ +Changes in Element v1.3.17 (2022-01-31) +======================================= + +Bugfixes 🐛 +---------- + - Display static map images in the timeline and improve Location sharing feature ([#5084](https://github.com/vector-im/element-android/issues/5084)) + - Show the legal mention of mapbox when sharing location ([#5062](https://github.com/vector-im/element-android/issues/5062)) + - Poll cannot end in some unencrypted rooms ([#5067](https://github.com/vector-im/element-android/issues/5067)) + - Selecting Transfer in a call should immediately put the other person on hold until the call connects or the Transfer is cancelled. ([#5081](https://github.com/vector-im/element-android/issues/5081)) + - Fixing crashes when quickly scrolling or restoring the room timeline ([#5091](https://github.com/vector-im/element-android/issues/5091)) + + Changes in Element 1.3.16 (2022-01-25) ====================================== diff --git a/README.md b/README.md index dedc9da2dd..d784841e2c 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,8 @@ It is a total rewrite of [Riot-Android](https://github.com/vector-im/riot-androi [Get it on Google Play](https://play.google.com/store/apps/details?id=im.vector.app) [Get it on F-Droid](https://f-droid.org/app/im.vector.app) -Nightly build: [![Buildkite](https://badge.buildkite.com/ad0065c1b70f557cd3b1d3d68f9c2154010f83c4d6f71706a9.svg?branch=develop)](https://buildkite.com/matrix-dot-org/element-android/builds?branch=develop) +Nightly build: [![Buildkite](https://badge.buildkite.com/ad0065c1b70f557cd3b1d3d68f9c2154010f83c4d6f71706a9.svg?branch=develop)](https://buildkite.com/matrix-dot-org/element-android/builds?branch=develop) Nighly sanity test status: [![allScreensTest](https://github.com/vector-im/element-android/actions/workflows/sanity_test.yml/badge.svg)](https://github.com/vector-im/element-android/actions/workflows/sanity_test.yml) + # New Android SDK diff --git a/changelog.d/5062.bugfix b/changelog.d/5062.bugfix deleted file mode 100644 index ec24bfd6c1..0000000000 --- a/changelog.d/5062.bugfix +++ /dev/null @@ -1 +0,0 @@ -Show the legal mention of mapbox when sharing location \ No newline at end of file diff --git a/changelog.d/5067.bugfix b/changelog.d/5067.bugfix deleted file mode 100644 index 7ad88b608d..0000000000 --- a/changelog.d/5067.bugfix +++ /dev/null @@ -1 +0,0 @@ -Poll cannot end in some unencrypted rooms \ No newline at end of file diff --git a/fastlane/metadata/android/cs-CZ/changelogs/40103130.txt b/fastlane/metadata/android/cs-CZ/changelogs/40103130.txt new file mode 100644 index 0000000000..dab96ddd72 --- /dev/null +++ b/fastlane/metadata/android/cs-CZ/changelogs/40103130.txt @@ -0,0 +1,2 @@ +Hlavní změny v této verzi: Změna na úvodních obrazovkách, včetně přihlášení do služby Analytics. V experimentálních funkcích byla přidána podpora pro události s matematikou. +Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.3.13 diff --git a/fastlane/metadata/android/cs-CZ/changelogs/40103140.txt b/fastlane/metadata/android/cs-CZ/changelogs/40103140.txt new file mode 100644 index 0000000000..8d07600bc1 --- /dev/null +++ b/fastlane/metadata/android/cs-CZ/changelogs/40103140.txt @@ -0,0 +1,2 @@ +Hlavní změny v této verzi: Změna na úvodních obrazovkách, včetně přihlášení do služby Analytics. V experimentálních funkcích byla přidána podpora pro události s matematikou. +Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.3.14 diff --git a/fastlane/metadata/android/cs-CZ/changelogs/40103150.txt b/fastlane/metadata/android/cs-CZ/changelogs/40103150.txt new file mode 100644 index 0000000000..260011b49d --- /dev/null +++ b/fastlane/metadata/android/cs-CZ/changelogs/40103150.txt @@ -0,0 +1,2 @@ +Hlavní změny v této verzi: Změna na úvodních obrazovkách, včetně přihlášení do služby Analytics. V experimentálních funkcích byla přidána podpora pro události s matematikou. +Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.3.15 diff --git a/fastlane/metadata/android/de-DE/changelogs/40103130.txt b/fastlane/metadata/android/de-DE/changelogs/40103130.txt new file mode 100644 index 0000000000..82aaadd5f3 --- /dev/null +++ b/fastlane/metadata/android/de-DE/changelogs/40103130.txt @@ -0,0 +1,2 @@ +Hauptänderungen: Neues Onboarding, Unterstützung für Mathematische Ausdrücke in Labs +Änderungsliste: https://github.com/vector-im/element-android/releases/tag/v1.3.13 diff --git a/fastlane/metadata/android/de-DE/changelogs/40103140.txt b/fastlane/metadata/android/de-DE/changelogs/40103140.txt new file mode 100644 index 0000000000..6032784f64 --- /dev/null +++ b/fastlane/metadata/android/de-DE/changelogs/40103140.txt @@ -0,0 +1,2 @@ +Hauptänderungen: Neues Onboarding, Unterstützung für Mathematische Ausdrücke in Labs +Änderungsliste: https://github.com/vector-im/element-android/releases/tag/v1.3.14 diff --git a/fastlane/metadata/android/de-DE/changelogs/40103150.txt b/fastlane/metadata/android/de-DE/changelogs/40103150.txt new file mode 100644 index 0000000000..6e324d4ef5 --- /dev/null +++ b/fastlane/metadata/android/de-DE/changelogs/40103150.txt @@ -0,0 +1,2 @@ +Hauptänderungen: Neues Onboarding, Unterstützung für Mathematische Ausdrücke in Labs +Änderungsliste: https://github.com/vector-im/element-android/releases/tag/v1.3.15 diff --git a/fastlane/metadata/android/en-US/changelogs/40103170.txt b/fastlane/metadata/android/en-US/changelogs/40103170.txt new file mode 100644 index 0000000000..2d0062bb23 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/40103170.txt @@ -0,0 +1,2 @@ +Main changes in this version: send your location to any room. Edit poll. +Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.3.17 \ No newline at end of file diff --git a/fastlane/metadata/android/et/changelogs/40103130.txt b/fastlane/metadata/android/et/changelogs/40103130.txt new file mode 100644 index 0000000000..b2c8054559 --- /dev/null +++ b/fastlane/metadata/android/et/changelogs/40103130.txt @@ -0,0 +1,2 @@ +Põhilised muutused selles versioonis: liitumisvaate täiendused, võimalus saata meile analüütikat. Katsete alla on lisandunud üritused ning matemaatiliste valemite kirjutamise võimalus. +Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.3.13 diff --git a/fastlane/metadata/android/et/changelogs/40103140.txt b/fastlane/metadata/android/et/changelogs/40103140.txt new file mode 100644 index 0000000000..14c34169d0 --- /dev/null +++ b/fastlane/metadata/android/et/changelogs/40103140.txt @@ -0,0 +1,2 @@ +Põhilised muutused selles versioonis: liitumisvaate täiendused, võimalus saata meile analüütikat. Katsete alla on lisandunud üritused ning matemaatiliste valemite kirjutamise võimalus. +Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.3.14 diff --git a/fastlane/metadata/android/et/changelogs/40103150.txt b/fastlane/metadata/android/et/changelogs/40103150.txt new file mode 100644 index 0000000000..ce86924106 --- /dev/null +++ b/fastlane/metadata/android/et/changelogs/40103150.txt @@ -0,0 +1,2 @@ +Põhilised muutused selles versioonis: liitumisvaate täiendused, võimalus saata meile analüütikat. Katsete alla on lisandunud üritused ning matemaatiliste valemite kirjutamise võimalus. +Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.3.15 diff --git a/fastlane/metadata/android/hu-HU/changelogs/40103130.txt b/fastlane/metadata/android/hu-HU/changelogs/40103130.txt new file mode 100644 index 0000000000..4c39f156f9 --- /dev/null +++ b/fastlane/metadata/android/hu-HU/changelogs/40103130.txt @@ -0,0 +1,2 @@ +Fő változás ebben a verzióban: Első változások a bemutató képernyőn, beleértve az analitikai adatküldés engedélyezésének lehetőségét. Matematikai formulák támogatása a Laborok között. +Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.3.13 diff --git a/fastlane/metadata/android/hu-HU/changelogs/40103140.txt b/fastlane/metadata/android/hu-HU/changelogs/40103140.txt new file mode 100644 index 0000000000..b53f857896 --- /dev/null +++ b/fastlane/metadata/android/hu-HU/changelogs/40103140.txt @@ -0,0 +1,2 @@ +Fő változás ebben a verzióban: Első változások a bemutató képernyőn, beleértve az analitikai adatküldés engedélyezésének lehetőségét. Matematikai formulák támogatása a Laborok között. +Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.3.14 diff --git a/fastlane/metadata/android/hu-HU/changelogs/40103150.txt b/fastlane/metadata/android/hu-HU/changelogs/40103150.txt new file mode 100644 index 0000000000..abb4bf336a --- /dev/null +++ b/fastlane/metadata/android/hu-HU/changelogs/40103150.txt @@ -0,0 +1,2 @@ +Fő változás ebben a verzióban: Első változások a bemutató képernyőn, beleértve az analitikai adatküldés engedélyezésének lehetőségét. Matematikai formulák támogatása a Laborok között. +Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.3.15 diff --git a/fastlane/metadata/android/id/changelogs/40103130.txt b/fastlane/metadata/android/id/changelogs/40103130.txt new file mode 100644 index 0000000000..26a784d62a --- /dev/null +++ b/fastlane/metadata/android/id/changelogs/40103130.txt @@ -0,0 +1,2 @@ +Perubahan utama dalam versi ini: Perubahan pertama di layar permulaan, termasuk analitik opt-in. Dukungan untuk Peristiwa dengan Matematika ditambahkan di Uji Coba. +Changelog lanjutan: diff --git a/fastlane/metadata/android/id/changelogs/40103140.txt b/fastlane/metadata/android/id/changelogs/40103140.txt new file mode 100644 index 0000000000..dfefff307f --- /dev/null +++ b/fastlane/metadata/android/id/changelogs/40103140.txt @@ -0,0 +1,2 @@ +Perubahan utama dalam versi ini: Perubahan pertama di layar permulaan, termasuk analitik opt-in. Dukungan untuk Peristiwa dengan Matematika ditambahkan di Uji Coba. +Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.3.14 diff --git a/fastlane/metadata/android/id/changelogs/40103150.txt b/fastlane/metadata/android/id/changelogs/40103150.txt new file mode 100644 index 0000000000..c46e661d47 --- /dev/null +++ b/fastlane/metadata/android/id/changelogs/40103150.txt @@ -0,0 +1,2 @@ +Perubahan utama dalam versi ini: Perubahan pertama di layar permulaan, termasuk analitik opt-in. Dukungan untuk Peristiwa dengan Matematika ditambahkan di Uji Coba. +Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.3.15 diff --git a/fastlane/metadata/android/it-IT/changelogs/40103130.txt b/fastlane/metadata/android/it-IT/changelogs/40103130.txt new file mode 100644 index 0000000000..d113b7ac66 --- /dev/null +++ b/fastlane/metadata/android/it-IT/changelogs/40103130.txt @@ -0,0 +1,2 @@ +Modifiche principali in questa versione: prima modifica nelle schermate onboarding, incluso l'opt-in di Analytics. Supporto agli eventi con Math aggiunto nei laboratori. +Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.3.13 diff --git a/fastlane/metadata/android/it-IT/changelogs/40103140.txt b/fastlane/metadata/android/it-IT/changelogs/40103140.txt new file mode 100644 index 0000000000..b875832368 --- /dev/null +++ b/fastlane/metadata/android/it-IT/changelogs/40103140.txt @@ -0,0 +1,2 @@ +Modifiche principali in questa versione: prima modifica nelle schermate onboarding, incluso l'opt-in di Analytics. Supporto agli eventi con Math aggiunto nei laboratori. +Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.3.14 diff --git a/fastlane/metadata/android/it-IT/changelogs/40103150.txt b/fastlane/metadata/android/it-IT/changelogs/40103150.txt new file mode 100644 index 0000000000..0e7586be19 --- /dev/null +++ b/fastlane/metadata/android/it-IT/changelogs/40103150.txt @@ -0,0 +1,2 @@ +Modifiche principali in questa versione: prima modifica nelle schermate onboarding, incluso l'opt-in di Analytics. Supporto agli eventi con Math aggiunto nei laboratori. +Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.3.15 diff --git a/fastlane/metadata/android/pt-BR/changelogs/40103130.txt b/fastlane/metadata/android/pt-BR/changelogs/40103130.txt new file mode 100644 index 0000000000..6f4975d01c --- /dev/null +++ b/fastlane/metadata/android/pt-BR/changelogs/40103130.txt @@ -0,0 +1,2 @@ +Principais mudanças nesta versão: Primeira mudança em telas de onboarding, incluindo opt-in de Analítica. Suporte para Eventos com Matemática adicionado nos labs. +Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.3.13 diff --git a/fastlane/metadata/android/pt-BR/changelogs/40103140.txt b/fastlane/metadata/android/pt-BR/changelogs/40103140.txt new file mode 100644 index 0000000000..ab7470b96b --- /dev/null +++ b/fastlane/metadata/android/pt-BR/changelogs/40103140.txt @@ -0,0 +1,2 @@ +Principais mudanças nesta versão: Primeira mudança em telas de onboarding, incluindo opt-in de Analítica. Suporte para Eventos com Matemática adicionado nos labs. +Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.3.14 diff --git a/fastlane/metadata/android/pt-BR/changelogs/40103150.txt b/fastlane/metadata/android/pt-BR/changelogs/40103150.txt new file mode 100644 index 0000000000..40439e3f94 --- /dev/null +++ b/fastlane/metadata/android/pt-BR/changelogs/40103150.txt @@ -0,0 +1,2 @@ +Principais mudanças nesta versão: Primeira mudança em telas de onboarding, incluindo opt-in de Analítica. Suporte para Eventos com Matemática adicionado nos labs. +Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.3.15 diff --git a/fastlane/metadata/android/sk/changelogs/40103130.txt b/fastlane/metadata/android/sk/changelogs/40103130.txt new file mode 100644 index 0000000000..31f6cf31db --- /dev/null +++ b/fastlane/metadata/android/sk/changelogs/40103130.txt @@ -0,0 +1,2 @@ +Hlavné zmeny v tejto verzii: Prvá zmena v obrazovkách pri vstupe do systému vrátane prihlásenia do služby Analytics. Pridanie podpory pre udalosti s matematikou v laboratóriách. +Úplný zoznam zmien: https://github.com/vector-im/element-android/releases/tag/v1.3.13 diff --git a/fastlane/metadata/android/sk/changelogs/40103140.txt b/fastlane/metadata/android/sk/changelogs/40103140.txt new file mode 100644 index 0000000000..8e21829163 --- /dev/null +++ b/fastlane/metadata/android/sk/changelogs/40103140.txt @@ -0,0 +1,2 @@ +Hlavné zmeny v tejto verzii: Prvá zmena v obrazovkách pri vstupe do systému vrátane prihlásenia do služby Analytics. Pridanie podpory pre udalosti s matematikou v laboratóriách. +Úplný zoznam zmien: https://github.com/vector-im/element-android/releases/tag/v1.3.14 diff --git a/fastlane/metadata/android/sk/changelogs/40103150.txt b/fastlane/metadata/android/sk/changelogs/40103150.txt new file mode 100644 index 0000000000..7016270d76 --- /dev/null +++ b/fastlane/metadata/android/sk/changelogs/40103150.txt @@ -0,0 +1,2 @@ +Hlavné zmeny v tejto verzii: Prvá zmena v obrazovkách pri vstupe do systému vrátane prihlásenia do služby Analytics. Pridanie podpory pre udalosti s matematikou v laboratóriách. +Úplný zoznam zmien: https://github.com/vector-im/element-android/releases/tag/v1.3.15 diff --git a/fastlane/metadata/android/uk/changelogs/40103130.txt b/fastlane/metadata/android/uk/changelogs/40103130.txt new file mode 100644 index 0000000000..ad0bcda6e6 --- /dev/null +++ b/fastlane/metadata/android/uk/changelogs/40103130.txt @@ -0,0 +1,2 @@ +Основні зміни у цій версії: перша зміна на екрані привітання, включно з увімкненням аналітики. У лабораторії додано підтримку подій з математичними формулами. +Вичерпний перелік змін: https://github.com/vector-im/element-android/releases/tag/v1.3.13 diff --git a/fastlane/metadata/android/uk/changelogs/40103140.txt b/fastlane/metadata/android/uk/changelogs/40103140.txt new file mode 100644 index 0000000000..355f8add3e --- /dev/null +++ b/fastlane/metadata/android/uk/changelogs/40103140.txt @@ -0,0 +1,2 @@ +Основні зміни у цій версії: перша зміна на екрані привітання, включно з увімкненням аналітики. У лабораторії додано підтримку подій з математичними формулами. +Вичерпний перелік змін: https://github.com/vector-im/element-android/releases/tag/v1.3.14 diff --git a/fastlane/metadata/android/uk/changelogs/40103150.txt b/fastlane/metadata/android/uk/changelogs/40103150.txt new file mode 100644 index 0000000000..0d9d702f02 --- /dev/null +++ b/fastlane/metadata/android/uk/changelogs/40103150.txt @@ -0,0 +1,2 @@ +Основні зміни у цій версії: перша зміна на екрані привітання, включно з увімкненням аналітики. У лабораторії додано підтримку подій з математичними формулами. +Вичерпний перелік змін: https://github.com/vector-im/element-android/releases/tag/v1.3.15 diff --git a/fastlane/metadata/android/zh-TW/changelogs/40103130.txt b/fastlane/metadata/android/zh-TW/changelogs/40103130.txt new file mode 100644 index 0000000000..e0f9b47e16 --- /dev/null +++ b/fastlane/metadata/android/zh-TW/changelogs/40103130.txt @@ -0,0 +1,2 @@ +此版本中的主要變動:首次使用畫面的第一個變化,包含了選擇加入的分析功能。新增對數學活動的支援至實驗室中。 +完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.3.13 diff --git a/fastlane/metadata/android/zh-TW/changelogs/40103140.txt b/fastlane/metadata/android/zh-TW/changelogs/40103140.txt new file mode 100644 index 0000000000..8366a01265 --- /dev/null +++ b/fastlane/metadata/android/zh-TW/changelogs/40103140.txt @@ -0,0 +1,2 @@ +此版本中的主要變動:首次使用畫面的第一個變化,包含了選擇加入的分析功能。新增對數學活動的支援至實驗室中。 +完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.3.14 diff --git a/fastlane/metadata/android/zh-TW/changelogs/40103150.txt b/fastlane/metadata/android/zh-TW/changelogs/40103150.txt new file mode 100644 index 0000000000..a6ddd5aa8c --- /dev/null +++ b/fastlane/metadata/android/zh-TW/changelogs/40103150.txt @@ -0,0 +1,2 @@ +此版本中的主要變動:首次使用畫面的第一個變化,包含了選擇加入的分析功能。新增對數學活動的支援至實驗室中。 +完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.3.15 diff --git a/library/ui-styles/src/main/res/values-ldrtl/bools.xml b/library/ui-styles/src/main/res/values-ldrtl/bools.xml new file mode 100644 index 0000000000..27b280985f --- /dev/null +++ b/library/ui-styles/src/main/res/values-ldrtl/bools.xml @@ -0,0 +1,6 @@ + + + + true + + \ No newline at end of file diff --git a/library/ui-styles/src/main/res/values/bools.xml b/library/ui-styles/src/main/res/values/bools.xml index 93d5f925af..9966999f28 100644 --- a/library/ui-styles/src/main/res/values/bools.xml +++ b/library/ui-styles/src/main/res/values/bools.xml @@ -4,4 +4,6 @@ false + false + \ No newline at end of file diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index 57f40d1889..a4102f7441 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -164,7 +164,7 @@ dependencies { implementation libs.apache.commonsImaging // Phone number https://github.com/google/libphonenumber - implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.41' + implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.42' testImplementation libs.tests.junit testImplementation 'org.robolectric:robolectric:4.7.3' diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLocationContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLocationContent.kt index bf51e7177b..c090487c58 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLocationContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLocationContent.kt @@ -63,5 +63,5 @@ data class MessageLocationContent( @Json(name = "org.matrix.msc1767.text") val text: String? = null ) : MessageContent { - fun getUri() = locationInfo?.geoUri ?: geoUri + fun getBestGeoUri() = locationInfo?.geoUri ?: geoUri } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt index da15e158e5..8b05d2ea62 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt @@ -74,6 +74,7 @@ internal class DefaultProcessEventForPushTask @Inject constructor( event to it } } + Timber.d("[PushRules] matched ${matchedEvents.size} out of ${allEvents.size}") val allRedactedEvents = params.syncResponse.join .asSequence() diff --git a/vector/build.gradle b/vector/build.gradle index 1c65850a00..8792f526d4 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -373,7 +373,7 @@ dependencies { implementation 'com.facebook.stetho:stetho:1.6.0' // Phone number https://github.com/google/libphonenumber - implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.41' + implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.42' // FlowBinding implementation libs.github.flowBinding diff --git a/vector/src/androidTest/java/im/vector/app/EspressoExt.kt b/vector/src/androidTest/java/im/vector/app/EspressoExt.kt index 59ad122f36..ef76a9d1f3 100644 --- a/vector/src/androidTest/java/im/vector/app/EspressoExt.kt +++ b/vector/src/androidTest/java/im/vector/app/EspressoExt.kt @@ -195,6 +195,9 @@ fun activityIdlingResource(activityClass: Class<*>): IdlingResource { println("*** [$name] onActivityLifecycleChanged callback: $callback") callback?.onTransitionToIdle() } + else -> { + // do nothing, we're blocking until the activity resumes + } } } } diff --git a/vector/src/androidTest/java/im/vector/app/ui/robot/MessageMenuRobot.kt b/vector/src/androidTest/java/im/vector/app/ui/robot/MessageMenuRobot.kt index 934c6c76a1..5973dc3473 100644 --- a/vector/src/androidTest/java/im/vector/app/ui/robot/MessageMenuRobot.kt +++ b/vector/src/androidTest/java/im/vector/app/ui/robot/MessageMenuRobot.kt @@ -17,12 +17,16 @@ package im.vector.app.ui.robot import androidx.test.espresso.Espresso.pressBack +import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn import com.adevinta.android.barista.interaction.BaristaListInteractions.clickListItem import com.google.android.material.bottomsheet.BottomSheetBehavior import im.vector.app.R +import im.vector.app.espresso.tools.waitUntilActivityVisible +import im.vector.app.espresso.tools.waitUntilViewVisible import im.vector.app.features.home.room.detail.timeline.edithistory.ViewEditHistoryBottomSheet +import im.vector.app.features.reactions.EmojiReactionPickerActivity import im.vector.app.interactWithSheet import java.lang.Thread.sleep @@ -54,7 +58,10 @@ class MessageMenuRobot( fun addReactionFromEmojiPicker() { clickOn(R.string.message_add_reaction) // Wait for emoji to load, it's async now - sleep(2000) + waitUntilActivityVisible { + waitUntilViewVisible(withId(R.id.emojiRecyclerView)) + waitUntilViewVisible(withText("😀")) + } clickListItem(R.id.emojiRecyclerView, 4) autoClosed = true } diff --git a/vector/src/androidTest/java/im/vector/app/ui/robot/RoomDetailRobot.kt b/vector/src/androidTest/java/im/vector/app/ui/robot/RoomDetailRobot.kt index ebf5fdf23d..da57b105d7 100644 --- a/vector/src/androidTest/java/im/vector/app/ui/robot/RoomDetailRobot.kt +++ b/vector/src/androidTest/java/im/vector/app/ui/robot/RoomDetailRobot.kt @@ -20,13 +20,13 @@ import androidx.recyclerview.widget.RecyclerView import androidx.test.espresso.Espresso.onView import androidx.test.espresso.Espresso.pressBack import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.action.ViewActions.longClick import androidx.test.espresso.contrib.RecyclerViewActions import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText import com.adevinta.android.barista.interaction.BaristaClickInteractions import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn -import com.adevinta.android.barista.interaction.BaristaClickInteractions.longClickOn import com.adevinta.android.barista.interaction.BaristaEditTextInteractions.writeTo import com.adevinta.android.barista.interaction.BaristaMenuClickInteractions.clickMenu import com.adevinta.android.barista.interaction.BaristaMenuClickInteractions.openMenu @@ -70,6 +70,7 @@ class RoomDetailRobot { openMessageMenu(message) { addQuickReaction(quickReaction) } + waitUntilViewVisible(withText(quickReaction)) println("Open reactions bottom sheet") // Open reactions longClickReaction(quickReaction) @@ -103,7 +104,7 @@ class RoomDetailRobot { private fun longClickReaction(quickReaction: String) { withRetry { - longClickOn(quickReaction) + onView(withText(quickReaction)).perform(longClick()) } } diff --git a/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt index cdecd2d6c6..5295cbaec3 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt @@ -17,24 +17,25 @@ package im.vector.app.core.epoxy.bottomsheet import android.text.method.MovementMethod +import android.widget.FrameLayout import android.widget.ImageView import android.widget.TextView import androidx.core.view.isVisible import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass +import com.bumptech.glide.request.RequestOptions import im.vector.app.R import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.epoxy.onClick import im.vector.app.core.extensions.setTextOrHide +import im.vector.app.core.glide.GlideApp import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider import im.vector.app.features.home.room.detail.timeline.item.BindingOptions import im.vector.app.features.home.room.detail.timeline.tools.findPillsAndProcess -import im.vector.app.features.location.LocationData -import im.vector.app.features.location.MapTilerMapView import im.vector.app.features.media.ImageContentRenderer import im.vector.lib.core.utils.epoxy.charsequence.EpoxyCharSequence import org.matrix.android.sdk.api.util.MatrixItem @@ -70,7 +71,7 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel - holder.mapView.initialize { - if (holder.view.isAttachedToWindow) { - holder.mapView.zoomToLocation(location.latitude, location.longitude, 15.0) - locationPinProvider?.create(matrixItem.id) { pinDrawable -> - holder.mapView.addPinToMap(matrixItem.id, pinDrawable) - holder.mapView.updatePinLocation(matrixItem.id, location.latitude, location.longitude) - } - } + if (locationUrl == null) { + holder.body.isVisible = true + holder.mapViewContainer.isVisible = false + } else { + holder.body.isVisible = false + holder.mapViewContainer.isVisible = true + GlideApp.with(holder.staticMapImageView) + .load(locationUrl) + .apply(RequestOptions.centerCropTransform()) + .into(holder.staticMapImageView) + + locationPinProvider?.create(matrixItem.id) { pinDrawable -> + GlideApp.with(holder.staticMapPinImageView) + .load(pinDrawable) + .into(holder.staticMapPinImageView) } } } @@ -124,6 +129,8 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel(R.id.bottom_sheet_message_preview_body_details) val timestamp by bind(R.id.bottom_sheet_message_preview_timestamp) val imagePreview by bind(R.id.bottom_sheet_message_preview_image) - val mapView by bind(R.id.bottom_sheet_message_preview_location) + val mapViewContainer by bind(R.id.mapViewContainer) + val staticMapImageView by bind(R.id.staticMapImageView) + val staticMapPinImageView by bind(R.id.staticMapPinImageView) } } diff --git a/vector/src/main/java/im/vector/app/core/extensions/Session.kt b/vector/src/main/java/im/vector/app/core/extensions/Session.kt index 90b08ef92b..87ed51522f 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/Session.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/Session.kt @@ -21,6 +21,7 @@ import androidx.core.content.ContextCompat import androidx.lifecycle.Lifecycle import androidx.lifecycle.ProcessLifecycleOwner import im.vector.app.core.services.VectorSyncService +import im.vector.app.features.session.VectorSessionStore import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState import org.matrix.android.sdk.api.session.sync.FilterService @@ -76,3 +77,5 @@ fun Session.cannotLogoutSafely(): Boolean { // That are not backed up !sharedSecretStorageService.isRecoverySetup()) } + +fun Session.vectorStore(context: Context) = VectorSessionStore(context, myUserId) diff --git a/vector/src/main/java/im/vector/app/features/MainActivity.kt b/vector/src/main/java/im/vector/app/features/MainActivity.kt index b4706780b7..33b735551c 100644 --- a/vector/src/main/java/im/vector/app/features/MainActivity.kt +++ b/vector/src/main/java/im/vector/app/features/MainActivity.kt @@ -29,6 +29,7 @@ import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.extensions.startSyncing +import im.vector.app.core.extensions.vectorStore import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.utils.deleteAllFiles import im.vector.app.databinding.ActivityMainBinding @@ -40,6 +41,7 @@ import im.vector.app.features.pin.PinCodeStore import im.vector.app.features.pin.PinLocker import im.vector.app.features.pin.UnlockedActivity import im.vector.app.features.popup.PopupAlertManager +import im.vector.app.features.session.VectorSessionStore import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.signout.hard.SignedOutActivity import im.vector.app.features.themes.ActivityOtherThemes @@ -143,13 +145,15 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity startNextActivityAndFinish() return } + + val onboardingStore = session.vectorStore(this) when { args.isAccountDeactivated -> { lifecycleScope.launch { // Just do the local cleanup Timber.w("Account deactivated, start app") sessionHolder.clearActiveSession() - doLocalCleanup(clearPreferences = true) + doLocalCleanup(clearPreferences = true, onboardingStore) startNextActivityAndFinish() } } @@ -163,14 +167,14 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity } Timber.w("SIGN_OUT: success, start app") sessionHolder.clearActiveSession() - doLocalCleanup(clearPreferences = true) + doLocalCleanup(clearPreferences = true, onboardingStore) startNextActivityAndFinish() } } args.clearCache -> { lifecycleScope.launch { session.clearCache() - doLocalCleanup(clearPreferences = false) + doLocalCleanup(clearPreferences = false, onboardingStore) session.startSyncing(applicationContext) startNextActivityAndFinish() } @@ -183,7 +187,7 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity Timber.w("Ignoring invalid token global error") } - private suspend fun doLocalCleanup(clearPreferences: Boolean) { + private suspend fun doLocalCleanup(clearPreferences: Boolean, vectorSessionStore: VectorSessionStore) { // On UI Thread Glide.get(this@MainActivity).clearMemory() @@ -193,6 +197,7 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity pinLocker.unlock() pinCodeStore.deleteEncodedPin() vectorAnalytics.onSignOut() + vectorSessionStore.clear() } withContext(Dispatchers.IO) { // On BG thread diff --git a/vector/src/main/java/im/vector/app/features/analytics/AnalyticsTracker.kt b/vector/src/main/java/im/vector/app/features/analytics/AnalyticsTracker.kt index e1da0f4434..e85919a45f 100644 --- a/vector/src/main/java/im/vector/app/features/analytics/AnalyticsTracker.kt +++ b/vector/src/main/java/im/vector/app/features/analytics/AnalyticsTracker.kt @@ -18,6 +18,7 @@ package im.vector.app.features.analytics import im.vector.app.features.analytics.itf.VectorAnalyticsEvent import im.vector.app.features.analytics.itf.VectorAnalyticsScreen +import im.vector.app.features.analytics.plan.Identity interface AnalyticsTracker { /** @@ -29,4 +30,9 @@ interface AnalyticsTracker { * Track a displayed screen */ fun screen(screen: VectorAnalyticsScreen) + + /** + * Update user specific properties + */ + fun updateUserProperties(identity: Identity) } diff --git a/vector/src/main/java/im/vector/app/features/analytics/extensions/IdentityExt.kt b/vector/src/main/java/im/vector/app/features/analytics/extensions/IdentityExt.kt new file mode 100644 index 0000000000..d87769fd36 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/analytics/extensions/IdentityExt.kt @@ -0,0 +1,29 @@ +/* + * 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.analytics.extensions + +import im.vector.app.features.analytics.plan.Identity +import im.vector.app.features.onboarding.FtueUseCase + +fun FtueUseCase.toTrackingValue(): Identity.FtueUseCaseSelection { + return when (this) { + FtueUseCase.FRIENDS_FAMILY -> Identity.FtueUseCaseSelection.PersonalMessaging + FtueUseCase.TEAMS -> Identity.FtueUseCaseSelection.WorkMessaging + FtueUseCase.COMMUNITIES -> Identity.FtueUseCaseSelection.CommunityMessaging + FtueUseCase.SKIP -> Identity.FtueUseCaseSelection.Skip + } +} diff --git a/vector/src/main/java/im/vector/app/features/analytics/impl/DefaultVectorAnalytics.kt b/vector/src/main/java/im/vector/app/features/analytics/impl/DefaultVectorAnalytics.kt index eaf2e42705..d32cef604b 100644 --- a/vector/src/main/java/im/vector/app/features/analytics/impl/DefaultVectorAnalytics.kt +++ b/vector/src/main/java/im/vector/app/features/analytics/impl/DefaultVectorAnalytics.kt @@ -17,6 +17,7 @@ package im.vector.app.features.analytics.impl import android.content.Context +import com.posthog.android.Options import com.posthog.android.PostHog import com.posthog.android.Properties import im.vector.app.BuildConfig @@ -25,6 +26,7 @@ import im.vector.app.features.analytics.VectorAnalytics import im.vector.app.features.analytics.itf.VectorAnalyticsEvent import im.vector.app.features.analytics.itf.VectorAnalyticsScreen import im.vector.app.features.analytics.log.analyticsTag +import im.vector.app.features.analytics.plan.Identity import im.vector.app.features.analytics.store.AnalyticsStore import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.Flow @@ -34,6 +36,9 @@ import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton +private val REUSE_EXISTING_ID: String? = null +private val IGNORED_OPTIONS: Options? = null + @Singleton class DefaultVectorAnalytics @Inject constructor( private val context: Context, @@ -170,6 +175,10 @@ class DefaultVectorAnalytics @Inject constructor( ?.screen(screen.getName(), screen.getProperties()?.toPostHogProperties()) } + override fun updateUserProperties(identity: Identity) { + posthog?.identify(REUSE_EXISTING_ID, identity.getProperties().toPostHogProperties(), IGNORED_OPTIONS) + } + private fun Map?.toPostHogProperties(): Properties? { if (this == null) return null diff --git a/vector/src/main/java/im/vector/app/features/analytics/plan/PerformanceTimer.kt b/vector/src/main/java/im/vector/app/features/analytics/plan/PerformanceTimer.kt index 2cfc366cd3..59dd997f92 100644 --- a/vector/src/main/java/im/vector/app/features/analytics/plan/PerformanceTimer.kt +++ b/vector/src/main/java/im/vector/app/features/analytics/plan/PerformanceTimer.kt @@ -46,12 +46,14 @@ data class PerformanceTimer( enum class Name { /** - * The time spent parsing the response from an initial /sync request. + * The time spent parsing the response from an initial /sync request. In + * this case, `itemCount` should contain the number of joined rooms. */ InitialSyncParsing, /** - * The time spent waiting for a response to an initial /sync request. + * The time spent waiting for a response to an initial /sync request. In + * this case, `itemCount` should contain the number of joined rooms. */ InitialSyncRequest, @@ -62,13 +64,16 @@ data class PerformanceTimer( NotificationsOpenEvent, /** - * The duration of a regular /sync request when resuming the app. + * The duration of a regular /sync request when resuming the app. In + * this case, `itemCount` should contain the number of joined rooms in + * the response. */ StartupIncrementalSync, /** * The duration of an initial /sync request during startup (if the store - * has been wiped). + * has been wiped). In this case, `itemCount` should contain the number + * of joined rooms. */ StartupInitialSync, diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt index 22f1fc40a2..642d259723 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt @@ -16,6 +16,7 @@ package im.vector.app.features.call +import android.app.Activity import android.app.KeyguardManager import android.app.PictureInPictureParams import android.content.Context @@ -43,6 +44,7 @@ import com.google.android.material.card.MaterialCardView import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R +import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.utils.PERMISSIONS_FOR_AUDIO_IP_CALL @@ -518,13 +520,19 @@ class VectorCallActivity : VectorBaseActivity(), CallContro } is VectorCallViewEvents.ShowCallTransferScreen -> { val callId = withState(callViewModel) { it.callId } - navigator.openCallTransfer(this, callId) + navigator.openCallTransfer(this, callTransferActivityResultLauncher, callId) } null -> { } } } + private val callTransferActivityResultLauncher = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_CANCELED) { + callViewModel.handle(VectorCallViewActions.CallTransferSelectionCancelled) + } + } + private fun onErrorTimoutConnect(turn: TurnServerResponse?) { Timber.tag(loggerTag.value).d("onErrorTimoutConnect $turn") // TODO ask to use default stun, etc... diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallViewActions.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallViewActions.kt index 67aa7bede2..fb39660282 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallViewActions.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallViewActions.kt @@ -36,5 +36,6 @@ sealed class VectorCallViewActions : VectorViewModelAction { object ToggleCamera : VectorCallViewActions() object ToggleHDSD : VectorCallViewActions() object InitiateCallTransfer : VectorCallViewActions() + object CallTransferSelectionCancelled : VectorCallViewActions() object TransferCall : VectorCallViewActions() } diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt index 5af2b826af..4aca0ea499 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt @@ -319,10 +319,14 @@ class VectorCallViewModel @AssistedInject constructor( call?.sendDtmfDigit(action.digit) } VectorCallViewActions.InitiateCallTransfer -> { + call?.updateRemoteOnHold(true) _viewEvents.post( VectorCallViewEvents.ShowCallTransferScreen ) } + VectorCallViewActions.CallTransferSelectionCancelled -> { + call?.updateRemoteOnHold(false) + } VectorCallViewActions.TransferCall -> { handleCallTransfer() } diff --git a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferActivity.kt b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferActivity.kt index 959e96cc4c..0e63316bbe 100644 --- a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferActivity.kt @@ -16,6 +16,7 @@ package im.vector.app.features.call.transfer +import android.app.Activity import android.content.Context import android.content.Intent import android.os.Bundle @@ -55,7 +56,7 @@ class CallTransferActivity : VectorBaseActivity() { callTransferViewModel.observeViewEvents { when (it) { - is CallTransferViewEvents.Dismiss -> finish() + is CallTransferViewEvents.Complete -> handleComplete() CallTransferViewEvents.Loading -> showWaitingView() is CallTransferViewEvents.FailToTransfer -> showSnackbar(getString(R.string.call_transfer_failure)) } @@ -93,6 +94,11 @@ class CallTransferActivity : VectorBaseActivity() { } } + private fun handleComplete() { + setResult(Activity.RESULT_OK) + finish() + } + companion object { fun newIntent(context: Context, callId: String): Intent { diff --git a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewEvents.kt b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewEvents.kt index fd4c9d672d..a8451e4fb5 100644 --- a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewEvents.kt @@ -19,7 +19,7 @@ package im.vector.app.features.call.transfer import im.vector.app.core.platform.VectorViewEvents sealed class CallTransferViewEvents : VectorViewEvents { - object Dismiss : CallTransferViewEvents() + object Complete : CallTransferViewEvents() object Loading : CallTransferViewEvents() object FailToTransfer : CallTransferViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewModel.kt b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewModel.kt index ffc6ff9bc3..de6a5de539 100644 --- a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewModel.kt @@ -50,14 +50,14 @@ class CallTransferViewModel @AssistedInject constructor(@Assisted initialState: private val callListener = object : WebRtcCall.Listener { override fun onStateUpdate(call: MxCall) { if (call.state is CallState.Ended) { - _viewEvents.post(CallTransferViewEvents.Dismiss) + _viewEvents.post(CallTransferViewEvents.Complete) } } } init { if (call == null) { - _viewEvents.post(CallTransferViewEvents.Dismiss) + _viewEvents.post(CallTransferViewEvents.Complete) } else { call.addListener(callListener) } @@ -89,7 +89,7 @@ class CallTransferViewModel @AssistedInject constructor(@Assisted initialState: } else { call?.transferToUser(action.selectedUserId, null) } - _viewEvents.post(CallTransferViewEvents.Dismiss) + _viewEvents.post(CallTransferViewEvents.Complete) } catch (failure: Throwable) { _viewEvents.post(CallTransferViewEvents.FailToTransfer) } @@ -111,7 +111,7 @@ class CallTransferViewModel @AssistedInject constructor(@Assisted initialState: } else { call?.transferToUser(result.userId, result.roomId) } - _viewEvents.post(CallTransferViewEvents.Dismiss) + _viewEvents.post(CallTransferViewEvents.Complete) } catch (failure: Throwable) { _viewEvents.post(CallTransferViewEvents.FailToTransfer) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt index 58e36d2303..14c8e598f8 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt @@ -20,7 +20,6 @@ import android.net.Uri import android.view.View import im.vector.app.core.platform.VectorViewModelAction import im.vector.app.features.call.conference.ConferenceEvent -import im.vector.app.features.location.LocationData import org.matrix.android.sdk.api.session.content.ContentAttachmentData import org.matrix.android.sdk.api.session.room.model.message.MessageStickerContent import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachmentContent @@ -90,6 +89,7 @@ sealed class RoomDetailAction : VectorViewModelAction { data class EnsureNativeWidgetAllowed(val widget: Widget, val userJustAccepted: Boolean, val grantedEvents: RoomDetailViewEvents) : RoomDetailAction() + data class UpdateJoinJitsiCallStatus(val conferenceEvent: ConferenceEvent) : RoomDetailAction() data class OpenOrCreateDm(val userId: String) : RoomDetailAction() @@ -112,7 +112,4 @@ sealed class RoomDetailAction : VectorViewModelAction { // Poll data class EndPoll(val eventId: String) : RoomDetailAction() - - // Location - data class ShowLocation(val locationData: LocationData, val userId: String) : RoomDetailAction() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index 9926ecad24..b58a1d627e 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -171,8 +171,8 @@ import im.vector.app.features.html.EventHtmlRenderer import im.vector.app.features.html.PillImageSpan import im.vector.app.features.html.PillsPostProcessor import im.vector.app.features.invite.VectorInviteView -import im.vector.app.features.location.LocationData import im.vector.app.features.location.LocationSharingMode +import im.vector.app.features.location.toLocationData import im.vector.app.features.media.ImageContentRenderer import im.vector.app.features.media.VideoContentRenderer import im.vector.app.features.notifications.NotificationDrawerManager @@ -481,7 +481,6 @@ class RoomDetailFragment @Inject constructor( RoomDetailViewEvents.StopChatEffects -> handleStopChatEffects() is RoomDetailViewEvents.DisplayAndAcceptCall -> acceptIncomingCall(it) RoomDetailViewEvents.RoomReplacementStarted -> handleRoomReplacement() - is RoomDetailViewEvents.ShowLocation -> handleShowLocationPreview(it) }.exhaustive } @@ -613,14 +612,14 @@ class RoomDetailFragment @Inject constructor( } } - private fun handleShowLocationPreview(viewEvent: RoomDetailViewEvents.ShowLocation) { + private fun handleShowLocationPreview(locationContent: MessageLocationContent, senderId: String) { navigator .openLocationSharing( context = requireContext(), roomId = roomDetailArgs.roomId, mode = LocationSharingMode.PREVIEW, - initialLocationData = viewEvent.locationData, - locationOwnerId = viewEvent.userId + initialLocationData = locationContent.toLocationData(), + locationOwnerId = senderId ) } @@ -1828,6 +1827,12 @@ class RoomDetailFragment @Inject constructor( is EncryptedEventContent -> { roomDetailViewModel.handle(RoomDetailAction.TapOnFailedToDecrypt(informationData.eventId)) } + is MessageLocationContent -> { + handleShowLocationPreview(messageContent, informationData.senderId) + } + else -> { + Timber.d("No click action defined for this message content") + } } } @@ -1940,7 +1945,7 @@ class RoomDetailFragment @Inject constructor( when (action.messageContent) { is MessageTextContent -> shareText(requireContext(), action.messageContent.body) is MessageLocationContent -> { - LocationData.create(action.messageContent.getUri())?.let { + action.messageContent.toLocationData()?.let { openLocation(requireActivity(), it.latitude, it.longitude) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt index b0921e01f9..86240a5ffe 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt @@ -20,7 +20,6 @@ import android.net.Uri import android.view.View import im.vector.app.core.platform.VectorViewEvents import im.vector.app.features.call.webrtc.WebRtcCall -import im.vector.app.features.location.LocationData import org.matrix.android.sdk.api.session.widgets.model.Widget import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode @@ -83,6 +82,4 @@ sealed class RoomDetailViewEvents : VectorViewEvents { data class StartChatEffect(val type: ChatEffect) : RoomDetailViewEvents() object StopChatEffects : RoomDetailViewEvents() object RoomReplacementStarted : RoomDetailViewEvents() - - data class ShowLocation(val locationData: LocationData, val userId: String) : RoomDetailViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index 6e14b0fc76..9149ae1dca 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -53,7 +53,6 @@ import im.vector.app.features.home.room.detail.sticker.StickerPickerActionHandle import im.vector.app.features.home.room.detail.timeline.factory.TimelineFactory import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever import im.vector.app.features.home.room.typing.TypingHelper -import im.vector.app.features.location.LocationData import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import im.vector.app.features.session.coroutineScope import im.vector.app.features.settings.VectorDataStore @@ -385,14 +384,9 @@ class RoomDetailViewModel @AssistedInject constructor( _viewEvents.post(RoomDetailViewEvents.OpenRoom(action.replacementRoomId, closeCurrentRoom = true)) } is RoomDetailAction.EndPoll -> handleEndPoll(action.eventId) - is RoomDetailAction.ShowLocation -> handleShowLocation(action.locationData, action.userId) }.exhaustive } - private fun handleShowLocation(locationData: LocationData, userId: String) { - _viewEvents.post(RoomDetailViewEvents.ShowLocation(locationData, userId)) - } - private fun handleJitsiCallJoinStatus(action: RoomDetailAction.UpdateJoinJitsiCallStatus) = withState { state -> if (state.jitsiState.confId == null) { // If jitsi widget is removed while on the call diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt index 1ff9679479..086a093068 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt @@ -39,7 +39,9 @@ import im.vector.app.features.home.room.detail.timeline.item.E2EDecoration import im.vector.app.features.home.room.detail.timeline.tools.createLinkMovementMethod import im.vector.app.features.home.room.detail.timeline.tools.linkify import im.vector.app.features.html.SpanUtils -import im.vector.app.features.location.LocationData +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 im.vector.app.features.media.ImageContentRenderer import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence import org.matrix.android.sdk.api.extensions.orFalse @@ -62,6 +64,7 @@ class MessageActionsEpoxyController @Inject constructor( private val spanUtils: SpanUtils, private val eventDetailsFormatter: EventDetailsFormatter, private val dateFormatter: VectorDateFormatter, + private val urlMapProvider: UrlMapProvider, private val locationPinProvider: LocationPinProvider ) : TypedEpoxyController() { @@ -74,9 +77,11 @@ class MessageActionsEpoxyController @Inject constructor( val formattedDate = dateFormatter.format(date, DateFormatKind.MESSAGE_DETAIL) val body = state.messageBody.linkify(host.listener) val bindingOptions = spanUtils.getBindingOptions(body) - val locationData = state.timelineEvent()?.root?.getClearContent()?.toModel(catchError = true)?.let { - LocationData.create(it.getUri()) - } + val locationUrl = state.timelineEvent()?.root?.getClearContent() + ?.toModel(catchError = true) + ?.toLocationData() + ?.let { urlMapProvider.buildStaticMapUrl(it, INITIAL_MAP_ZOOM_IN_TIMELINE, 1200, 800) } + bottomSheetMessagePreviewItem { id("preview") avatarRenderer(host.avatarRenderer) @@ -89,7 +94,7 @@ class MessageActionsEpoxyController @Inject constructor( body(body.toEpoxyCharSequence()) bodyDetails(host.eventDetailsFormatter.format(state.timelineEvent()?.root)?.toEpoxyCharSequence()) time(formattedDate) - locationData(locationData) + locationUrl(locationUrl) locationPinProvider(host.locationPinProvider) } 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 8e791e11c8..cedcb66ef3 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 @@ -16,6 +16,7 @@ package im.vector.app.features.home.room.detail.timeline.factory +import android.content.res.Resources import android.text.Spannable import android.text.SpannableStringBuilder import android.text.Spanned @@ -33,7 +34,6 @@ import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.DimensionConverter import im.vector.app.core.utils.containsOnlyEmojis -import im.vector.app.features.home.room.detail.RoomDetailAction import im.vector.app.features.home.room.detail.timeline.TimelineEventController import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider import im.vector.app.features.home.room.detail.timeline.helper.ContentDownloadStateTrackerBinder @@ -68,7 +68,9 @@ import im.vector.app.features.html.EventHtmlRenderer import im.vector.app.features.html.PillsPostProcessor import im.vector.app.features.html.SpanUtils import im.vector.app.features.html.VectorHtmlCompressor -import im.vector.app.features.location.LocationData +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 im.vector.app.features.media.ImageContentRenderer import im.vector.app.features.media.VideoContentRenderer import im.vector.app.features.settings.VectorPreferences @@ -123,7 +125,10 @@ class MessageItemFactory @Inject constructor( private val session: Session, private val voiceMessagePlaybackTracker: VoiceMessagePlaybackTracker, private val locationPinProvider: LocationPinProvider, - private val vectorPreferences: VectorPreferences) { + private val vectorPreferences: VectorPreferences, + private val urlMapProvider: UrlMapProvider, + private val resources: Resources +) { // TODO inject this properly? private var roomId: String = "" @@ -178,7 +183,7 @@ class MessageItemFactory @Inject constructor( is MessagePollContent -> buildPollItem(messageContent, informationData, highlight, callback, attributes) is MessageLocationContent -> { if (vectorPreferences.labsRenderLocationsInTimeline()) { - buildLocationItem(messageContent, informationData, highlight, callback, attributes) + buildLocationItem(messageContent, informationData, highlight, attributes) } else { buildMessageTextItem(messageContent.body, false, informationData, highlight, callback, attributes) } @@ -193,27 +198,21 @@ class MessageItemFactory @Inject constructor( private fun buildLocationItem(locationContent: MessageLocationContent, informationData: MessageInformationData, highlight: Boolean, - callback: TimelineEventController.Callback?, attributes: AbsMessageItem.Attributes): MessageLocationItem? { - val geoUri = locationContent.getUri() - val locationData = LocationData.create(geoUri) + val width = resources.displayMetrics.widthPixels - dimensionConverter.dpToPx(60) + val height = dimensionConverter.dpToPx(200) - val mapCallback: MessageLocationItem.Callback = object : MessageLocationItem.Callback { - override fun onMapClicked() { - locationData?.let { - callback?.onTimelineItemAction(RoomDetailAction.ShowLocation(it, informationData.senderId)) - } - } + val locationUrl = locationContent.toLocationData()?.let { + urlMapProvider.buildStaticMapUrl(it, INITIAL_MAP_ZOOM_IN_TIMELINE, width, height) } return MessageLocationItem_() .attributes(attributes) - .locationData(locationData) + .locationUrl(locationUrl) .userId(informationData.senderId) .locationPinProvider(locationPinProvider) .highlighted(highlight) .leftGuideline(avatarSizeProvider.leftGuideline) - .callback(mapCallback) } private fun buildPollItem(pollContent: MessagePollContent, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/LocationPinProvider.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/LocationPinProvider.kt index fe3a7d9007..e92376c44d 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/LocationPinProvider.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/LocationPinProvider.kt @@ -28,6 +28,7 @@ import im.vector.app.core.glide.GlideApp import im.vector.app.core.utils.DimensionConverter import im.vector.app.features.home.AvatarRenderer import org.matrix.android.sdk.api.util.toMatrixItem +import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @@ -54,22 +55,36 @@ class LocationPinProvider @Inject constructor( val size = dimensionConverter.dpToPx(44) avatarRenderer.render(glideRequests, it, object : CustomTarget(size, size) { override fun onResourceReady(resource: Drawable, transition: Transition?) { - val bgUserPin = ContextCompat.getDrawable(context, R.drawable.bg_map_user_pin)!! - val layerDrawable = LayerDrawable(arrayOf(bgUserPin, resource)) - val horizontalInset = dimensionConverter.dpToPx(4) - val topInset = dimensionConverter.dpToPx(4) - val bottomInset = dimensionConverter.dpToPx(8) - layerDrawable.setLayerInset(1, horizontalInset, topInset, horizontalInset, bottomInset) - - cache[userId] = layerDrawable - - callback(layerDrawable) + Timber.d("## Location: onResourceReady") + val pinDrawable = createPinDrawable(resource) + cache[userId] = pinDrawable + callback(pinDrawable) } override fun onLoadCleared(placeholder: Drawable?) { // Is it possible? Put placeholder instead? + // FIXME The doc says it has to be implemented and should free resources + Timber.d("## Location: onLoadCleared") + } + + override fun onLoadFailed(errorDrawable: Drawable?) { + Timber.w("## Location: onLoadFailed") + errorDrawable ?: return + val pinDrawable = createPinDrawable(errorDrawable) + cache[userId] = pinDrawable + callback(pinDrawable) } }) } } + + private fun createPinDrawable(drawable: Drawable): Drawable { + val bgUserPin = ContextCompat.getDrawable(context, R.drawable.bg_map_user_pin)!! + val layerDrawable = LayerDrawable(arrayOf(bgUserPin, drawable)) + val horizontalInset = dimensionConverter.dpToPx(4) + val topInset = dimensionConverter.dpToPx(4) + val bottomInset = dimensionConverter.dpToPx(8) + layerDrawable.setLayerInset(1, horizontalInset, topInset, horizontalInset, bottomInset) + return layerDrawable + } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/BaseEventItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/BaseEventItem.kt index 3df6393c05..5c56ad4e76 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/BaseEventItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/BaseEventItem.kt @@ -41,9 +41,6 @@ abstract class BaseEventItem : VectorEpoxyModel @EpoxyAttribute open var leftGuideline: Int = 0 - @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) - lateinit var dimensionConverter: DimensionConverter - final override fun getViewType(): Int { // This makes sure we have a unique integer for the combination of layout and ViewStubId. return pairingFunction(layout, getViewStubId()).also { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt index 6ed4b1bce4..11faad8f13 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt @@ -16,73 +16,51 @@ package im.vector.app.features.home.room.detail.timeline.item -import android.widget.FrameLayout -import androidx.constraintlayout.widget.ConstraintLayout +import android.widget.ImageView 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 com.google.android.material.card.MaterialCardView +import com.bumptech.glide.request.RequestOptions import im.vector.app.R -import im.vector.app.core.epoxy.onClick +import im.vector.app.core.glide.GlideApp 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.shapeAppearanceModel -import im.vector.app.features.location.LocationData -import im.vector.app.features.location.MapTilerMapView @EpoxyModelClass(layout = R.layout.item_timeline_event_base) abstract class MessageLocationItem : AbsMessageItem() { - interface Callback { - fun onMapClicked() - } - @EpoxyAttribute - var callback: Callback? = null - - @EpoxyAttribute - var locationData: LocationData? = null + var locationUrl: String? = null @EpoxyAttribute var userId: String? = null - @EpoxyAttribute + @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var locationPinProvider: LocationPinProvider? = null override fun bind(holder: Holder) { super.bind(holder) - renderSendState(holder.mapViewContainer, null) - val location = locationData ?: return + renderSendState(holder.view, null) + val location = locationUrl ?: return val locationOwnerId = userId ?: return + GlideApp.with(holder.staticMapImageView) + .load(location) + .apply(RequestOptions.centerCropTransform()) + .into(holder.staticMapImageView) - holder.clickableMapArea.onClick { - callback?.onMapClicked() - } - - holder.mapView.apply { - initialize { - zoomToLocation(location.latitude, location.longitude, INITIAL_ZOOM) - - locationPinProvider?.create(locationOwnerId) { pinDrawable -> - addPinToMap(locationOwnerId, pinDrawable) - updatePinLocation(locationOwnerId, location.latitude, location.longitude) - } - } + locationPinProvider?.create(locationOwnerId) { pinDrawable -> + GlideApp.with(holder.staticMapPinImageView) + .load(pinDrawable) + .into(holder.staticMapPinImageView) } } override fun getViewStubId() = STUB_ID class Holder : AbsMessageItem.Holder(STUB_ID) { - val mapCardView by bind(R.id.mapCardView) - val mapViewContainer by bind(R.id.mapViewContainer) - val mapView by bind(R.id.mapView) - val clickableMapArea by bind(R.id.clickableMapArea) + val staticMapImageView by bind(R.id.staticMapImageView) + val staticMapPinImageView by bind(R.id.staticMapPinImageView) } companion object { private const val STUB_ID = R.id.messageContentLocationStub - private const val INITIAL_ZOOM = 15.0 } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageTextItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageTextItem.kt index dd3899a19d..ed9bc87834 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageTextItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageTextItem.kt @@ -110,6 +110,7 @@ abstract class MessageTextItem : AbsMessageItem() { val textFuture = PrecomputedTextCompat.getTextFuture(message, TextViewCompat.getTextMetricsParams(this), null) setTextFuture(textFuture) } else { + setTextFuture(null) text = message } } 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 630df16a37..29ca6b81a9 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 @@ -16,6 +16,10 @@ package im.vector.app.features.location -const val INITIAL_MAP_ZOOM = 15.0 -const val MIN_TIME_MILLIS_TO_UPDATE_LOCATION = 1 * 60 * 1000L // every 1 minute -const val MIN_DISTANCE_METERS_TO_UPDATE_LOCATION = 10f +const val MAP_BASE_URL = "https://api.maptiler.com/maps/streets/style.json" +const val STATIC_MAP_BASE_URL = "https://api.maptiler.com/maps/basic/static/" + +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_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 c3ff09ebcd..a69d8d20e3 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 @@ -17,41 +17,43 @@ package im.vector.app.features.location import android.os.Parcelable +import androidx.annotation.VisibleForTesting import kotlinx.parcelize.Parcelize +import org.matrix.android.sdk.api.session.room.model.message.MessageLocationContent @Parcelize data class LocationData( val latitude: Double, val longitude: Double, val uncertainty: Double? -) : Parcelable { +) : Parcelable - companion object { - - /** - * Creates location data from geo uri - * @param geoUri geo:latitude,longitude;uncertainty - * @return location data or null if geo uri is not valid - */ - fun create(geoUri: String): LocationData? { - val geoParts = geoUri - .split(":") - .takeIf { it.firstOrNull() == "geo" } - ?.getOrNull(1) - ?.split(",") - - val latitude = geoParts?.firstOrNull() - val geoTailParts = geoParts?.getOrNull(1)?.split(";") - val longitude = geoTailParts?.firstOrNull() - val uncertainty = geoTailParts?.getOrNull(1)?.replace("u=", "") - - return if (latitude != null && longitude != null) { - LocationData( - latitude = latitude.toDouble(), - longitude = longitude.toDouble(), - uncertainty = uncertainty?.toDouble() - ) - } else null - } - } +/** + * Creates location data from a LocationContent + * "geo:40.05,29.24;30" -> LocationData(40.05, 29.24, 30) + * @return location data or null if geo uri is not valid + */ +fun MessageLocationContent.toLocationData(): LocationData? { + return parseGeo(getBestGeoUri()) +} + +@VisibleForTesting +fun parseGeo(geo: String): LocationData? { + val geoParts = geo + .split(":") + .takeIf { it.firstOrNull() == "geo" } + ?.getOrNull(1) + ?.split(";") ?: return null + + val gpsParts = geoParts.getOrNull(0)?.split(",") ?: return null + val lat = gpsParts.getOrNull(0)?.toDoubleOrNull() ?: return null + val lng = gpsParts.getOrNull(1)?.toDoubleOrNull() ?: return null + + val uncertainty = geoParts.getOrNull(1)?.replace("u=", "")?.toDoubleOrNull() + + return LocationData( + latitude = lat, + longitude = lng, + uncertainty = uncertainty + ) } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationPreviewFragment.kt b/vector/src/main/java/im/vector/app/features/location/LocationPreviewFragment.kt index 6209bf5a4f..c4f2f148bf 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationPreviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationPreviewFragment.kt @@ -21,20 +21,30 @@ import android.view.LayoutInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup +import androidx.lifecycle.lifecycleScope import com.airbnb.mvrx.args +import com.mapbox.mapboxsdk.maps.MapView import im.vector.app.R import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.openLocation import im.vector.app.databinding.FragmentLocationPreviewBinding import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider +import java.lang.ref.WeakReference import javax.inject.Inject +/** + * TODO Move locationPinProvider to a ViewModel + */ class LocationPreviewFragment @Inject constructor( + private val urlMapProvider: UrlMapProvider, private val locationPinProvider: LocationPinProvider ) : VectorBaseFragment() { private val args: LocationSharingArgs by args() + // Keep a ref to handle properly the onDestroy callback + private var mapView: WeakReference? = null + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLocationPreviewBinding { return FragmentLocationPreviewBinding.inflate(layoutInflater, container, false) } @@ -42,11 +52,15 @@ class LocationPreviewFragment @Inject constructor( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - views.mapView.initialize { - if (isAdded) { - onMapReady() - } - } + mapView = WeakReference(views.mapView) + views.mapView.onCreate(savedInstanceState) + views.mapView.initialize(urlMapProvider.mapUrl) + loadPinDrawable() + } + + override fun onResume() { + super.onResume() + views.mapView.onResume() } override fun onPause() { @@ -54,11 +68,32 @@ class LocationPreviewFragment @Inject constructor( super.onPause() } + override fun onLowMemory() { + views.mapView.onLowMemory() + super.onLowMemory() + } + + override fun onStart() { + super.onStart() + views.mapView.onStart() + } + override fun onStop() { views.mapView.onStop() super.onStop() } + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + views.mapView.onSaveInstanceState(outState) + } + + override fun onDestroy() { + mapView?.get()?.onDestroy() + mapView?.clear() + super.onDestroy() + } + override fun getMenuRes() = R.menu.menu_location_preview override fun onOptionsItemSelected(item: MenuItem): Boolean { @@ -76,18 +111,20 @@ class LocationPreviewFragment @Inject constructor( openLocation(requireActivity(), location.latitude, location.longitude) } - private fun onMapReady() { - if (!isAdded) return - + private fun loadPinDrawable() { val location = args.initialLocationData ?: return val userId = args.locationOwnerId locationPinProvider.create(userId) { pinDrawable -> - views.mapView.apply { - zoomToLocation(location.latitude, location.longitude, INITIAL_MAP_ZOOM) - deleteAllPins() - addPinToMap(userId, pinDrawable) - updatePinLocation(userId, location.latitude, location.longitude) + lifecycleScope.launchWhenResumed { + views.mapView.render( + MapState( + zoomOnlyOnce = true, + pinLocationData = location, + pinId = args.locationOwnerId, + pinDrawable = pinDrawable + ) + ) } } } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingAction.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingAction.kt index 71101d0612..01319ef6c7 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingAction.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingAction.kt @@ -19,7 +19,5 @@ package im.vector.app.features.location import im.vector.app.core.platform.VectorViewModelAction sealed class LocationSharingAction : VectorViewModelAction { - data class OnLocationUpdate(val locationData: LocationData) : LocationSharingAction() object OnShareLocation : LocationSharingAction() - object OnLocationProviderIsNotAvailable : LocationSharingAction() } 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 900f465f04..f6bad2826b 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 @@ -20,29 +20,29 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.view.isGone import com.airbnb.mvrx.fragmentViewModel +import com.airbnb.mvrx.withState import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.mapbox.mapboxsdk.maps.MapView import im.vector.app.R import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentLocationSharingBinding -import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider -import org.matrix.android.sdk.api.session.Session +import java.lang.ref.WeakReference import javax.inject.Inject +/** + * We should consider using SupportMapFragment for a out of the box lifecycle handling + */ class LocationSharingFragment @Inject constructor( - private val locationTracker: LocationTracker, - private val session: Session, - private val locationPinProvider: LocationPinProvider -) : VectorBaseFragment(), LocationTracker.Callback { - - init { - locationTracker.callback = this - } + private val urlMapProvider: UrlMapProvider +) : VectorBaseFragment() { private val viewModel: LocationSharingViewModel by fragmentViewModel() - private var lastZoomValue: Double = -1.0 + // Keep a ref to handle properly the onDestroy callback + private var mapView: WeakReference? = null override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLocationSharingBinding { return FragmentLocationSharingBinding.inflate(inflater, container, false) @@ -51,11 +51,9 @@ class LocationSharingFragment @Inject constructor( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - views.mapView.initialize { - if (isAdded) { - onMapReady() - } - } + mapView = WeakReference(views.mapView) + views.mapView.onCreate(savedInstanceState) + views.mapView.initialize(urlMapProvider.mapUrl) views.shareLocationContainer.debouncedClicks { viewModel.handle(LocationSharingAction.OnShareLocation) @@ -63,54 +61,48 @@ class LocationSharingFragment @Inject constructor( viewModel.observeViewEvents { when (it) { - LocationSharingViewEvents.LocationNotAvailableError -> handleLocationNotAvailableError() - LocationSharingViewEvents.Close -> activity?.finish() + LocationSharingViewEvents.LocationNotAvailableError -> handleLocationNotAvailableError() + LocationSharingViewEvents.Close -> activity?.finish() }.exhaustive } } + override fun onResume() { + super.onResume() + views.mapView.onResume() + } + override fun onPause() { views.mapView.onPause() super.onPause() } + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + views.mapView.onSaveInstanceState(outState) + } + + override fun onStart() { + super.onStart() + views.mapView.onStart() + } + override fun onStop() { views.mapView.onStop() super.onStop() } + override fun onLowMemory() { + super.onLowMemory() + views.mapView.onLowMemory() + } + override fun onDestroy() { - locationTracker.stop() + mapView?.get()?.onDestroy() + mapView?.clear() super.onDestroy() } - private fun onMapReady() { - if (!isAdded) return - - locationPinProvider.create(session.myUserId) { - views.mapView.addPinToMap( - pinId = USER_PIN_NAME, - image = it, - ) - // All set, start location tracker - locationTracker.start() - } - } - - override fun onLocationUpdate(locationData: LocationData) { - lastZoomValue = if (lastZoomValue == -1.0) INITIAL_MAP_ZOOM else views.mapView.getCurrentZoom() ?: INITIAL_MAP_ZOOM - - views.mapView.zoomToLocation(locationData.latitude, locationData.longitude, lastZoomValue) - views.mapView.deleteAllPins() - views.mapView.updatePinLocation(USER_PIN_NAME, locationData.latitude, locationData.longitude) - - viewModel.handle(LocationSharingAction.OnLocationUpdate(locationData)) - } - - override fun onLocationProviderIsNotAvailable() { - viewModel.handle(LocationSharingAction.OnLocationProviderIsNotAvailable) - } - private fun handleLocationNotAvailableError() { MaterialAlertDialogBuilder(requireActivity()) .setTitle(R.string.location_not_available_dialog_title) @@ -118,9 +110,15 @@ class LocationSharingFragment @Inject constructor( .setPositiveButton(R.string.ok) { _, _ -> activity?.finish() } + .setCancelable(false) .show() } + override fun invalidate() = withState(viewModel) { state -> + views.mapView.render(state.toMapState()) + views.shareLocationGpsLoading.isGone = state.lastKnownLocation != null + } + companion object { const val USER_PIN_NAME = "USER_PIN_NAME" } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt index b3c97310e1..f4e1fd0281 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt @@ -24,12 +24,15 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel +import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider import org.matrix.android.sdk.api.session.Session class LocationSharingViewModel @AssistedInject constructor( @Assisted private val initialState: LocationSharingViewState, - session: Session -) : VectorViewModel(initialState) { + private val locationTracker: LocationTracker, + private val locationPinProvider: LocationPinProvider, + private val session: Session +) : VectorViewModel(initialState), LocationTracker.Callback { private val room = session.getRoom(initialState.roomId)!! @@ -38,14 +41,31 @@ class LocationSharingViewModel @AssistedInject constructor( override fun create(initialState: LocationSharingViewState): LocationSharingViewModel } - companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() { + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() + + init { + locationTracker.start(this) + createPin() + } + + private fun createPin() { + locationPinProvider.create(session.myUserId) { + setState { + copy( + pinDrawable = it + ) + } + } + } + + override fun onCleared() { + super.onCleared() + locationTracker.stop() } override fun handle(action: LocationSharingAction) { when (action) { - is LocationSharingAction.OnLocationUpdate -> handleLocationUpdate(action.locationData) - LocationSharingAction.OnShareLocation -> handleShareLocation() - LocationSharingAction.OnLocationProviderIsNotAvailable -> handleLocationProviderIsNotAvailable() + LocationSharingAction.OnShareLocation -> handleShareLocation() }.exhaustive } @@ -62,13 +82,13 @@ class LocationSharingViewModel @AssistedInject constructor( } } - private fun handleLocationUpdate(locationData: LocationData) { + override fun onLocationUpdate(locationData: LocationData) { setState { copy(lastKnownLocation = locationData) } } - private fun handleLocationProviderIsNotAvailable() { + override fun onLocationProviderIsNotAvailable() { _viewEvents.post(LocationSharingViewEvents.LocationNotAvailableError) } } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewState.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewState.kt index 2869929b12..f3b937855a 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewState.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewState.kt @@ -16,6 +16,7 @@ package im.vector.app.features.location +import android.graphics.drawable.Drawable import androidx.annotation.StringRes import com.airbnb.mvrx.MavericksState import im.vector.app.R @@ -28,7 +29,8 @@ enum class LocationSharingMode(@StringRes val titleRes: Int) { data class LocationSharingViewState( val roomId: String, val mode: LocationSharingMode, - val lastKnownLocation: LocationData? = null + val lastKnownLocation: LocationData? = null, + val pinDrawable: Drawable? = null ) : MavericksState { constructor(locationSharingArgs: LocationSharingArgs) : this( @@ -36,3 +38,10 @@ data class LocationSharingViewState( mode = locationSharingArgs.mode ) } + +fun LocationSharingViewState.toMapState() = MapState( + zoomOnlyOnce = true, + pinLocationData = lastKnownLocation, + pinId = LocationSharingFragment.USER_PIN_NAME, + pinDrawable = pinDrawable +) 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 0c0315cf34..162fbc5959 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 @@ -19,70 +19,108 @@ package im.vector.app.features.location import android.Manifest import android.content.Context import android.location.Location -import android.location.LocationListener import android.location.LocationManager import androidx.annotation.RequiresPermission import androidx.core.content.getSystemService +import androidx.core.location.LocationListenerCompat +import im.vector.app.BuildConfig import timber.log.Timber import javax.inject.Inject class LocationTracker @Inject constructor( - private val context: Context -) : LocationListener { + context: Context +) : LocationListenerCompat { + + private val locationManager = context.getSystemService() interface Callback { fun onLocationUpdate(locationData: LocationData) fun onLocationProviderIsNotAvailable() } - private var locationManager: LocationManager? = null - var callback: Callback? = null + private var callback: Callback? = null + + private var hasGpsProviderLiveLocation = false @RequiresPermission(anyOf = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION]) - fun start() { - val locationManager = context.getSystemService() + fun start(callback: Callback?) { + Timber.d("## LocationTracker. start()") + hasGpsProviderLiveLocation = false + this.callback = callback - locationManager?.let { - val isGpsEnabled = it.isProviderEnabled(LocationManager.GPS_PROVIDER) - val isNetworkEnabled = it.isProviderEnabled(LocationManager.NETWORK_PROVIDER) - - val provider = when { - isGpsEnabled -> LocationManager.GPS_PROVIDER - isNetworkEnabled -> LocationManager.NETWORK_PROVIDER - else -> { - callback?.onLocationProviderIsNotAvailable() - Timber.v("## LocationTracker. There is no location provider available") - return - } - } - - // Send last known location without waiting location updates - it.getLastKnownLocation(provider)?.let { lastKnownLocation -> - callback?.onLocationUpdate(lastKnownLocation.toLocationData()) - } - - it.requestLocationUpdates( - provider, - MIN_TIME_MILLIS_TO_UPDATE_LOCATION, - MIN_DISTANCE_METERS_TO_UPDATE_LOCATION, - this - ) - } ?: run { + if (locationManager == null) { callback?.onLocationProviderIsNotAvailable() Timber.v("## LocationTracker. LocationManager is not available") + return } + + locationManager.allProviders + .takeIf { it.isNotEmpty() } + // Take GPS first + ?.sortedByDescending { if (it == LocationManager.GPS_PROVIDER) 1 else 0 } + ?.forEach { provider -> + Timber.d("## LocationTracker. track location using $provider") + + // Send last known location without waiting location updates + locationManager.getLastKnownLocation(provider)?.let { lastKnownLocation -> + if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) { + Timber.d("## LocationTracker. lastKnownLocation: $lastKnownLocation") + } else { + Timber.d("## LocationTracker. lastKnownLocation: ${lastKnownLocation.provider}") + } + notifyLocation(lastKnownLocation, isLive = false) + } + + locationManager.requestLocationUpdates( + provider, + MIN_TIME_TO_UPDATE_LOCATION_MILLIS, + MIN_DISTANCE_TO_UPDATE_LOCATION_METERS, + this + ) + } + ?: run { + callback?.onLocationProviderIsNotAvailable() + Timber.v("## LocationTracker. There is no location provider available") + } } @RequiresPermission(anyOf = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION]) fun stop() { + Timber.d("## LocationTracker. stop()") locationManager?.removeUpdates(this) callback = null } override fun onLocationChanged(location: Location) { + if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) { + Timber.d("## LocationTracker. onLocationChanged: $location") + } else { + Timber.d("## LocationTracker. onLocationChanged: ${location.provider}") + } + notifyLocation(location, isLive = true) + } + + private fun notifyLocation(location: Location, isLive: Boolean) { + when (location.provider) { + LocationManager.GPS_PROVIDER -> { + hasGpsProviderLiveLocation = isLive + } + else -> { + if (hasGpsProviderLiveLocation) { + // Ignore this update + Timber.d("## LocationTracker. ignoring location from ${location.provider}, we have gps live location") + return + } + } + } callback?.onLocationUpdate(location.toLocationData()) } + override fun onProviderDisabled(provider: String) { + Timber.d("## LocationTracker. onProviderDisabled: $provider") + callback?.onLocationProviderIsNotAvailable() + } + private fun Location.toLocationData(): LocationData { return LocationData(latitude, longitude, accuracy.toDouble()) } diff --git a/vector/src/main/java/im/vector/app/features/location/VectorMapView.kt b/vector/src/main/java/im/vector/app/features/location/MapState.kt similarity index 61% rename from vector/src/main/java/im/vector/app/features/location/VectorMapView.kt rename to vector/src/main/java/im/vector/app/features/location/MapState.kt index 23b59bf99a..d001457e4f 100644 --- a/vector/src/main/java/im/vector/app/features/location/VectorMapView.kt +++ b/vector/src/main/java/im/vector/app/features/location/MapState.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 New Vector Ltd + * 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. @@ -18,15 +18,9 @@ package im.vector.app.features.location import android.graphics.drawable.Drawable -interface VectorMapView { - fun initialize(onMapReady: () -> Unit) - - fun addPinToMap(pinId: String, image: Drawable) - fun updatePinLocation(pinId: String, latitude: Double, longitude: Double) - fun deleteAllPins() - - fun zoomToLocation(latitude: Double, longitude: Double, zoom: Double) - fun getCurrentZoom(): Double? - - fun onClick(callback: () -> Unit) -} +data class MapState( + val zoomOnlyOnce: Boolean, + val pinLocationData: LocationData? = null, + val pinId: String, + val pinDrawable: Drawable? = null +) diff --git a/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt b/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt index c64af1ebaa..dd80f701f6 100644 --- a/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt +++ b/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt @@ -17,7 +17,6 @@ package im.vector.app.features.location import android.content.Context -import android.graphics.drawable.Drawable import android.util.AttributeSet import com.mapbox.mapboxsdk.camera.CameraPosition import com.mapbox.mapboxsdk.geometry.LatLng @@ -27,65 +26,76 @@ import com.mapbox.mapboxsdk.maps.Style import com.mapbox.mapboxsdk.plugins.annotation.SymbolManager import com.mapbox.mapboxsdk.plugins.annotation.SymbolOptions import com.mapbox.mapboxsdk.style.layers.Property -import im.vector.app.BuildConfig +import timber.log.Timber class MapTilerMapView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 -) : MapView(context, attrs, defStyleAttr), VectorMapView { +) : MapView(context, attrs, defStyleAttr) { - private var map: MapboxMap? = null - private var symbolManager: SymbolManager? = null - private var style: Style? = null + private var pendingState: MapState? = null - override fun initialize(onMapReady: () -> Unit) { + data class MapRefs( + val map: MapboxMap, + val symbolManager: SymbolManager, + val style: Style + ) + + private var mapRefs: MapRefs? = null + private var initZoomDone = false + + /** + * For location fragments + */ + fun initialize(url: String) { + Timber.d("## Location: initialize") getMapAsync { map -> - map.setStyle(styleUrl) { style -> - this.symbolManager = SymbolManager(this, map, style) - this.map = map - this.style = style - onMapReady() + map.setStyle(url) { style -> + mapRefs = MapRefs( + map, + SymbolManager(this, map, style), + style + ) + pendingState?.let { render(it) } + pendingState = null } } } - override fun addPinToMap(pinId: String, image: Drawable) { - style?.addImage(pinId, image) - } + fun render(state: MapState) { + val safeMapRefs = mapRefs ?: return Unit.also { + pendingState = state + } - override fun updatePinLocation(pinId: String, latitude: Double, longitude: Double) { - symbolManager?.create( - SymbolOptions() - .withLatLng(LatLng(latitude, longitude)) - .withIconImage(pinId) - .withIconAnchor(Property.ICON_ANCHOR_BOTTOM) - ) - } + state.pinDrawable?.let { pinDrawable -> + if (!safeMapRefs.style.isFullyLoaded || + safeMapRefs.style.getImage(state.pinId) == null) { + safeMapRefs.style.addImage(state.pinId, pinDrawable) + } + } - override fun deleteAllPins() { - symbolManager?.deleteAll() - } + state.pinLocationData?.let { locationData -> + if (!initZoomDone || !state.zoomOnlyOnce) { + zoomToLocation(locationData.latitude, locationData.longitude) + initZoomDone = true + } - override fun zoomToLocation(latitude: Double, longitude: Double, zoom: Double) { - map?.cameraPosition = CameraPosition.Builder() - .target(LatLng(latitude, longitude)) - .zoom(zoom) - .build() - } - - override fun getCurrentZoom(): Double? { - return map?.cameraPosition?.zoom - } - - override fun onClick(callback: () -> Unit) { - map?.addOnMapClickListener { - callback() - true + safeMapRefs.symbolManager.deleteAll() + safeMapRefs.symbolManager.create( + SymbolOptions() + .withLatLng(LatLng(locationData.latitude, locationData.longitude)) + .withIconImage(state.pinId) + .withIconAnchor(Property.ICON_ANCHOR_BOTTOM) + ) } } - companion object { - private const val styleUrl = "https://api.maptiler.com/maps/streets/style.json?key=${BuildConfig.mapTilerKey}" + private fun zoomToLocation(latitude: Double, longitude: Double) { + Timber.d("## Location: zoomToLocation") + mapRefs?.map?.cameraPosition = CameraPosition.Builder() + .target(LatLng(latitude, longitude)) + .zoom(INITIAL_MAP_ZOOM_IN_PREVIEW) + .build() } } diff --git a/vector/src/main/java/im/vector/app/features/location/UrlMapProvider.kt b/vector/src/main/java/im/vector/app/features/location/UrlMapProvider.kt new file mode 100644 index 0000000000..76d44f5ece --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/location/UrlMapProvider.kt @@ -0,0 +1,58 @@ +/* + * 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 + +import android.content.res.Resources +import im.vector.app.BuildConfig +import im.vector.app.R +import javax.inject.Inject + +class UrlMapProvider @Inject constructor( + private val resources: Resources +) { + private val keyParam = "?key=${BuildConfig.mapTilerKey}" + + // This is static so no need for a fun + val mapUrl = buildString { + append(MAP_BASE_URL) + append(keyParam) + } + + fun buildStaticMapUrl(locationData: LocationData, + zoom: Double, + width: Int, + height: Int): String { + return buildString { + append(STATIC_MAP_BASE_URL) + append(locationData.longitude) + append(",") + append(locationData.latitude) + append(",") + append(zoom) + append("/") + append(width) + append("x") + append(height) + append(".png") + append(keyParam) + if (!resources.getBoolean(R.bool.is_rtl)) { + // On LTR languages we want the legal mentions to be displayed on the bottom left of the image + append("&attribution=bottomleft") + } + } + } +} 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 f66ced3299..8f0b1723a0 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 @@ -524,9 +524,13 @@ class DefaultNavigator @Inject constructor( context.startActivity(RoomDevToolActivity.intent(context, roomId)) } - override fun openCallTransfer(context: Context, callId: String) { + override fun openCallTransfer( + context: Context, + activityResultLauncher: ActivityResultLauncher, + callId: String + ) { val intent = CallTransferActivity.newIntent(context, callId) - context.startActivity(intent) + activityResultLauncher.launch(intent) } override fun openCreatePoll(context: Context, roomId: String, editedEventId: String?, mode: PollMode) { diff --git a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt index 775272bd33..d6ca83a8a3 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt @@ -149,7 +149,11 @@ interface Navigator { fun openDevTools(context: Context, roomId: String) - fun openCallTransfer(context: Context, callId: String) + fun openCallTransfer( + context: Context, + activityResultLauncher: ActivityResultLauncher, + callId: String + ) fun openCreatePoll(context: Context, roomId: String, editedEventId: String?, mode: PollMode) diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventProcessor.kt b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventProcessor.kt index 3d10d74fe3..351f085b7e 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventProcessor.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventProcessor.kt @@ -20,6 +20,7 @@ import im.vector.app.features.invite.AutoAcceptInvites import im.vector.app.features.notifications.ProcessedEvent.Type.KEEP import im.vector.app.features.notifications.ProcessedEvent.Type.REMOVE import org.matrix.android.sdk.api.session.events.model.EventType +import timber.log.Timber import javax.inject.Inject private typealias ProcessedEvents = List> @@ -33,9 +34,13 @@ class NotifiableEventProcessor @Inject constructor( val processedEvents = queuedEvents.map { val type = when (it) { is InviteNotifiableEvent -> if (autoAcceptInvites.hideInvites) REMOVE else KEEP - is NotifiableMessageEvent -> if (shouldIgnoreMessageEventInRoom(currentRoomId, it.roomId) || outdatedDetector.isMessageOutdated(it)) { - REMOVE - } else KEEP + is NotifiableMessageEvent -> when { + shouldIgnoreMessageEventInRoom(currentRoomId, it.roomId) -> REMOVE + .also { Timber.d("notification message removed due to currently viewing the same room") } + outdatedDetector.isMessageOutdated(it) -> REMOVE + .also { Timber.d("notification message removed due to being read") } + else -> KEEP + } is SimpleNotifiableEvent -> when (it.type) { EventType.REDACTION -> REMOVE else -> KEEP diff --git a/vector/src/main/java/im/vector/app/features/notifications/PushRuleTriggerListener.kt b/vector/src/main/java/im/vector/app/features/notifications/PushRuleTriggerListener.kt index ff817520db..cd08820fc1 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/PushRuleTriggerListener.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/PushRuleTriggerListener.kt @@ -55,12 +55,12 @@ class PushRuleTriggerListener @Inject constructor( private suspend fun createNotifiableEvents(pushEvents: PushEvents, session: Session): List { return pushEvents.matchedEvents.mapNotNull { (event, pushRule) -> - Timber.v("Push rule match for event ${event.eventId}") + Timber.d("Push rule match for event ${event.eventId}") val action = pushRule.getActions().toNotificationAction() if (action.shouldNotify) { resolver.resolveEvent(event, session, isNoisy = !action.soundName.isNullOrBlank()) } else { - Timber.v("Matched push rule is set to not notify") + Timber.d("Matched push rule is set to not notify") null } } diff --git a/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt b/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt index e720b7307c..952612b43f 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt @@ -16,9 +16,13 @@ package im.vector.app.features.onboarding -enum class FtueUseCase { - FRIENDS_FAMILY, - TEAMS, - COMMUNITIES, - SKIP +enum class FtueUseCase(val persistableValue: String) { + FRIENDS_FAMILY("friends_family"), + TEAMS("teams"), + COMMUNITIES("communities"), + SKIP("skip"); + + companion object { + fun from(persistedValue: String) = values().first { it.persistableValue == persistedValue } + } } diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt index 43f37f4601..8097e90206 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt @@ -32,10 +32,14 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.extensions.configureAndStart import im.vector.app.core.extensions.exhaustive +import im.vector.app.core.extensions.vectorStore import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.ensureTrailingSlash import im.vector.app.features.VectorFeatures +import im.vector.app.features.analytics.AnalyticsTracker +import im.vector.app.features.analytics.extensions.toTrackingValue +import im.vector.app.features.analytics.plan.Identity import im.vector.app.features.login.HomeServerConnectionConfigFactory import im.vector.app.features.login.LoginConfig import im.vector.app.features.login.LoginMode @@ -73,7 +77,8 @@ class OnboardingViewModel @AssistedInject constructor( private val reAuthHelper: ReAuthHelper, private val stringProvider: StringProvider, private val homeServerHistoryService: HomeServerHistoryService, - private val vectorFeatures: VectorFeatures + private val vectorFeatures: VectorFeatures, + private val analyticsTracker: AnalyticsTracker ) : VectorViewModel(initialState) { @AssistedFactory @@ -125,7 +130,7 @@ class OnboardingViewModel @AssistedInject constructor( when (action) { is OnboardingAction.OnGetStarted -> handleSplashAction(action.resetLoginConfig, action.onboardingFlow) is OnboardingAction.OnIAlreadyHaveAnAccount -> handleSplashAction(action.resetLoginConfig, action.onboardingFlow) - is OnboardingAction.UpdateUseCase -> handleUpdateUseCase() + is OnboardingAction.UpdateUseCase -> handleUpdateUseCase(action) OnboardingAction.ResetUseCase -> resetUseCase() is OnboardingAction.UpdateServerType -> handleUpdateServerType(action) is OnboardingAction.UpdateSignMode -> handleUpdateSignMode(action) @@ -458,13 +463,15 @@ class OnboardingViewModel @AssistedInject constructor( } } - private fun handleUpdateUseCase() { - // TODO act on the use case selection + private fun handleUpdateUseCase(action: OnboardingAction.UpdateUseCase) { + setState { copy(useCase = action.useCase) } + analyticsTracker.updateUserProperties(Identity(ftueUseCaseSelection = action.useCase.toTrackingValue())) _viewEvents.post(OnboardingViewEvents.OpenServerSelection) } private fun resetUseCase() { - // TODO remove stored use case + setState { copy(useCase = null) } + analyticsTracker.updateUserProperties(Identity(ftueUseCaseSelection = null)) } private fun handleUpdateServerType(action: OnboardingAction.UpdateServerType) { @@ -745,6 +752,9 @@ class OnboardingViewModel @AssistedInject constructor( } private suspend fun onSessionCreated(session: Session) { + awaitState().useCase?.let { useCase -> + session.vectorStore(applicationContext).setUseCase(useCase) + } activeSessionHolder.setActiveSession(session) authenticationService.reset() diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt index fd25f3901e..d05a8294f6 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt @@ -40,6 +40,8 @@ data class OnboardingViewState( @PersistState val serverType: ServerType = ServerType.Unknown, @PersistState + val useCase: FtueUseCase? = null, + @PersistState val signMode: SignMode = SignMode.Unknown, @PersistState val resetPasswordEmail: String? = null, diff --git a/vector/src/main/java/im/vector/app/features/session/VectorSessionStore.kt b/vector/src/main/java/im/vector/app/features/session/VectorSessionStore.kt new file mode 100644 index 0000000000..ce85eeeb98 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/session/VectorSessionStore.kt @@ -0,0 +1,60 @@ +/* + * 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.session + +import android.content.Context +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.stringPreferencesKey +import androidx.datastore.preferences.preferencesDataStore +import im.vector.app.features.onboarding.FtueUseCase +import kotlinx.coroutines.flow.first +import org.matrix.android.sdk.internal.util.md5 + +/** + * Local storage for: + * - messaging use case (Enum/String) + */ +class VectorSessionStore constructor( + private val context: Context, + myUserId: String +) { + + private val Context.dataStore: DataStore by preferencesDataStore(name = "vector_session_store_${myUserId.md5()}") + private val useCaseKey = stringPreferencesKey("use_case") + + suspend fun readUseCase() = context.dataStore.data.first().let { preferences -> + preferences[useCaseKey]?.let { FtueUseCase.from(it) } + } + + suspend fun setUseCase(useCase: FtueUseCase) { + context.dataStore.edit { settings -> + settings[useCaseKey] = useCase.persistableValue + } + } + + suspend fun resetUseCase() { + context.dataStore.edit { settings -> + settings.remove(useCaseKey) + } + } + + suspend fun clear() { + context.dataStore.edit { settings -> settings.clear() } + } +} diff --git a/vector/src/main/res/layout/fragment_location_sharing.xml b/vector/src/main/res/layout/fragment_location_sharing.xml index f9a37a6241..ad418f3e1c 100644 --- a/vector/src/main/res/layout/fragment_location_sharing.xml +++ b/vector/src/main/res/layout/fragment_location_sharing.xml @@ -48,4 +48,13 @@ android:textColor="?colorPrimary" android:textStyle="bold" /> + + \ No newline at end of file diff --git a/vector/src/main/res/layout/item_bottom_sheet_message_preview.xml b/vector/src/main/res/layout/item_bottom_sheet_message_preview.xml index 08a8f5b27f..b41fda99fb 100644 --- a/vector/src/main/res/layout/item_bottom_sheet_message_preview.xml +++ b/vector/src/main/res/layout/item_bottom_sheet_message_preview.xml @@ -106,18 +106,34 @@ tools:text="1080 x 1024 - 43s - 12kB" tools:visibility="visible" /> - + tools:alpha="0.3" + tools:visibility="visible"> + + + + + diff --git a/vector/src/main/res/layout/item_timeline_event_location_stub.xml b/vector/src/main/res/layout/item_timeline_event_location_stub.xml index 134e9e005d..316470b5f1 100644 --- a/vector/src/main/res/layout/item_timeline_event_location_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_location_stub.xml @@ -1,36 +1,24 @@ + android:layout_height="wrap_content" + app:cardCornerRadius="8dp"> - + android:layout_height="200dp" + android:contentDescription="@string/a11y_static_map_image" /> - - - - - + diff --git a/vector/src/main/res/values-cs/strings.xml b/vector/src/main/res/values-cs/strings.xml index c54fb52153..dbbb6d5238 100644 --- a/vector/src/main/res/values-cs/strings.xml +++ b/vector/src/main/res/values-cs/strings.xml @@ -611,7 +611,7 @@ Zmínit Zobrazit seznam relací Toto je náhled místnosti. Interakce s místností byla vypnuta. - Vykázání uživatele jej vykopne z této místnosti a zamezí, aby opět vstoupili. + Vykázání uživatele jej odebere z této místnosti a zamezí, aby opět vstoupil. Důvod Opravdu chcete pozvat uživatele %s do této konverzace\? "%1$s, " @@ -930,7 +930,7 @@ Ukázat potvrzení o přečtení Klepněte na potvrzení o přečtení pro podrobnosti. Ukázat události příchodů a odchodů - Nemá vliv na pozvánky, nakopnutí a zákazy. + Pozvánky, odebrání a vykázání nejsou dotčeny. Ukázat události účtu Zahrnout avatar a změny veřejného jména. Vibrovat při zmínce uživatele @@ -2386,7 +2386,7 @@ %d vteřin Zobrazit stavové události účastníků v místnosti - Zahrnuje události pozvat/vstoupit/opustit/vykopnout/vykázat a změny avatara/veřejného jména. + Zahrnuje události pozvat/vstoupit/opustit/odebrat/vykázat a změny avatara/veřejného jména. Hlasování Reagoval(a): %s Výsledek ověření @@ -3010,7 +3010,7 @@ Zobrazit a aktualizovat role potřebné ke změně různých částí prostoru. Oprávnění prostoru Zrušením vykázání uživateli umožní znovu se připojit do prostoru. - Vykázáním uživatele z tohoto prostoru vykopnete a zabráníte mu v dalším připojení. + Vykázání uživatele z tohoto prostoru ho odebere a zamezí, aby opět vstoupil. vykopnutí uživatele je z tohoto prostoru odstraní. \n \nAbyste jim zabránili v dalším vstupu, měli byste je raději vykázat. @@ -3072,8 +3072,8 @@ Konečný výsledek na základě %1$d hlasů - Nikdo nehlasoval - %1$d hlasů. Hlasujte pro zobrazení výsledků + %1$d hlas. Hlasujte pro zobrazení výsledků + %1$d hlasy. Hlasujte pro zobrazení výsledků %1$d hlasů. Hlasujte pro zobrazení výsledků @@ -3120,7 +3120,40 @@ Přepsat barvu přezdívky Již mám účet Zlepšete týmovou komunikaci. - Spojte se s kýmkoli. + Bezpečené zasílání zpráv. Máte vše pod kontrolou. Vlastněte své konverzace. + Sdílet polohu + Zobrazit polohy uživatele na časové ose + Po zapnutí budete moci odeslat svou polohu do libovolné místnosti + Povolit sdílení polohy + 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 polohu + Poloha + Sdílet polohu + Výsledky se zobrazí až po ukončení hlasování + Uzavřené hlasování + Hlasující vidí výsledky ihned po hlasování + Otevřené hlasování + Typ hlasování + UPRAVIT HLASOVÁNÍ + Upravit hlasování + Nikdo nehlasoval + Chybně nakonfigurovaná úroveň důvěryhodnosti + Šifrování je špatně nakonfigurováno + Obnovit šifrování + Šifrování bylo špatně nakonfigurováno, takže nelze odesílat zprávy. Kontaktujte správce, aby obnovil šifrování do funkčního stavu. + Kontaktujte správce, aby obnovil šifrování do funkčního stavu. + Šifrování bylo špatně nakonfigurováno. + Sdíleli svou polohu + Vytvořit účet + Zasílání zpráv pro váš tým. + Koncové šifrování bez potřeby telefonního čísla. Žádné reklamy ani vytěžování dat. + Můžete si vybrat, kde budou vaše konverzace uloženy, a získat tak kontrolu a nezávislost. Připojeno přes Matrix. + Bezpečná a nezávislá komunikace, která vám poskytne stejnou úroveň soukromí jako osobní rozhovor u vás doma. + Poloha + Šifrování bylo špatně nakonfigurováno, takže nelze odesílat zprávy. Kliknutím otevřete nastavení. \ No newline at end of file diff --git a/vector/src/main/res/values-de/strings.xml b/vector/src/main/res/values-de/strings.xml index b4616a338c..e8a961dba5 100644 --- a/vector/src/main/res/values-de/strings.xml +++ b/vector/src/main/res/values-de/strings.xml @@ -3058,4 +3058,37 @@ Endgültiges Ergebnis basiert auf %1$d Stimmen Element verbindet Datenschutz mit tollen Features. + Verschlüsselung wiederherstellen + Standort freigeben + Standort freigeben + Standort freigeben + Standort freigeben + Standortfreigabe aktivieren + Öffnen mit + ${app_name} konnte nicht auf deinen Standort zugreifen. Bitte versuche es später noch einmal. + ${app_name} konnte nicht auf deinen Standort zugreifen + Standort + Ergebnisse werden erst angezeigt, wenn du die Umfrage beendest + Geschlossene Umfrage + Ergebnisse werden direkt nach Stimmabgabe angezeigt + Offene Umfrage + Umfragetyp + UMFRAGE BEARBEITEN + Umfrage bearbeiten + Keine Stimmen abgegeben + Konto erstellen + Nachrichtenaustausch für dein Team. + Ende-zu-Ende-verschlüsselt und ohne Telefonnummer nutzbar. Keine Werbung oder Datenerfassung. + Wähle wo deine Gespräche liegen, für Kontrolle und Unabhängigkeit. Verbunden mit Matrix. + Sichere und unabhängige Kommunikation, die für die gleiche Vertraulichkeit sorgt, wie ein Gespräch von Angesicht zu Angesicht in deinem eigenen Zuhause. + Standort + Fehlerhaft konfiguriertes Vertrauenslevel + Die Verschlüsselung ist fehlerhaft konfiguriert + Bitte kontaktiere einen Admin, um die Verschlüsselung zurückzusetzen. + Die Verschlüsselung wurde fehlerhaft konfiguriert. + Die Verschlüsselung wurde fehlerhaft konfiguriert und du kannst keine Nachrichten senden. Klicke, um die Einstellungen zu öffnen. + Die Verschlüsselung wurde fehlerhaft konfiguriert und du kannst keine Nachrichten senden. Bitte kontaktiere einen Admin, um die Verschlüsselung wiederherzustellen. + Geteilte Standorte anzeigen + Sobald aktiviert, kannst du deinen Standort in jeden Raum senden + Hat den Standort geteilt \ No newline at end of file diff --git a/vector/src/main/res/values-et/strings.xml b/vector/src/main/res/values-et/strings.xml index 2799218362..77f854f73b 100644 --- a/vector/src/main/res/values-et/strings.xml +++ b/vector/src/main/res/values-et/strings.xml @@ -1381,7 +1381,7 @@ Näita kõikide sõnumite ajatempleid Lugemisteatisele klõpsimine kuvab detailsema loendi. Näita jututubadega liitumised ja neist lahkumised - Siia alla ei kuulu kutsed, müksamised ja suhtluskeelud. + Siia alla ei kuulu kutsed, eemaldamised ja suhtluskeelud. Näita kontoga seotud sündmusi Sealhulgas tunnuspildi ja kuvatava nime muutusi. Kasutaja mainimisel anna väringa märku @@ -2342,7 +2342,7 @@ %d sekundit Näita jututoa liikmete olekusündmusi - Sealhulgas kutsumisi, liitumisi, lahkumisi, müksamisi, keelamisi ning tunnuspildi ja kuvatava nime muutusi. + Sealhulgas kutsumisi, liitumisi, lahkumisi, eemaldamisi, keelamisi ning tunnuspildi ja kuvatava nime muutusi. Küsitlus Reageeris: %s Verifitseerimise tulemus @@ -3061,7 +3061,40 @@ Asenda hüüdnime värvid Mul on kasutajakonto juba olemas Lase tiimidel vabalt tegutseda. - Suhtle kellega soovid. + Turvaline sõnumivahetus. Sul on kontroll oma andmete üle. Vestlused, mida sa tegelikult ka omad. + Jaga asukohta + Kuva ajajoonel kasutaja asukohti + Kui see seadistus on kasutusel, siis sa saad oma asukohta jagada igas jututoas + Luba asukohta jagada + Ava muu rakendusega + ${app_name} ei saanud asukohta tuvastada. Palun proovi hiljem uuesti. + ${app_name} ei saanud asukohta tuvastada + Jaga asukohta + Jaga asukohta + Asukoht + Jaga asukohta + Tulemusi kuvame vaid siis, kui küsitlus on lõppenud + Küsitlus on lõppenud + Osalejad näevad tulemusi peale oma valiku salvestamist + Ava küsitlus + Küsitluse tüüp + MUUDA KÜSITLUST + Muuda küsitlust + Hääletanuid ei ole + Seadmete usaldusseosed on valesti seadistatud + Krüptimise seadistustes on viga + Taasta krüptimine + Kui soovid krüptimist töökorda saada, siis võta ühendust serveri haldajaga. + Krüptimise seadistustes on viga. + Jagas oma asukohta + Loo kasutajakonto + Sõnumisuhtlus sinu tiimi või kogukonna jaoks. + Tagatud on andmete läbiv krüptimine ning oma telefoninumbrit ei pea sa jagama. Pole reklaame ega sinu andmete kogumist. + Sa ise valid serveri, kus sinu vestlusi hoitakse ning sellega tagadki kontrolli oma andmete üle. Lahendus põhineb Matrix\'i võrgul. + Turvaline ja sõltumatu suhtluslahendus, mis tagab sama privaatsuse, kui omavaheline vestlus sinu kodus. + Asukoht + Krüptimise seadistustes on viga ja sa ei saa sõnumeid saata. Seadistuste avamiseks klõpsi siin. + Krüptimise seadistustes on viga ja sa ei saa sõnumeid saata. Kui soovid krüptimist töökorda saada, siis võta ühendust serveri haldajaga. \ No newline at end of file diff --git a/vector/src/main/res/values-fa/strings.xml b/vector/src/main/res/values-fa/strings.xml index 84a6a6d5ca..a3782df603 100644 --- a/vector/src/main/res/values-fa/strings.xml +++ b/vector/src/main/res/values-fa/strings.xml @@ -815,7 +815,7 @@ لغو نادیده‌گیری اشاره نمایش فهرست نشست - انسداد کاربر، او را از این اتاق اخراج کرده و از پیوستن دوباره‌اش جلوگیری می‌کند. + انسداد کاربر، او را از این اتاق برداشته و از پیوستن دوباره‌اش جلوگیری می‌کند. دلیل تنظیمات پیام‌ها @@ -1002,7 +1002,7 @@ \nمدیرهای یکپارچگی، داده‌های پیکربندی را دریافت کرده و می‌توانند از طرف شما ابزارک‌ها را تغییر داده، دعوت‌های اتاق فرستاده و سطوح قدرت را تنظیم کنند. نمایش برچسب زمانی برای تمامی پیام‌ها نمایش برچسب‌های زمانی در قالب ۱۲ساعته - شامل رویدادهای دعوت/پیوستن/ترک/اخراج/تحریم و تغییرهای چهرک/نام نمایشی. + شامل رویدادهای دعوت/پیوستن/ترک/برداشتن/تحریم و تغییرهای چهرک/نام نمایشی. پشتیبان امن مدیریت برپایی پشتیبان امن @@ -2956,7 +2956,7 @@ دیدن و به‌روز رسانی نقش‌های لازم برای تغییر بخش‌های مختلف فضا. اجازه‌های فضا لغو تحریم کاربر، اجازهٔ پیوستن دوباره‌اش به فضا را می‌دهد. - تحریم کاربر، او را از این فضا اخراج کرده و از پیوستن دوباره‌اش جلوگیری می‌کند. + تحریم کاربر، او را از این فضا برداشته و از پیوستن دوباره‌اش جلوگیری می‌کند. اخراج کاربر، از این فضا برش می‌دارد. \n \nبرای پیش‌گیری از پیوستن دوباره، باید تحریمش کنید. @@ -3059,7 +3059,25 @@ سامانه‌تان هنگام‌ مواجهه با خطای ناتوانی در رمزگشایی، گزارش‌ها را به صورت خودکار خواهد فرستاد گزارش خودکار خطاهای رمزگشایی. از پیش حساب دارم - ارتباط با هرکسی. + پیام‌رسانی امن. شما کنترل می‌کنید. صاحب گفت‌وگوهایتان باشید. + هم‌رسانی مکان + به کار انداختن هم‌رسانی مکان + گشودن با + هم‌رسانی مکان + هم‌رسانی مکان + مکان + هم‌رسانی مکان + نظرسنجی بسته + گشودن نظرسنجی + گونهٔ نظرسنجی + ویرایش نظرسنجی + ویرایش نظرسنجی + رأیی داده نشده + بازیابی رمزنگاری + مکانش را هم‌رسانی کرد + ایجاد حساب + پیام‌رسانی برای گروهتان. + مکان \ 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 5a7b47622f..08dcb09190 100644 --- a/vector/src/main/res/values-fr/strings.xml +++ b/vector/src/main/res/values-fr/strings.xml @@ -3017,7 +3017,7 @@ Résultat final sur la base de %1$d votes - Aucun vote exprimé + %1$d vote exprimé. Votez pour voir les résultats %1$d votes exprimés. Votez pour voir les résultats @@ -3040,8 +3040,8 @@ La politique de votre serveur d’accueil Politique de ${app_name} Vous pouvez désactiver ceci à tout moment dans les paramètres - Nous ne partageons pas d’information avec des tierces parties - Nous n’enregistrons ou ne profilons aucune donnée du compte + Nous ne partageons aucune information avec des tiers + Nous n’enregistrons ou ne profilons aucune donnée du compte ici Aidez nous à identifier les problèmes et améliorer Element en envoyant des rapports d’usage anonymes. Pour comprendre de quelle manière les gens utilisent Element sur plusieurs appareils, nous créeront un identifiant aléatoire commun à tous vos appareils. \n @@ -3061,8 +3061,41 @@ Outrepasser la couleur du pseudo J’ai déjà un compte Être vicTeams, ce n’est pas Slack vous voulez. - Se connecter avec qui vous voulez. + Messagerie sécurisée. Vous êtes aux commandes. Contrôlez vos conversations. Vous n’êtes pas autorisé à rejoindre ce salon + Choisissez où vos conversations sont stockées, vous avez le contrôle et êtes indépendant. Connecté via Matrix. + Le chiffrement a été mal configuré ce qui vous empêche d’envoyer des messages. Veuillez contacter un administrateur pour remettre le chiffrement en état de marche. + Partager la localisation + Afficher les localisations de l\'utilisateur dans le temps + Une fois activé il vous sera possible d\'envoyer votre localisation dans n\'importe quel salon + Activer le partage de localisation + 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 localisation + Localisation + Partager la localisation + Les résultats ne sont dévoilés que lorsque vous terminez le sondage + Sondage fermé + Les votants voient les résultats dès qu\'ils ont votés + Ouvrir le sondage + Type de sondage + ÉDITER LE SONDAGE + Éditer le sondage + Aucun vote exprimé + Niveau de confiance mal configuré + Le chiffrement est mal configuré + Restaurer le Chiffrement + Veuillez contacter un administrateur pour reconfigurer le chiffrement. + Le chiffrement a été mal configuré. + On partagé leur localisation + Créer un compte + Messagerie pour votre équipe. + Chiffré de bout en bout et aucun numéro de téléphone n\'est nécessaire. Pas de pubs ni de collecte de données. + Communication indépendante et sécurisée qui vous donne le même niveau d\'intimité qu\'une discussion face-à-face dans votre maison. + Localisation + Le chiffrement a été mal configuré ce qui vous empêche d\'envoyer des messages. Cliquez pour ouvrir les paramètres. \ 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 ccdbb41c35..0a6b6d9a97 100644 --- a/vector/src/main/res/values-hu/strings.xml +++ b/vector/src/main/res/values-hu/strings.xml @@ -777,7 +777,7 @@ Figyelmeztetés: ez a fájl törlésre kerülhet, ha az alkalmazást törli.Biztos, hogy hanghívást akarsz indítani\? Biztos, hogy videóhívást akarsz indítani\? Csoportok listája - A felhasználó kitiltása kirúgja őt a szobából, és megakadályozza, hogy újra csatlakozhasson. + A felhasználó kitiltása eltávolítja őt a szobából, és megakadályozza, hogy újra csatlakozhasson. Összes üzenet (hangos) Összes üzenet Csak megemlítések @@ -1009,7 +1009,7 @@ Matrixban az üzenetek láthatósága hasonlít az e-mailre. Az üzenet törlés Olvasás visszajelzés megjelenítése További információér kattints az olvasás visszaigazolásokra. Be-, és kilépési események megjelenítése - Meghívások, kirúgások és kitiltások nem változtak. + Meghívások, eltávolítások és kitiltások nem változtak. Fiók események megjelenítése Avatar és név változásokat tartalmaz. Jelszó @@ -2751,7 +2751,7 @@ Ha nem te állítottad be a visszaállítási metódust, akkor egy támadó pró A üzenetek olvashatóságának változtatása csak az új üzenetekre lesz érvényes. A régi üzenetek láthatósága nem fog változni. Az üzenet szerkesztőhöz emodzsi billentyűzet gomb hozzáadása Emodzsi billentyűzet megjelenítése - Magába foglalja a meghívás/belépés/kilépés/kirúgás/kitiltás eseményt és profilkép/megjelenítési név változtatást. + Magába foglalja a meghívás/belépés/kilépés/eltávolítás/kitiltás eseményt és profilkép/megjelenítési név változtatást. Használd a /confetti parancsot vagy küldj üzenetet ami ❄️-t vagy 🎉-t tartalmaz Beszélgetés effektek megjelenítése Szoba tagok státusz eseményeinek megjelenítése @@ -3015,7 +3015,7 @@ Ha nem te állítottad be a visszaállítási metódust, akkor egy támadó pró Eredmény %1$d szavazat alapján - Nem szavazott senki + %1$d leadott szavazat. Szavazz az eredmények megtekintéséhez %1$d leadott szavazat. Szavazz az eredmények megtekintéséhez @@ -3061,6 +3061,39 @@ Ha nem te állítottad be a visszaállítási metódust, akkor egy támadó pró Becenév színének megváltoztatása Már van fiókom Rázd fel a csoportjaidat. - Lépj kapcsolatba bárkivel. + Biztonságos üzenetküldés. Te irányítasz. + Tartózkodási hely megosztása + A felhasználó földrajzi helyzetének megjelenítése az idővonalon + A beállítás után bármelyik szobában megoszthatod a földrajzi helyzetedet + Földrajzi hely megosztás engedélyezése + Megnyitás ezzel + ${app_name} nem fér hozzá a helyadatodhoz. Próbáld újra később. + ${app_name} nem fér hozzá a földrajzi helyzetedhez + Tartózkodási hely megosztása + Tartózkodási hely megosztása + Földrajzi helyzet + Tartózkodási hely megosztása + Az eredmény csak a szavazás végeztével válik láthatóvá + Lezárt szavazás + A szavazók a szavazásuk után látják a szavazatokat + Szavazás megnyitása + Szavazás típusa + SZAVAZÁS SZERKESZTÉSE + Szavazás szerkesztése + Nem adtak le szavazatot + Megbízhatósági szint beállítási hiba + A titkosítási beállítás hibás + Titkosítás helyreállítása + Kérjük vedd fel a kapcsolatot az adminisztrátorral a titkosítás helyreállítása érdekében. + A titkosítás beállítása hibás. + A földrajzi helyzetüket megosztották + Fiók létrehozása + Üzenetküldés a csoportodnak. + Telefonszám nélkül végpontok között titkosított. Reklámok és adatbányászat nélkül. + Válaszd meg hol legyenek a beszélgetéseid tárolva, visszaadja az irányítást és függetlenné tesz. Csatlakozva a Matrixhoz. + Biztonságos és független kommunikáció ami olyan biztonságos mintha valakivel négyszemközt beszélgetnél a házadban. + Földrajzi helyzet + A titkosítás beállítása hibás így nem lehet üzenetet küldeni. Kattints a beállításokért. + A titkosítás beállítása hibás így nem lehet üzenetet küldeni. Kérjük vedd fel a kapcsolatot az adminisztrátorral a titkosítás helyreállításához. \ No newline at end of file diff --git a/vector/src/main/res/values-in/strings.xml b/vector/src/main/res/values-in/strings.xml index 21dfefa374..62145bf64e 100644 --- a/vector/src/main/res/values-in/strings.xml +++ b/vector/src/main/res/values-in/strings.xml @@ -3006,7 +3006,40 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan. Ubah warna nama tampilan Saya sudah punya akun Bebaskan. - Hubung dengan siapa pun. + Perpesanan yang aman. Anda dalam kendali. Miliki percakapan Anda. + Bagikan lokasi + Tampilkan lokasi pengguna di linimasa + Setelah diaktifkan Anda akan dapat mengirim lokasi Anda ke ruangan apa saja + Aktifkan pembagian lokasi + Buka dengan + ${app_name} tidak dapat mengakses lokasi Anda. Silakan coba lagi nanti. + ${app_name} tidak dapat mengakses lokasi Anda + Bagikan lokasi + Bagikan lokasi + Lokasi + Bagikan lokasi + Hasil akan ditampilkan ketika Anda mengakhiri poll-nya + Poll tertutup + Pemberi suara akan melihat hasilnya ketika telah memberikan suara + Buka poll + Tipe poll + EDIT POLL + Edit poll + Tidak ada suara + Tingkat kepercayaan dikonfigurasi dengan salah + Enkripsi dikonfigurasi dengan salah + Pulihkan Enkripsi + Mohon hubungi sebuah admin untuk memulihkan enkripsi ke status yang valid. + Enkripsi telah dikonfigurasi dengan salah. + Membagikan lokasinya + Buat akun + Perpesanan untuk tim Anda. + Terenkripsi secara ujung-ke-ujung dan tidak memerlukan nomor telepon. Tidak ada iklan atau penambangan data. + Anda pilih di mana percakapan Anda disimpan, memberikan Anda kendali dan kebebasan. Terhubung via Matrix. + Komunikasi aman dan independen yang memberikan tingkat privasi yang sama seperti percakapan wajah-ke-wajah di dalam rumah Anda sendiri. + Lokasi + Enkripsi telah dikonfigurasi dengan salah sehingga Anda tidak dapat mengirim pesan. Klik untuk membuka pengaturan. + Enkripsi telah dikonfigurasi dengan salah sehingga Anda tidak dapat mengirim pesan. Mohon hubungi sebuah admin untuk memulihkan enkripsi ke status yang valid. \ 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 d581a97f0b..221a996e0c 100644 --- a/vector/src/main/res/values-it/strings.xml +++ b/vector/src/main/res/values-it/strings.xml @@ -846,7 +846,7 @@ %d utente %d utenti - Bandire l\'utente lo espellerà dalla stanza e gli impedirà di rientrare. + Bandire l\'utente lo rimuoverà da questa stanza e gli impedirà di rientrare. %d nuovo messaggio %d nuovi messaggi @@ -1081,7 +1081,7 @@ Mostra le conferme di lettura Clicca sulle conferme di lettura per vederne l\'elenco dettagliato. Mostra entrata ed uscita degli utenti - Inviti, butta fuori e ban non vengono considerati. + Inviti, rimozioni e ban non vengono considerati. Mostra i cambiamenti degli account Includi cambiamenti dell\'avatar e del nome visualizzato. Password @@ -2333,7 +2333,7 @@ %d secondi Mostra eventi di stato dei membri della stanza - Includi eventi di invito/entrata/uscita/kick/ban e modifiche di avatar/nome. + Includi eventi di invito/entrata/uscita/rimozione/ban e modifiche di avatar/nome. Sondaggio Reagito con: %s Verifica conclusa @@ -2946,7 +2946,7 @@ Seleziona i ruoli necessari per cambiare varie parti dello spazio Vedi e aggiorna i ruoli necessari per cambiare varie parti dello spazio. Riammettere l\'utente gli permetterà di rientrare nello spazio. - Bandire l\'utente lo espellerà da questo spazio e gli impedirà di rientrare. + Bandire l\'utente lo rimuoverà da questo spazio e gli impedirà di rientrare. buttando fuori l\'utente verrà rimosso da questo spazio. \n \nPer impedire che possa rientrare, dovresti invece bandirlo. @@ -3006,7 +3006,7 @@ Risultato finale basato su %1$d voti - Nessun voto + %1$d voto. Vota per vedere i risultati %1$d voti. Vota per vedere i risultati @@ -3051,7 +3051,40 @@ Sovrascrivi colore nick Ho già un account Riduci il carico ai team. - Connettiti con chiunque. + Messaggistica sicura. Tu hai il controllo. Prendi il controllo delle tue conversazioni. + Condividi posizione + Mostra le posizioni dell\'utente nella linea temporale + Una volta attivata, potrai inviare la tua posizione in qualsiasi stanza + Attiva condivisione posizione + 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 posizione + Posizione + Condividi posizione + I risultati verranno rivelati solo quando termini il sondaggio + Sondaggio chiuso + I votanti vedono i risultati appena avranno votato + Apri sondaggio + Tipo sondaggio + MODIFICA SONDAGGIO + Modifica sondaggio + Nessun voto + Livello di fiducia configurato male + La crittografia è configurata male + Reimposta crittografia + Contatta un amministratore per reimpostare la crittografia ad uno stato valido. + La crittografia è stata configurata male. + Ha condiviso la sua posizione + Crea account + Messaggistica per la tua squadra. + Crittografia end-to-end e nessun numero di telefono richiesto. Niente pubblicità o raccolta di dati. + Scegli dove vengono tenute le tue conversazioni, dandoti controllo e indipendenza. Connesso via Matrix. + Comunicazioni sicure e indipendenti che ti danno lo stesso livello di riservatezza di una conversazione faccia a faccia in casa tua. + Posizione + La crittografia è stata configurata male, perciò non puoi inviare messaggi. Clicca per aprire le impostazioni. + La crittografia è stata configurata male, perciò non puoi inviare messaggi. Contatta l\'amministratore per reimpostare la crittografia ad uno stato valido. \ No newline at end of file diff --git a/vector/src/main/res/values-pt-rBR/strings.xml b/vector/src/main/res/values-pt-rBR/strings.xml index 513709c0fd..228b61cb6f 100644 --- a/vector/src/main/res/values-pt-rBR/strings.xml +++ b/vector/src/main/res/values-pt-rBR/strings.xml @@ -819,7 +819,7 @@ Tirar vídeo Lista de Grupos Chamar - Banir usuária(o) vai expulsá-la(o) desta sala e preveni-la(o) de se juntar de novo. + Banir usuária(o) vai removê-la(o) desta sala e preveni-la(o) de se juntar de novo. Todas as mensagens (barulhento) Todas as mensagens Menções somente @@ -1118,7 +1118,7 @@ Mostrar recibos de leitura Clique nos recibos de leitura para uma lista detalhada. Mostrar eventos de juntar-se e sair - Convites, expulsões e bans são desafetados. + Convites, remoções e bans são desafetados. Mostrar eventos de conta Inclui mudanças de avatar e nome de exibição. Conexão no Background @@ -2334,7 +2334,7 @@ %d segundo %d segundos - Inclui eventos de convite/juntar-se/saiu/expulsão/ban e mudanças de avatar/nome de exibição. + Inclui eventos de convidar/juntar-se/saiu/remover/ban e mudanças de avatar/nome de exibição. Emails e números de telefone Gerenciar emails e números de telefone linkados a sua conta Matrix Código @@ -2956,7 +2956,7 @@ Veja e atualize os papéis requeridos para mudar várias partes do espaço. Permissões de espaço Desbanir usuária(o) vai permiti-la(o) se juntar ao espaço de novo. - Banir usuária(o) vai expulsá-la(o) deste espaço e preveni-la de se juntar de novo. + Banir usuária(o) vai removê-la(o) deste espaço e preveni-la de se juntar de novo. expulsar usuária(o) vai removê-la(o) deste espaço. \n \nPara preveni-la(o) de se juntar de novo, você devia bani-la(o) em vez disso. @@ -3061,7 +3061,40 @@ Sobrepor cor de nick Eu já tenho uma conta Dê liberdade a times. - Conecte-se com qualquer pessoa. + Mensageria segura. Você está em controle. Tenha posse de suas conversas. + Compartilhar localização + Render localizações de usuária(o) na timeline + Uma vez habilitada você vai ser capaz de enviar sua localização a qualquer sala + Habilitar compartilhamento de localização + 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ção + Localização + Compartilhar localização + Resultados são somente revelados quando você termina a sondagem + Sondagem fechada + Votantes veem resultados assim que elas(es) têm votado + Sondagem aberta + Tipo de sondagem + EDITAR SONDAGEM + Editar sondagem + Nenhum voto lançado + Nível de confiança malconfigurado + Encriptação está malconfigurada + Restaurar Encriptação + Por favor contacte um(a) admin para restaurar encriptação a um estado válido. + Compartilhou a localização dela(e) + Localização + Encriptação tem sido malconfigurada. + Criar conta + Mensageria para seu time. + Encriptado ponta-a-ponta e nenhum número de telefone requerido. Sem publicidade ou datamining. + Escolha onde suas conversas são mantidas, dando-lhe controle e independência. Conectado via Matrix. + Comunicação segura e independente que lhe dá o mesmo nível de privacidade que conversa face-a-face em sua própria casa. + Encriptação tem sido malconfigurada então você não pode enviar mensagens. Clique para abrir configurações. + Encriptação tem sido malconfigurada então você não pode enviar mensagens. Por favor contacte um(a) admin para restaurar encriptação a um estado válido. \ 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 e4557d5f22..5377dfe99f 100644 --- a/vector/src/main/res/values-sk/strings.xml +++ b/vector/src/main/res/values-sk/strings.xml @@ -795,7 +795,7 @@ Ste si istý, že chcete začať hlasový hovor? Ste si istí, že chcete začať video hovor? Zoznam komunít - Zakázanie používateľa ho vylúči z tejto miestnosti a zabráni mu v ďalšom vstupe. + Zakázanie používateľa ho odstráni z tejto miestnosti a zabráni mu v ďalšom vstupe. Všetky správy (hlasné) Všetky správy Len zmienky @@ -2176,4 +2176,106 @@ • Servery s číslicami ako IP adresy sú povolené. • Servery, ktorých názov sa zhoduje s %s sú povolené. • Servery, ktorých názov sa zhoduje s %s sú zakázané. + Nastaviť rolu + Fráza pre obnovenie + Editor správ + Nedá sa skenovať + Iné relácie + Aktuálna relácia + Vyčistiť údaje + Vyčistiť údaje + Knižnice tretích strán + Pomoc a podpora + Pozvať pomocou používateľského mena alebo e-mailu + Povoliť matematiku LaTeX + Reštartujte aplikáciu, aby sa zmena prejavila. + Vy máte všetko pod kontrolou. + Bezpečné zasielanie správ. + Automatické hlásenie chýb dešifrovania. + Váš systém bude automaticky odosielať záznamy, keď sa vyskytne chyba dešifrovania + Otvoriť kameru + Posielajte obrázky a videá + Odoslať nálepku + Otvoriť kontakty + Vytvoriť anketu + Bezpečná a nezávislá komunikácia, ktorá vám poskytuje rovnakú úroveň súkromia ako rozhovor zoči-voči vo vašom vlastnom dome. + Vyberte si, kde budú vaše rozhovory uložené, a získajte tak kontrolu a nezávislosť. Pripojené cez Matrix. + End-to-end šifrovanie a bez potreby telefónneho čísla. Žiadne reklamy ani zneužívanie údajov. + Zasielanie správ pre váš tím. + Vytvoriť účet + Šifrovanie je nesprávne nastavené. + Obráťte sa na správcu, aby obnovil platný stav šifrovania. + Obnoviť šifrovanie + Šifrovanie je nesprávne nastavené + Žiadne odovzdané hlasy + Upraviť anketu + Výsledky sa zobrazia až po ukončení ankety + Poloha + Zdieľali svoju polohu + Zdieľať polohu + Poloha + 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 + Povoliť zdieľanie polohy + Po zapnutí tejto funkcie budete môcť odoslať svoju polohu do ľubovoľnej miestnosti + Vykresliť umiestnenie používateľa na časovej osi + Zdieľať polohu + Prémiový hosting pre organizácie + Prihlásiť sa do %1$s + Pripojiť sa k vlastnému serveru + Pripojiť sa k službám Element Matrix + Pripojiť sa k %1$s + Prémiový hosting pre organizácie + Pripojte sa k miliónom ľudí zadarmo na najväčšom verejnom serveri + Rovnako ako e-mail, aj účty majú jeden domov, hoci môžete hovoriť s kýmkoľvek + Začnite + Rozšírte a prispôsobte si možnosti + Udržujte konverzácie súkromné pomocou šifrovania + Komunikujte s ľuďmi priamo alebo v skupinách + Je to vaša konverzácia. Vlastnite ju. + Vytvorili ste miestnosť len pre pozvaných. + %1$s vytvoril miestnosť len na pozvanie. + Miestnosť ste zverejnili pre každého, kto pozná odkaz. + %1$s zverejnil miestnosť pre každého, kto pozná odkaz. + Dlhým kliknutím na miestnosť zobrazíte ďalšie možnosti + Nevykonali ste žiadne zmeny + %1$s nevykonal žiadne zmeny + Tento obsah bol nahlásený ako spam. +\n +\nAk nechcete vidieť ďalší obsah od tohto používateľa, môžete ho ignorovať a skryť jeho správy. + Nahlásené ako spam + Tento obsah bol nahlásený. +\n +\nAk nechcete vidieť ďalší obsah od tohto používateľa, môžete ho ignorovať a skryť jeho správy. + NAHLÁSIŤ + Dôvod nahlásenia tohto obsahu + V tejto miestnosti nie sú žiadne súbory + MÉDIÁ + V tejto miestnosti nie sú žiadne médiá + Pri načítavaní prílohy došlo k chybe. + Súbor \'%1$s\' (%2$s) je príliš veľký na odoslanie. Limit je %3$s. + Prejsť na spodok + Vytvoriť novú miestnosť + Vytvoriť novú priamu konverzáciu + Po prijatí podmienok domovského servera to skúste znova. + Zobraziť a aktualizovať role potrebné na zmenu rôznych častí priestoru. + Zobraziť a aktualizovať role potrebné na zmenu rôznych častí miestnosti. + Šifrovanie bolo nesprávne nastavené, takže nemôžete odosielať správy. Kliknutím otvorte nastavenia. + Šifrovanie bolo nesprávne nastavené, takže nemôžete odosielať správy. Obráťte sa na správcu, aby obnovil platný stav šifrovania. + Zrušenie zákazu používateľovi mu umožní znovu sa pripojiť do priestoru. + Zakázanie používateľa ho odstráni z tohto priestoru a zabráni mu v ďalšom vstupe. + Používateľ bude z tohto priestoru odstránený. +\n +\nAby ste mu zabránili v opätovnom pripojení, mali by ste ho zakázať. + Táto miestnosť nie je verejná. Bez pozvánky sa do nej nebudete môcť opätovne pripojiť. + Pokračovať aj tak + Povoľte prístup ku kontaktom. + Ak chcete skenovať QR kód, musíte povoliť prístup k fotoaparátu. + Volaný používateľ je obsadený. + Používateľ je obsadený + %s podržal hovor + Hlasový hovor s %s \ No newline at end of file diff --git a/vector/src/main/res/values-sq/strings.xml b/vector/src/main/res/values-sq/strings.xml index a31673925d..d93331777f 100644 --- a/vector/src/main/res/values-sq/strings.xml +++ b/vector/src/main/res/values-sq/strings.xml @@ -500,7 +500,7 @@ ID Përdoruesi, Emër ose email-i Përmendje Shfaq Listë Sesionesh - Dëbimi i përdoruesit do ta përzërë atë nga kjo dhomë dhe do t’i pengojë atij rihyrjen. + Dëbimi i përdoruesit do ta heqë atë nga kjo dhomë dhe do t’i pengojë atij rihyrjen. Arsye Jeni i sigurt se doni të ftohet %s te kjo fjalosje? "%1$s, " @@ -1048,7 +1048,7 @@ Parashihini lidhjet brenda fjalosjes, kur shërbyesi juaj home e mbulon këtë veçori. Lejojuni përdoruesve të tjerë të dinë se po shtypni. Formatojini mesazhet duke përdorur sintaksën Markdown përpara se të dërgohen. Kjo lejon formatim të thelluar, f.v., përdorimi i yllthit për ta shfaqur tekstin me të pjerrëta. - Nuk prek ftesat, përzëniet dhe dëbimet. + Nuk prek ftesat, heqjet dhe dëbimet. ${app_name}-i mund të xhirojë në prapaskenë që të administrojë njoftimet tuaja në rrugë të sigurt dhe privatisht. Kjo mund të ndikojë në harxhimin e baterisë. ${app_name}-i grumbullon të dhëna analitike anonime që të na lejojë ta përmirësojmë aplikacionin. Ju lutemi, aktivizoni analizat që të na ndihmoni të përmirësojmë ${app_name}-in. @@ -2201,7 +2201,7 @@ %d sekonda Shfaq akte gjendjeje përdoruesish të dhomës - Përfshin akte ftimi/pjesëmarrjeje/ikjeje/përzënieje/dëbimi dhe ndryshime emri avatari/shfaqjeje. + Përfshin akte ftimi/pjesëmarrjeje/ikjeje/heqjeje/dëbimi dhe ndryshime emri avatari/shfaqjeje. Kopjeruajtje e Sigurt Administroni Ujdisni Kopjeruajtje të Sigurt @@ -2867,8 +2867,8 @@ %1$s Prekeni që të kthehet Thirrje aktive (%1$s) · - Thirrje aktive - %1$d thirrje aktive + Thirrje aktive · + %1$d thirrje aktive · Lidhja dështoi S’ka përgjigje @@ -2913,7 +2913,7 @@ Do të braktisni krejt dhomat dhe hapësirat te %s. Braktis krejt dhomat dhe hapësirat Jeni i sigurt se doni të dilni nga \'%s\? - Zbulim (%S) + Zbulim (%s) Përfundoje ujdisjen Ftoni me email, gjeni kontakte, etj… Përfundo ujdisjen e zbulimit. @@ -2945,7 +2945,7 @@ Të shohë dhe përditësojë role të domosdoshëm për të ndryshuar anë të ndryshme të hapësirës. Leje hapësire Heqja e dëbimit përdoruesit do t’i lejojë të marrë pjesë sërish në hapësirë. - Dëbimi i përdoruesit do ta përzërë atë nga kjo hapësirë dhe do t’i pengojë atij rihyrjen. + Dëbimi i përdoruesit do ta heqë atë nga kjo hapësirë dhe do t’i pengojë atij rihyrjen. Përzënia e përdoruesit do ta heqë prej kësaj hapësire. \n \nQë të pengohet pjesëmarrja sërish e tij, duhet ta dëboni. @@ -3032,8 +3032,8 @@ Ndihmoni të përmirësohet Element-in Aktivizoje - S’ka vota të hedhura - %1$d votë e hedhur. Votoni, që të shihni përfundimet + %1$d votë e hedhur. Votoni, që të shihni përfundimet + %1$d vota të hedhur. Votoni, që të shihni përfundimet Që ndryshimi të hyjë në fuqi, rinisni aplikacionin. Aktivizo elementë LaTeX për matematikë @@ -3048,7 +3048,40 @@ Raporto Vetvetiu Gabime Shfshehtëzimi. Anashkalo ngjyrë nofke Kam tashmë një llogari - Lidhuni me këdo. + Shkëmbim i siguruar mesazhesh. Kontrollin e keni ju. Jini zot i bisedave tuaja. + Jepe vendndodhjen + Aktivizoni dhënie vendndodhjeje + Jepe vendndodhjen + Jepe vendndodhjen + Vendndodhje + Jepe vendndodhjen + Përfundimet shfaqen vetëm kur përfundoni anketimin + Anketim i mbyllur + Votuesit do të shohin përfundime sapo të kenë votuar + Anketim i hapur + Lloj anketimi + Krijoni llogari + Shkëmbim mesazhesh për ekipin tuaj. + I fshehtëzuar skaj më skaj dhe pa u dashur numër telefoni. Pa reklama, apo shfrytëzim të dhënash. + Zgjidhni ku mbahen bisedat tuaja, duke ju dhënë kontroll dhe pavarësi. Të lidhur përmes Matrix-i. + Komunikim i sigurt dhe i pavarur, që ju jep të njëjtën shkallë privatësie si biseda kokë më kokë, në shtëpinë tuaj. + Trego vendndodhje përdoruesi në rrjedhën kohore + Pasi të aktivizohet, do të jeni në gjendje të dërgoni vendndodhjen tuaj në çfarëdo dhome + Hape me + ${app_name} s’njohu dot vendndodhjen tuaj. Ju lutemi, riprovoni më vonë. + ${app_name} s’mësoi dot vendndodhjen tuaj + PËRPUNONI ANKETIMIN + Përpunoni anketimin + S’janë hedhur vota + Shkallë besueshmërie e formësuar gabim + Fshehtëzimi është formësuar gabim + Riktheni Fshehtëzimin + Ju lutemi, lidhuni me një nga përgjegjësit, që ta ktheni fshehtëzimin në një gjendje të vlefshme. + Fshehtëzimi është formësuar gabim. + Tregoi vendndodhjen e vet + Vendndodhje + Fshehtëzimi është formësuar keq, që të mos dërgoni mesazhe. Klikoni që të hapni rregullimet. + Fshehtëzimi është formësuar keq, që të mos dërgoni mesazhe. Ju lutemi, lidhuni me një nga përgjegjësit që ta ktheni fshehtëzimin në një gjendje të vlefshme. \ No newline at end of file diff --git a/vector/src/main/res/values-tr/strings.xml b/vector/src/main/res/values-tr/strings.xml index 50177afcc5..50af4a203c 100644 --- a/vector/src/main/res/values-tr/strings.xml +++ b/vector/src/main/res/values-tr/strings.xml @@ -37,7 +37,7 @@ Deşifre Edilmiş Kaynağı İncele Sil Yeniden Adlandır - İçeriği bildir + Rapor İçeriği Şu anki görüşme Devam eden konferans görüşmesi. \n%1$s veya %2$s olarak katıl @@ -118,7 +118,7 @@ Hesap oluştur Giriş yap Çıkış yap - Ev Sunucusu URL\'si + Ana Sunucu URL\'si Ara Yeni Sohbete Başla Sesli Görüşme Başlat @@ -188,7 +188,7 @@ Hata Sistem Uyarıları Gönder - Kimlik Sunucusu URL\'si + Kimlik sunucusu URL\'si E-posta adresi E-posta adresi (isteğe bağlı) Telefon numarası @@ -213,10 +213,10 @@ Eposta ve telefon numarası ile aynı anda kayıt yaptırmak şimdilik API yüzüden desteklenmiyor. Sadece telefon numarası hesaba eklenecektir. \n \nEpostanı daha sonra profil ayarlarından ekleyebilirsin. - Ana Makine senin robot olmadığından emin olmak istiyor + Bu ana sunucu bir robot olmadığından emin olmak istiyor Kullandığın kullanıcı adı - Ana Makine: - Tanımlayıcı Sunucu: + Ana sunucu: + Kimlik sunucusu: Eposta adresimi doğruladım Şifrenizi sıfırlamak için, hesabınıza bağlı eposta adresini girin: Hesaba bağlı eposta adresi mutlaka girilmeli. @@ -248,8 +248,8 @@ Makbuz Listesini Oku Grup Listesi - 1 üyelik değişimi - %d üyelik değişimi + %d üyelik değişikliği + %d üyelik değişiklikleri Farklı gönder Orijinal @@ -325,28 +325,28 @@ Yeni Sohbet Üye ekle - 1 aktif üye - %d aktif üye + %d aktif üye + %d aktif üyeler - 1 üye - %d üye + %d üye + %d üyeler 1 üye - 1sn - %dsn + %ds 1 + %ds - 1dk + %ddk %ddk - 1sa + %dsa %dsa - 1g + %dg %dg Odadan ayrıl @@ -376,7 +376,7 @@ Oturum Listesini Göster Bu işlemin geri dönüşü yok kullanıcıyı sen ile aynı erişim seviyesine getiriyorsun. \nEmin misin\? - Kullanıcıyı bu engellersen bu odadan kovulacak ve bir daha giremeyecek. + Kullanıcıyı yasaklamak onları bu odadan çıkaracak ve tekrar katılmalarını engelleyecektir. Neden %s adlı kullanıcılar(ı) bu sohbete davet etmek istediğinden emin misin\? "%1$s, " @@ -405,10 +405,10 @@ Gönderilmeyen mesajları tekrar gönder Gönderilmeyen mesajları sil Dosya bulunamadı - Bu odada mesajlaşmaya yetkin yok + Bu odaya mesaj gönderme izniniz yok. - 1 yeni mesaj - %d yeni mesaj + %d yeni mesaj + %d yeni mesajlar Güven Güvenme @@ -426,7 +426,7 @@ Dosyalar Ayarlar - 1 tane seçili + %d tane seçili %d tane seçili Hatalı ID. Eposta adresi ya da Matrix ID\'si olmalıdır \'@localpart:domain\' @@ -447,8 +447,8 @@ DOSYALAR Dizinlere gözat - 1 oda - %d oda + %d oda + %d odalar %2$s adında %1$s oda bulundu @@ -1068,7 +1068,7 @@ Hesap kurtarma epostası kaydedin. Daha sonra isteğe bağlı eposta veya telefon nuraması kullanarak sizi tanıyan kişilerin sizi bulabilmesini sağlayın. Cihazın saldırılara açık, eski bir TLS güvenlik protokolü kullanıyor; güvenliğin için bağlantın engellendi İkincil çağrı yardımcı sunucusuna izin ver - Anasunucunuz çağrı yardımcı sunucusu vermez ise %s çağrı yardımcı sunucusu olarak kullanılacaktır (Çağrıda IP adresiniz paylaşılacaktır) + Ana sunucunuz bir tane sunmadığında %sas assist kullanacaktır (arama sırasında IP adresiniz paylaşılacaktır) Bu eylemi gerçekleştirebilmek için ayarlarınızdan bir kimlik sunucusu ekleyin. Şifreni doğrula ${app_name}, cihazın sınırlı kaynaklarını (pil) koruyacak şekilde arka planda senkronize olur. @@ -1819,4 +1819,211 @@ Burada yükselttiniz. tüm oda üyeleri, katıldıkları andan itibaren. tüm oda üyeleri, davet edildikleri andan itibaren. + Hiçbiri + Yalnızca Söz ve Anahtar Kelimeler + Alanı yükseltin + m.room.server_acl olaylarını gönder + Alan adını değiştir + Alan şifrelemeyi etkinleştir + Alanın ana adresini değiştirme + Alan avatarını değiştir + Bu alanın çeşitli bölümlerini değiştirmek için gereken rolleri güncelleme izniniz yok + Bu alanın çeşitli bölümlerini değiştirmek için gereken rolleri seçin + Alanın çeşitli bölümlerini değiştirmek için gereken rolleri görüntüleyin ve güncelleyin. + Alan izinleri + Şifreleme yanlış yapılandırıldı, böylece mesaj gönderemezsiniz. Ayarlar\'ı açmak için tıklatın. + Şifreleme yanlış yapılandırıldı, böylece mesaj gönderemezsiniz. Şifrelemeyi geçerli bir duruma geri yüklemek için lütfen bir yöneticiyle iletişime geçin. + Yasağı kaldırmak, kullanıcının alana tekrar katılmasına izin verir. + Kullanıcıyı yasaklamak onları bu alandan kaldıracak ve tekrar katılmalarını engelleyecektir. + Kullanıcı bu alandan kaldırılacaktır. +\n +\nTekrar katılmalarını önlemek için, onları yasaklamalısınız. + Yine de Devam Et + Arama sonlandırılıyor… + Cevap yok + Aradığınız kullanıcı meşgul. + Kullanıcı meşgul + %s ile sesli arama + %s ile görüntülü arama + + Cevapsız görüntülü arama + %d cevapsız görüntülü aramalar + + + Cevapsız sesli arama + %d cevapsız sesli aramalar + + Çağrı çalıyor… + Yetkisiz, eksik geçerli kimlik doğrulama kimlik bilgileri + Ana sunucuyu seçin + %s url\'sinde bir ana sunucuya ulaşılamıyor. Lütfen bağlantınızı kontrol edin veya bir ana sunucuyu manuel olarak seçin. + Varsayılan olarak kullan ve tekrar sorma + Her zaman sor + Ana sunucu API URL\'si + Alanlar + Davetliler + Açık içeriğe sahip odalar da dahil olmak üzere oda dizinindeki tüm odaları göster. + Açık içerikli odaları göster + Oda rehberi + Önerilen Odalar + Yeni değer + Şimdi değil + Etkinleştirmek + Geri dönüş + Değiştirmek + Eksik izinler + Sesli mesaj göndermek için lütfen Mikrofona izin verin. + Bu işlemi gerçekleştirmek için lütfen Kameraya sistem ayarlarından izin verin. + Bazı izinler eksik, bu eylemi gerçekleştirmek için lütfen sistem ayarlarından izinleri verin. + Alanlar + daha fazla bilgi edin + Bildirimleri dinleme + Uçtan uca şifrelemeyi etkinleştirdiniz (tanınmayan algoritma %1$s). + %1$s uçtan uca şifrelemeyi etkinleştirdi (tanınmayan algoritma %2$s). + Uçtan uca şifrelemeyi açtın. + %1$s uçtan uca şifrelemeyi etkinleştirdi. + Konukların odaya katılmasını engellediniz. + %1$s konukların odaya katılmasını engelledi. + Konukların odaya katılmasını engellediniz. + %1$s konukların odaya katılmasını engelledi. + Konukların buraya katılmasına izin verdiniz. + %1$s konukların buraya katılmasına izin verdi. + Konukların odaya katılmasına izin verdiniz. + %1$s konukların odaya katılmasına izin verdi. + Bu odanın adreslerini değiştirdin. + %1$s bu odanın adreslerini değiştirdi. + Bu odanın ana ve alternatif adreslerini değiştirdiniz. + %1$s bu odanın ana ve alternatif adreslerini değiştirdi. + Bu odanın alternatif adreslerini değiştirdiniz. + %1$s bu odanın alternatif adreslerini değiştirdi. + + Bu oda için %1$s alternatif adresini kaldırdınız. + Bu oda için %1$s alternatif adreslerini kaldırdınız. + + + %1$s bu oda için %2$s alternatif adresini kaldırdı. + %1$s bu oda için %2$s alternatif adreslerini kaldırdı. + + + Bu oda için %1$s alternatif adresini eklediniz. + Bu oda için %1$s alternatif adreslerini eklediniz. + + + %1$s, bu oda için %2$s alternatif adresini ekledi. + %1$s, bu oda için %2$s alternatif adreslerini ekledi. + + Bu odanın ana adresini kaldırdınız. + %1$s bu odanın ana adresini kaldırdı. + Bu odanın ana adresini %1$s olarak ayarladınız. + %1$s, bu odanın ana adresini %2$s olarak belirledi. + Bu oda için adres olarak %1$s eklediniz ve %2$s\'i kaldırdınız. + %1$s, bu oda için adres olarak %2$s ekledi ve %3$s\'i kaldırdı. + + %1$s adresini bu odanın adresi olarak kaldırdınız. + %1$s adresini bu oda için adres olarak kaldırdınız. + + + %1$s, %2$s adresini bu odanın adresi olarak kaldırdı. + %1$s, %2$s adresini bu odanın adresi olarak kaldırdı. + + + Bu oda için adres olarak %1$s eklediniz. + Bu oda için adres olarak %1$s eklediniz. + + + %1$s, bu oda için bir adres olarak %2$s ekledi. + %1$s, bu oda için adres olarak %2$s ekledi. + + %1$s adlı kişinin davetini geri çektiniz. Neden: %2$s + %1$s, %2$s adlı kişinin davetini geri çekti. Neden: %3$s + %1$s davetini kabul ettiniz. Neden: %2$s + %1$s, %2$s davetini kabul etti. Neden: %3$s + %1$s için odaya katılma davetini iptal ettiniz. Neden: %2$s + %1$s, %2$s için odaya katılma davetini iptal etti. Neden: %3$s + Odaya katılması için %1$s\'a bir davetiye gönderdiniz. Neden: %2$s + %1$s, odaya katılması için %2$s\'a bir davetiye gönderdi. Neden: %3$s + %1$s\'i yasakladınız. Neden: %2$s + %1$s, %2$s\'yi yasakladı. Neden: %3$s + %1$s yasağını kaldırdınız. Neden: %2$s + %1$s planlanmamış %2$s. Nedeni: %3$s + %1$s kaldırdınız. Nedeni: %2$s + %1$s %2$s kaldırıldı. Nedeni: %3$s + Daveti reddettiniz. Sebep: %1$s + %1$s daveti reddetti. Neden: %2$s + Gittin. Sebep: %1$s + %1$s kaldı. Neden: %2$s + Odadan ayrıldın. Sebep: %1$s + %1$s odadan ayrıldı. Neden: %2$s + Katıldın. Sebep: %1$s + %1$s katıldı. Neden: %2$s + Odaya katıldın. Sebep: %1$s + %1$s odaya katıldı. Neden: %2$s + %1$s sizi davet etti. Neden: %2$s + %1$s davet ettiniz. Neden: %2$s + %1$s, %2$s\'i davet etti. Neden: %3$s + Senin davetiyen. Sebep: %1$s + %1$s\'in davetiyesi. Neden: %2$s + Gönderme kuyruğunu temizle + Mesajı gönderildi + İlk senkronizasyon: +\nHesap verilerini içe aktarma + İlk senkronizasyon: +\nToplulukları içe aktarma + İlk senkronizasyon: +\nSol odalar içe aktarılıyor + İlk senkronizasyon: +\nDavetli odaları içe aktarma + İlk senkronizasyon: +\nKonuşmalarınız yükleniyor +\nÇok sayıda odaya katıldıysanız, bu biraz zaman alabilir + İlk senkronizasyon: +\nOdaları içe aktarma + İlk senkronizasyon: +\nKripto içe aktarma + İlk senkronizasyon: +\nHesap içe aktarılıyor… + İlk senkronizasyon: +\nVeriler indiriliyor… + İlk senkronizasyon: +\nSunucu yanıtı bekleniyor… + Boş oda (%s idi) + + %1$s ve 1 kişi daha + %1$s ve %2$d kişi daha + + + %1$s, %2$s, %3$s ve %4$d diğer + %1$s, %2$s, %3$s ve %4$d kişi daha + + %1$s, %2$s, %3$s ve %4$s + %1$s ve %2$s + %s\'den davet + Bu odaya katılmanıza izin verilmiyor + Şu anda boş bir odaya yeniden katılmak mümkün değildir. + redaksiyon yapılamadı + Gönderenin cihazı bize bu mesajın anahtarlarını göndermedi. + ** Şifre çözülemiyor: %s ** + %1$s, %2$s ile %3$s arasında + %1$s, %2$s\'nin güç seviyesini değiştirdi. + %1$s\'nin güç seviyesini değiştirdiniz. + Özel (%1$d) + Moderatör + Admin + %1$s tarafından video konferans başlatıldı + %1$s widget\'ını değiştirdiniz + %1$s, %2$s widget\'ı ekledi + %1$s, %2$s profilini güncelledi + VoIP konferansı bitti + VoIP konferansı başladı + Bir VoIP konferansı talep ettiniz + %1$s bir VoIP konferansı istedi + 🎉 Tüm sunucuların katılımı yasaklanmıştır! Bu oda artık kullanılamaz. + • IP değişmezleriyle eşleşen sunucular artık yasaklanmıştır. + • IP değişmez değerleriyle eşleşen sunuculara artık izin verilmektedir. + • %s ile eşleşen sunucular izin verilenler listesinden kaldırıldı. + • %s ile eşleşen sunuculara artık izin veriliyor. + • %s ile eşleşen sunucular yasak listesinden kaldırıldı. + • %s ile eşleşen sunucular artık yasaklandı. + Bu oda için sunucu ACL\'lerini değiştirdiniz. + %s, bu oda için sunucu ACL\'lerini değiştirdi. \ 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 0a6261d2c1..c9e8372cc9 100644 --- a/vector/src/main/res/values-uk/strings.xml +++ b/vector/src/main/res/values-uk/strings.xml @@ -3105,7 +3105,7 @@ Точно видалити опитування\? Ви не зможете відновити опитування після видалення. Видалити опитування Голосування завершене - Голос надіслано + Проголосовано Завершити опитування Люди більше не зможуть голосувати, і будуть показані остаточні результати опитування. Завершити це опитування\? @@ -3163,7 +3163,40 @@ Замінити колір псевдоніма Я вже маю обліковий запис Удоскональте спілкування в команді. - З\'єднуйтеся з будь-ким. + Захищене спілкування. Ви контролюєте все. Володійте своїми розмовами. + Поділитися місцеперебуванням + Зображувати місцеперебування користувача у стрічці + Після увімкнення ви зможете надіслати своє місцеперебування до будь-якої кімнати + Увімкнути надсилання місцеперебування + Відкрити за допомогою + ${app_name} не може отримати доступ до вашого місцеперебування. Спробуйте пізніше. + ${app_name} не може отримати доступ до вашого місцеперебування + Поділитися місцеперебуванням + Поділитися місцеперебуванням + Місцеперебування + Поділитися місцеперебуванням + Результати можна переглянути лише після завершення опитування + Закрите опитування + Усі, хто проголосує, побачать результати одразу після голосування + Відкрити опитування + Тип опитування + РЕДАГУВАТИ ОПИТУВАННЯ + Редагувати опитування + Немає голосових бесід + Неправильне налаштування рівня довіри + Шифрування неправильно налаштовано + Відновити шифрування + Зв\'яжіться з адміністратором для відновлення роботи шифрування. + Шифрування неправильно налаштовано. + Поділилися своїм місцеперебуванням + Створити обліковий запис + Спілкування вашої команди. + З наскрізним шифруванням і без вимоги номера телефону. Без реклами чи аналізу даних. + Оберіть де спілкуватись, що дасть вам контроль і незалежність. Під\'єднано через Matrix. + Захищене та незалежне спілкування, яке дає вам такий самий рівень приватності, як розмова віч-на-віч у вашому власному домі. + Місцеперебування + Шифрування було налаштовано неправильно, тому ви не можете надсилати повідомлення. Торкніться, щоб відкрити налаштування. + Шифрування було налаштовано неправильно, тому ви не можете надсилати повідомлення. Зверніться до адміністратора, щоб відновити роботу шифрування. \ 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 9a8e90f751..3dcf132f92 100644 --- a/vector/src/main/res/values-zh-rTW/strings.xml +++ b/vector/src/main/res/values-zh-rTW/strings.xml @@ -542,7 +542,7 @@ 提及 顯示工作階段清單 您確定想要邀請 %s 到聊天室? - 封鎖使用者將會把他們從此聊天室中踢除,並避免他們再次加入。 + 封鎖使用者將會把他們從此聊天室中移除,並避免他們再次加入。 透過 ID 邀請 裝置上的聯絡人(%d) 使用者目錄 (%s) @@ -1046,7 +1046,7 @@ 顯示讀取回條 點選讀取回條以顯示詳細資料。 顯示加入與離開的活動 - 邀請、踢出與封鎖不受影響。 + 邀請、移除與封鎖不受影響。 顯示帳號活動 包含大頭貼與顯示名稱變動。 密碼 @@ -2298,7 +2298,7 @@ %d 秒 顯示聊天室成員狀態活動 - 包含邀請/加入/離開/踢除/封鎖事件與大頭貼/顯示名稱變更等。 + 包含邀請/加入/離開/移除/封鎖事件與大頭貼/顯示名稱變更等。 投票 反應:%s 驗證結論 @@ -2902,7 +2902,7 @@ 檢視並更新變更空間各部份所需的角色。 空間權限 取消封鎖使用者將讓他們可以再次加入空間。 - 封鎖使用者會將他們踢出此空間並防止他們再次加入。 + 封鎖使用者會將他們自此空間移除並防止他們再次加入。 踢除使用者將會將他們從此空間中移除。 \n \n為了防止他們再加入,您應該封鎖他們。 @@ -3002,7 +3002,40 @@ 覆寫暱稱色彩 我已有一個帳號 減少團隊的懈怠。 - 與任何人聯絡。 + 安全傳送訊息。 您已掌控了您的資料。 擁有您的對話。 + 分享位置 + 在時間軸中繪製使用者位置 + 啟用後,您就能將您的位置傳送至任何聊天室 + 啟用位置分享 + 開啟以 + ${app_name} 無法存取您的位置。請稍後再試。 + ${app_name} 無法存取您的位置 + 分享位置 + 分享位置 + 位置 + 分享位置 + 結果僅在您結束投票後顯示 + 已關閉投票 + 投票者在投票後可以立刻看到投票結果 + 開啟投票 + 投票類型 + 編輯投票 + 編輯投票 + 沒有投票 + 信任層級設定錯誤 + 加密設定錯誤 + 還原加密 + 請聯絡管理員以將加密還原至有效狀態。 + 加密設定錯誤。 + 分享了他們的位置 + 建立帳號 + 為您的團隊傳送訊息。 + 端到端加密,不需要電話號碼。沒有廣告或資料挖礦。 + 選擇保留對話的位置,讓您擁有控制權與獨立性。透過 Matrix 連結。 + 安全且獨立的通訊,為您提供與在家中進行面對面對話相同的隱私等級。 + 位置 + 加密設定錯誤,因此您無法傳送訊息。點擊以開啟設定。 + 加密設定錯誤,因此您無法傳送訊息。請聯絡管理員將加密還原至有效的狀態。 \ No newline at end of file diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 7a88126675..4e1d6d28cf 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -3711,6 +3711,7 @@ Share location Location Share location + Map Share location ${app_name} could not access your location ${app_name} could not access your location. Please try again later. diff --git a/vector/src/test/java/im/vector/app/features/location/LocationDataTest.kt b/vector/src/test/java/im/vector/app/features/location/LocationDataTest.kt new file mode 100644 index 0000000000..fcfff0096f --- /dev/null +++ b/vector/src/test/java/im/vector/app/features/location/LocationDataTest.kt @@ -0,0 +1,60 @@ +/* + * 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 + +import org.amshove.kluent.shouldBeEqualTo +import org.amshove.kluent.shouldBeNull +import org.junit.Test + +class LocationDataTest { + @Test + fun validCases() { + parseGeo("geo:12.34,56.78;13.56") shouldBeEqualTo + LocationData(latitude = 12.34, longitude = 56.78, uncertainty = 13.56) + parseGeo("geo:12.34,56.78") shouldBeEqualTo + LocationData(latitude = 12.34, longitude = 56.78, uncertainty = null) + // Error is ignored in case of invalid uncertainty + parseGeo("geo:12.34,56.78;13.5z6") shouldBeEqualTo + LocationData(latitude = 12.34, longitude = 56.78, uncertainty = null) + parseGeo("geo:12.34,56.78;13. 56") shouldBeEqualTo + LocationData(latitude = 12.34, longitude = 56.78, uncertainty = null) + // Space are ignored (trim) + parseGeo("geo: 12.34,56.78;13.56") shouldBeEqualTo + LocationData(latitude = 12.34, longitude = 56.78, uncertainty = 13.56) + parseGeo("geo:12.34,56.78; 13.56") shouldBeEqualTo + LocationData(latitude = 12.34, longitude = 56.78, uncertainty = 13.56) + } + + @Test + fun invalidCases() { + parseGeo("").shouldBeNull() + parseGeo("geo").shouldBeNull() + parseGeo("geo:").shouldBeNull() + parseGeo("geo:12.34").shouldBeNull() + parseGeo("geo:12.34;13.56").shouldBeNull() + parseGeo("gea:12.34,56.78;13.56").shouldBeNull() + parseGeo("geo:12.x34,56.78;13.56").shouldBeNull() + parseGeo("geo:12.34,56.7y8;13.56").shouldBeNull() + // Spaces are not ignored if inside the numbers + parseGeo("geo:12.3 4,56.78;13.56").shouldBeNull() + parseGeo("geo:12.34,56.7 8;13.56").shouldBeNull() + // Or in the protocol part + parseGeo(" geo:12.34,56.78;13.56").shouldBeNull() + parseGeo("ge o:12.34,56.78;13.56").shouldBeNull() + parseGeo("geo :12.34,56.78;13.56").shouldBeNull() + } +}