From c5fe9d4a18b3898b976b3431ce2093929d35128e Mon Sep 17 00:00:00 2001 From: lvre <7uu3qrbvm@relay.firefox.com> Date: Tue, 29 Nov 2022 15:07:51 +0000 Subject: [PATCH 001/108] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (2556 of 2556 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/pt_BR/ --- library/ui-strings/src/main/res/values-pt-rBR/strings.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/ui-strings/src/main/res/values-pt-rBR/strings.xml b/library/ui-strings/src/main/res/values-pt-rBR/strings.xml index 8baba5df53..8129a234fb 100644 --- a/library/ui-strings/src/main/res/values-pt-rBR/strings.xml +++ b/library/ui-strings/src/main/res/values-pt-rBR/strings.xml @@ -2723,7 +2723,7 @@ Sessões não-verificadas Sessões inativas são sessões que você não tem usado em algum tempo, mas elas continuam a receber chaves de encriptação. \n -\nRemover sessões inativas melhora segurança e performance, e torna-o mais fácil para você identificar se uma nova sessão é suspeita. +\nRemover sessões inativas melhora segurança e performance, e torna mais fácil para você identificar se uma nova sessão é suspeita. Sessões inativas Por favor esteja ciente que nomes de sessões também são visíveis a pessoas com quem você se comunica. Nomes de sessões personalizadas podem ajudar você a reconhecer seus dispositivos mais facilmente. @@ -2844,9 +2844,9 @@ Não dá pra começar um novo broadcast de voz Avançar rápido 30 segundos Retroceder 30 segundos - Sessões verificadas são onde quer que você está usando esta conta depois de entrar sua frasepasse ou confirmar sua identidade com uma outra sessão verificada. + Sessões verificadas são onde quer que você esteja usando esta conta depois de entrar sua frasepasse ou confirmar sua identidade com uma outra sessão verificada. \n -\nIsto significa que você tem todas as chaves necessitadas para destrancar suas mensagens encriptadas e confirmar a outras(os) usuárias(os) que você confia nesta sessão. +\nIsto significa que você tem todas as chaves necessárias para destrancar suas mensagens encriptadas e confirmar a outras(os) usuárias(os) que você confia nesta sessão. Fazer signout de %1$d sessão Fazer signout de %1$d sessões From 1c0fe56329f828aab086dba0f814b5cadc71f11f Mon Sep 17 00:00:00 2001 From: LinAGKar Date: Mon, 28 Nov 2022 21:32:51 +0000 Subject: [PATCH 002/108] Translated using Weblate (Swedish) Currently translated at 100.0% (2556 of 2556 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/sv/ --- .../ui-strings/src/main/res/values-sv/strings.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/library/ui-strings/src/main/res/values-sv/strings.xml b/library/ui-strings/src/main/res/values-sv/strings.xml index 65318096c7..45cfe4338b 100644 --- a/library/ui-strings/src/main/res/values-sv/strings.xml +++ b/library/ui-strings/src/main/res/values-sv/strings.xml @@ -2852,4 +2852,18 @@ Kan inte starta en ny röstsändning Spola framåt 30 sekunder Spola tillbaka 30 sekunder + skickade en omröstning. + skickade en dekal. + skickade en video. + skickade en bild. + skickade ett röstmeddelande. + skickade en ljudfil. + skickade en fil. + Svar på + Dölj IP-adress + Visa IP-adress + %1$s kvar + Citerar + Besvarar %s + Redigerar \ No newline at end of file From b3ffc4d76cf09f3cdeb688a17ad4c08ba7fc86d5 Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Mon, 28 Nov 2022 01:47:14 +0000 Subject: [PATCH 003/108] Translated using Weblate (Ukrainian) Currently translated at 100.0% (2556 of 2556 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/uk/ --- .../ui-strings/src/main/res/values-uk/strings.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/library/ui-strings/src/main/res/values-uk/strings.xml b/library/ui-strings/src/main/res/values-uk/strings.xml index 19889892ff..d65a7a1da7 100644 --- a/library/ui-strings/src/main/res/values-uk/strings.xml +++ b/library/ui-strings/src/main/res/values-uk/strings.xml @@ -2966,16 +2966,16 @@ Вийти Залишилося %1$s надсилає аудіофайл. - відправив файл. + надсилає файл. У відповідь на Сховати IP-адресу - створив голосування. - відправив наліпку. - відправив відео. - відправив зображення. - відправив голосове повідомлення. + створює опитування. + надсилає наліпку. + надсилає відео. + надсилає зображення. + надсилає голосове повідомлення. Показати IP-адресу Цитуючи - У відповідь на %s + У відповідь %s Редагування \ No newline at end of file From 4a70ea851814ddb394ecd091b620bc1062323e2a Mon Sep 17 00:00:00 2001 From: LinAGKar Date: Mon, 28 Nov 2022 21:24:35 +0000 Subject: [PATCH 004/108] Translated using Weblate (Swedish) Currently translated at 100.0% (82 of 82 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/sv/ --- fastlane/metadata/android/sv-SE/changelogs/40105080.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/sv-SE/changelogs/40105080.txt diff --git a/fastlane/metadata/android/sv-SE/changelogs/40105080.txt b/fastlane/metadata/android/sv-SE/changelogs/40105080.txt new file mode 100644 index 0000000000..cee589ed35 --- /dev/null +++ b/fastlane/metadata/android/sv-SE/changelogs/40105080.txt @@ -0,0 +1,2 @@ +Huvudsakliga ändringar i den här versionen: buggfixar och förbättringar. +Full ändringslogg: https://github.com/vector-im/element-android/releases From d1cef1bc5c56fc804fdfb9059d489ca43dbbe13f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Nov 2022 23:02:20 +0000 Subject: [PATCH 005/108] Bump com.autonomousapps.dependency-analysis from 1.16.0 to 1.17.0 Bumps com.autonomousapps.dependency-analysis from 1.16.0 to 1.17.0. --- updated-dependencies: - dependency-name: com.autonomousapps.dependency-analysis dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 51604b67a8..58084ab64d 100644 --- a/build.gradle +++ b/build.gradle @@ -48,7 +48,7 @@ plugins { id "com.google.devtools.ksp" version "1.7.21-1.0.8" // Dependency Analysis - id 'com.autonomousapps.dependency-analysis' version "1.16.0" + id 'com.autonomousapps.dependency-analysis' version "1.17.0" // Gradle doctor id "com.osacky.doctor" version "0.8.1" } From 279756bdfb64ab1c1df07be594313d0ae3ae334d Mon Sep 17 00:00:00 2001 From: waclaw66 Date: Thu, 1 Dec 2022 05:59:21 +0000 Subject: [PATCH 006/108] Translated using Weblate (Czech) Currently translated at 100.0% (2558 of 2558 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/cs/ --- library/ui-strings/src/main/res/values-cs/strings.xml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/library/ui-strings/src/main/res/values-cs/strings.xml b/library/ui-strings/src/main/res/values-cs/strings.xml index f260a129fc..67cc3353aa 100644 --- a/library/ui-strings/src/main/res/values-cs/strings.xml +++ b/library/ui-strings/src/main/res/values-cs/strings.xml @@ -2789,7 +2789,7 @@ Pravost této šifrované zprávy nelze v tomto zařízení zaručit. Požadujte, aby klávesnice neaktualizovala žádné personalizované údaje, jako je historie psaní a slovník, na základě toho, co jste napsali v konverzacích. Upozorňujeme, že některé klávesnice nemusí toto nastavení respektovat. Inkognito klávesnice - Přidá znaky (╯°□°)╯︵ ┻━┻ před zprávy ve formátu obyčejného textu + Přidá znaky (╯°□°)╯︵ ┻━┻ před zprávy ve formátu prostého textu Hlasové vysílání Otevřít nástroje pro vývojáře 🔒 V nastavení zabezpečení jste povolili šifrování pouze do ověřených relací pro všechny místnosti. @@ -2824,8 +2824,8 @@ ${app_name} potřebuje oprávnění k zobrazování oznámení. Oznámení mohou zobrazovat vaše zprávy, pozvánky atd. \n \nPro zobrazování oznámení povolte přístup na dalších vyskakovacích oknech. - Vyzkoušejte rozšířený textový editor (textový režim již brzy) - Povolit rozšířený textový editor + Vyzkoušejte editor formátovaného textu (režim prostého textu již brzy) + Povolit editor formátovaného textu Ujistěte se, že znáte původ tohoto kódu. Propojením zařízení poskytnete někomu plný přístup ke svému účtu. Potvrdit Zkuste to znovu @@ -2868,7 +2868,7 @@ Druhé zařízení je již přihlášeno. Při nastavování zabezpečeného zasílání zpráv se vyskytl problém se zabezpečením. Může být napadena jedna z následujících věcí: váš domovský server; vaše internetové připojení; vaše zařízení; Žádost se nezdařila. - Ukládání do vyrovnávací paměti + Ukládání do vyrovnávací paměti… Pozastavit hlasové vysílání Přehrát nebo obnovit hlasové vysílání Ukončit záznam hlasového vysílání @@ -2922,4 +2922,6 @@ Citace Odpovídám na %s Úpravy + Zobrazit poslední chaty v nabídce sdílení systému + Povolit přímé sdílení \ No newline at end of file From a0528fe0ce203f5ac1bfe4f012a636daa0536bb8 Mon Sep 17 00:00:00 2001 From: Vri Date: Wed, 30 Nov 2022 06:31:56 +0000 Subject: [PATCH 007/108] Translated using Weblate (German) Currently translated at 100.0% (2558 of 2558 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/de/ --- library/ui-strings/src/main/res/values-de/strings.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/ui-strings/src/main/res/values-de/strings.xml b/library/ui-strings/src/main/res/values-de/strings.xml index be53c15026..809ee477fc 100644 --- a/library/ui-strings/src/main/res/values-de/strings.xml +++ b/library/ui-strings/src/main/res/values-de/strings.xml @@ -2815,7 +2815,7 @@ Die Anfrage ist fehlgeschlagen. Abspielen oder fortsetzen der Sprachübertragung Fortsetzen der Sprachübertragung - Puffere + Puffere … Pausiere Sprachübertragung Stoppe Aufzeichnung der Sprachübertragung Pausiere Aufzeichnung der Sprachübertragung @@ -2865,4 +2865,6 @@ %s antworten IP-Adresse ausblenden IP-Adresse anzeigen + Kürzliche Unterhaltungen im Teilen-Menü des Systems anzeigen + Direktes Teilen aktivieren \ No newline at end of file From c6ed280a6f0a65b09cb63a8638afd3205fd2ccaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Wed, 30 Nov 2022 08:14:33 +0000 Subject: [PATCH 008/108] Translated using Weblate (Estonian) Currently translated at 99.6% (2550 of 2558 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/et/ --- library/ui-strings/src/main/res/values-et/strings.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/ui-strings/src/main/res/values-et/strings.xml b/library/ui-strings/src/main/res/values-et/strings.xml index 156221379d..96d9650ceb 100644 --- a/library/ui-strings/src/main/res/values-et/strings.xml +++ b/library/ui-strings/src/main/res/values-et/strings.xml @@ -2805,7 +2805,7 @@ Teine seade on juba võrku loginud. Turvalise sõnumivahetuse ülesseadmisel tekkis turvaviga. Üks kolmest võib olla sattunud vale osapoole kontrolli alla: sinu koduserver, sinu internetiühendus või sinu seade; Päring ei õnnestunud. - Andmed on puhverdamisel + Andmed on puhverdamisel… Alusta või jätka ringhäälingukõne esitamist Lõpeta ringhäälingukõne salvestamine Peata ringhäälingukõne salvestamine @@ -2857,4 +2857,6 @@ saatis video. saatis kleepsu. koostas küsitluse. + Kasuta otsejagamist + Näita viimaseid vestlusi süsteemses jagamisvaates \ No newline at end of file From 27acb198ab9c760e4ec348c153abc9a088dd1f0d Mon Sep 17 00:00:00 2001 From: Danial Behzadi Date: Wed, 30 Nov 2022 11:24:07 +0000 Subject: [PATCH 009/108] Translated using Weblate (Persian) Currently translated at 99.7% (2551 of 2558 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/fa/ --- library/ui-strings/src/main/res/values-fa/strings.xml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/library/ui-strings/src/main/res/values-fa/strings.xml b/library/ui-strings/src/main/res/values-fa/strings.xml index cc8d60a87b..a3a74df10f 100644 --- a/library/ui-strings/src/main/res/values-fa/strings.xml +++ b/library/ui-strings/src/main/res/values-fa/strings.xml @@ -943,7 +943,7 @@ \n \nپیام‌هایتان با قفل‌هایی امن شده‌اند و فقط شما و گیرندگان دیگر، کلیدهای یکتا را برای قفل‌گشاییشان دارید. امنیت - بثیش‌تر بدانید + بیش‌تر بدانید بیش‌تر کنش‌های مدیر تنظمیات اتاق @@ -2783,7 +2783,7 @@ نظرسنجی‌ها پیوست‌ها برچسب‌ها - میانگیری + میانگیری… زنده تأیید ۳ @@ -2844,4 +2844,9 @@ نقل کردن پاسخ دادن به %s ویرایش کردن + می‌توانید با یک رمز QR از این افزاره برای ورود به افزاره‌ای همراه یا روی وب استفاده کنید. دو راه برای این کار وجود دارد: + مشکلی امنیتی در برپایی پیام‌رسانی امن وجود داشت. ممکن است یکی از موارد زیر دستکاری شده باشند: کارساز خانیگیتان؛ اتّصال اینترنتیتان؛ افزاره(های)تان؛ + لطفاً مطمئن شوید که مبدأ این کد را می‌دانید. با پیوند دادن افزاره‌ها، دسترسی کامل را به حسابتان می‌دهید. + نمایش گپ‌های اخیر در فهرست هم رسانی سامانه + به کار انداختن هم‌رسانی مستقیم \ No newline at end of file From 1c7f789928f3414be104f934cdad76c25a9cd25b Mon Sep 17 00:00:00 2001 From: Glandos Date: Wed, 30 Nov 2022 11:55:22 +0000 Subject: [PATCH 010/108] Translated using Weblate (French) Currently translated at 100.0% (2558 of 2558 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/fr/ --- library/ui-strings/src/main/res/values-fr/strings.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/ui-strings/src/main/res/values-fr/strings.xml b/library/ui-strings/src/main/res/values-fr/strings.xml index cf49733bdf..d74d3bac71 100644 --- a/library/ui-strings/src/main/res/values-fr/strings.xml +++ b/library/ui-strings/src/main/res/values-fr/strings.xml @@ -2814,7 +2814,7 @@ Vous pouvez utiliser cet appareil pour connecter un appareil mobile ou un client web avec un QR code. Il y a deux façons de le faire : Se connecter avec un QR code Scanner le QR code - Mise en mémoire tampon + Mise en mémoire tampon… Mettre en pause la diffusion audio Lire ou continuer la diffusion audio Arrêter l’enregistrement de la diffusion audio @@ -2866,4 +2866,6 @@ Citation de Réponse à %s Modification + Affiche les conversations récentes dans le menu de partage du système + Activer le partage direct \ No newline at end of file From ab1db4de6870537774b8153dd82d28cc5153bca4 Mon Sep 17 00:00:00 2001 From: Linerly Date: Tue, 29 Nov 2022 23:32:26 +0000 Subject: [PATCH 011/108] Translated using Weblate (Indonesian) Currently translated at 100.0% (2558 of 2558 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/id/ --- library/ui-strings/src/main/res/values-in/strings.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/ui-strings/src/main/res/values-in/strings.xml b/library/ui-strings/src/main/res/values-in/strings.xml index ce9e524067..da4c474689 100644 --- a/library/ui-strings/src/main/res/values-in/strings.xml +++ b/library/ui-strings/src/main/res/values-in/strings.xml @@ -2762,7 +2762,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan. Permintaan gagal. Memungkinkan untuk merekam dan mengirim siaran suara dalam linimasa ruangan. Aktifkan siaran suara (dalam pengembangan aktif) - Memuat + Memuat… Jeda siaran suara Mainkan atau lanjutkan siaran suara Hentikan rekaman siaran suara @@ -2812,4 +2812,6 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan. Mengedit Tampilkan alamat IP Membalas ke %s + Tampilkan obrolan terkini dalam menu pembagian sistem + Aktifkan pembagian langsung \ No newline at end of file From 05e6a59a867664d9a35c3d5927ed22bced059a79 Mon Sep 17 00:00:00 2001 From: random Date: Thu, 1 Dec 2022 09:11:39 +0000 Subject: [PATCH 012/108] Translated using Weblate (Italian) Currently translated at 100.0% (2558 of 2558 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/it/ --- library/ui-strings/src/main/res/values-it/strings.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/ui-strings/src/main/res/values-it/strings.xml b/library/ui-strings/src/main/res/values-it/strings.xml index d244f26a43..d6a7858ebc 100644 --- a/library/ui-strings/src/main/res/values-it/strings.xml +++ b/library/ui-strings/src/main/res/values-it/strings.xml @@ -2805,7 +2805,7 @@ L\'altro dispositivo ha già fatto l\'accesso. Si è verificato un problema di sicurezza configurando i messaggi sicuri. Una delle seguenti cose potrebbe essere compromessa: il tuo homeserver; la/e connessione/i internet; il/i dispositivo/i; La richiesta è fallita. - Buffering + Buffer… Sospendi trasmissione vocale Avvia o riprendi trasmissione vocale Ferma registrazione trasmissione vocale @@ -2857,4 +2857,6 @@ Citazione Risposta a %s Modifica + Mostra chat recenti nel menu di condivisione di sistema + Attiva condivisione diretta \ No newline at end of file From 3d84a999e06d015631613542d008ab26753d5eea Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Wed, 30 Nov 2022 09:50:08 +0000 Subject: [PATCH 013/108] Translated using Weblate (Slovak) Currently translated at 100.0% (2558 of 2558 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/sk/ --- library/ui-strings/src/main/res/values-sk/strings.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/ui-strings/src/main/res/values-sk/strings.xml b/library/ui-strings/src/main/res/values-sk/strings.xml index 078ffc44eb..f59073c5db 100644 --- a/library/ui-strings/src/main/res/values-sk/strings.xml +++ b/library/ui-strings/src/main/res/values-sk/strings.xml @@ -2868,7 +2868,7 @@ Žiadosť zlyhala. Možnosť nahrávania a odosielania hlasového vysielania v časovej osi miestnosti. Zapnúť hlasové vysielanie (v štádiu aktívneho vývoja) - Načítavanie do vyrovnávacej pamäte + Načítavanie do vyrovnávacej pamäte… Pozastaviť hlasové vysielanie Prehrať alebo pokračovať v nahrávaní hlasového vysielania Zastaviť nahrávanie hlasového vysielania @@ -2922,4 +2922,6 @@ Zobraziť IP adresu Odpoveď na %s Úprava + Zobraziť posledné konverzácie v systémovej ponuke zdieľania + Povoliť priame zdieľanie \ No newline at end of file From b759f40c13eb1ba23a7e5ad5d5ebbaeb6dc18cce Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Wed, 30 Nov 2022 16:23:19 +0000 Subject: [PATCH 014/108] Translated using Weblate (Albanian) Currently translated at 99.3% (2541 of 2558 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/sq/ --- library/ui-strings/src/main/res/values-sq/strings.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/ui-strings/src/main/res/values-sq/strings.xml b/library/ui-strings/src/main/res/values-sq/strings.xml index 800ec17dcf..58214e8a22 100644 --- a/library/ui-strings/src/main/res/values-sq/strings.xml +++ b/library/ui-strings/src/main/res/values-sq/strings.xml @@ -2659,7 +2659,7 @@ \nKy shërbyes Home mund të mos jetë formësuar të shfaqë harta. Përfundimet do të jenë të dukshme pasi të ketë përfunduar pyetësori Kur bëhet ftesë në një dhomë të fshehtëzuar që ka historik ndarjesh me të tjerët, historiku i fshehtëzuar do të jetë i dukshëm. - Përdo + Ndal transmetim zanor Luani ose vazhdoni luajtje transmetimi zanor Ndal incizim transmetimi zanor @@ -2851,4 +2851,6 @@ Kthim prapa 30 sekonda Si përgjigje për %s Aktivizo MD të lënë për më vonë + Tkurr pjella të %s + Zgjero pjella të %s \ No newline at end of file From 571d1a4816652f6a46b1b7eced2ef6625c4f5e1a Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Tue, 29 Nov 2022 22:10:18 +0000 Subject: [PATCH 015/108] Translated using Weblate (Ukrainian) Currently translated at 100.0% (2558 of 2558 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/uk/ --- library/ui-strings/src/main/res/values-uk/strings.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/ui-strings/src/main/res/values-uk/strings.xml b/library/ui-strings/src/main/res/values-uk/strings.xml index d65a7a1da7..6a1c5355ab 100644 --- a/library/ui-strings/src/main/res/values-uk/strings.xml +++ b/library/ui-strings/src/main/res/values-uk/strings.xml @@ -2922,7 +2922,7 @@ Запит не виконаний. Можливість записувати та надсилати голосові трансляції до стрічки кімнати. Увімкнути голосові трансляції (в активній розробці) - Буферизація + Буферизація… Призупинити голосову трансляцію Відтворити або поновити відтворення голосової трансляції Припинити запис голосової трансляції @@ -2978,4 +2978,6 @@ Цитуючи У відповідь %s Редагування + Показувати останні бесіди в системному меню загального доступу + Увімкнути пряме поширення \ No newline at end of file From a57162cf83aa03ec8959f1585a76621a27b04fde Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Wed, 30 Nov 2022 02:05:55 +0000 Subject: [PATCH 016/108] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (2558 of 2558 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/zh_Hant/ --- library/ui-strings/src/main/res/values-zh-rTW/strings.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/ui-strings/src/main/res/values-zh-rTW/strings.xml b/library/ui-strings/src/main/res/values-zh-rTW/strings.xml index dc5f6d85e3..9a5439b2ae 100644 --- a/library/ui-strings/src/main/res/values-zh-rTW/strings.xml +++ b/library/ui-strings/src/main/res/values-zh-rTW/strings.xml @@ -2760,7 +2760,7 @@ 請求失敗。 可以在聊天室時間軸中錄製並傳送語音廣播。 啟用語音廣播(正在積極開發中) - 正在緩衝 + 正在緩衝…… 暫停語音廣播 播放或繼續語音廣播 停止語音廣播錄製 @@ -2810,4 +2810,6 @@ 引用 回覆給 %s 正在編輯 + 在系統分享選單中顯示最近聊天 + 啟用直接分享 \ No newline at end of file From 56715f13d48d31bf6df88e07fb31c29d99d54ee3 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Wed, 30 Nov 2022 16:24:03 +0000 Subject: [PATCH 017/108] Translated using Weblate (Albanian) Currently translated at 100.0% (82 of 82 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/sq/ --- fastlane/metadata/android/sq/changelogs/40105080.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/sq/changelogs/40105080.txt diff --git a/fastlane/metadata/android/sq/changelogs/40105080.txt b/fastlane/metadata/android/sq/changelogs/40105080.txt new file mode 100644 index 0000000000..b059e86cbd --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40105080.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: ndreqje të metash dhe përmirësime. +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases From b70370b21704e5908a9c18438be08cd8eb337930 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Dec 2022 23:02:57 +0000 Subject: [PATCH 018/108] Bump soloader from 0.10.4 to 0.10.5 Bumps [soloader](https://github.com/facebook/soloader) from 0.10.4 to 0.10.5. - [Release notes](https://github.com/facebook/soloader/releases) - [Commits](https://github.com/facebook/soloader/commits) --- updated-dependencies: - dependency-name: com.facebook.soloader:soloader dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- vector-app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector-app/build.gradle b/vector-app/build.gradle index 68e20996ad..1096c7ee63 100644 --- a/vector-app/build.gradle +++ b/vector-app/build.gradle @@ -359,7 +359,7 @@ dependencies { debugImplementation(libs.flipper.flipperNetworkPlugin) { exclude group: 'com.facebook.fbjni', module: 'fbjni' } - debugImplementation 'com.facebook.soloader:soloader:0.10.4' + debugImplementation 'com.facebook.soloader:soloader:0.10.5' debugImplementation "com.kgurgul.flipper:flipper-realm-android:2.2.0" gplayImplementation "com.google.android.gms:play-services-location:21.0.1" From 310ea99c443ef01083241bfc542ee34c7b1674a0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 2 Dec 2022 10:50:08 +0100 Subject: [PATCH 019/108] Fix bad pills color background. For light and dark theme the color is now 61708B (iso EleWeb) --- changelog.d/7274.bugfix | 1 + library/ui-styles/src/main/res/values/palette.xml | 2 +- library/ui-styles/src/main/res/values/theme_dark.xml | 2 +- library/ui-styles/src/main/res/values/theme_light.xml | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 changelog.d/7274.bugfix diff --git a/changelog.d/7274.bugfix b/changelog.d/7274.bugfix new file mode 100644 index 0000000000..e99daceb89 --- /dev/null +++ b/changelog.d/7274.bugfix @@ -0,0 +1 @@ +Fix bad pills color background. For light and dark theme the color is now 61708B (iso EleWeb) diff --git a/library/ui-styles/src/main/res/values/palette.xml b/library/ui-styles/src/main/res/values/palette.xml index 73ac768919..999dccf167 100644 --- a/library/ui-styles/src/main/res/values/palette.xml +++ b/library/ui-styles/src/main/res/values/palette.xml @@ -44,4 +44,4 @@ #15191E #21262C - \ No newline at end of file + diff --git a/library/ui-styles/src/main/res/values/theme_dark.xml b/library/ui-styles/src/main/res/values/theme_dark.xml index d5aaa88ab8..9665b7335c 100644 --- a/library/ui-styles/src/main/res/values/theme_dark.xml +++ b/library/ui-styles/src/main/res/values/theme_dark.xml @@ -53,7 +53,7 @@ ?vctr_content_quinary ?vctr_system ?vctr_system - ?vctr_content_tertiary + ?vctr_notice_secondary @color/element_accent_dark diff --git a/library/ui-styles/src/main/res/values/theme_light.xml b/library/ui-styles/src/main/res/values/theme_light.xml index 1978db9139..c19fe8a111 100644 --- a/library/ui-styles/src/main/res/values/theme_light.xml +++ b/library/ui-styles/src/main/res/values/theme_light.xml @@ -53,7 +53,7 @@ ?vctr_content_quinary ?vctr_system ?vctr_system - ?vctr_content_tertiary + ?vctr_notice_secondary @color/element_accent_light From 6f934e2d49e77f949b0e1c0eaae3651fabaac88b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 2 Dec 2022 16:50:45 +0100 Subject: [PATCH 020/108] Extract paparazzi rule creation --- .../PaparazziExampleScreenshotTest.kt | 16 +-------- .../im/vector/app/screenshot/PaparazziRule.kt | 35 +++++++++++++++++++ 2 files changed, 36 insertions(+), 15 deletions(-) create mode 100644 vector/src/test/java/im/vector/app/screenshot/PaparazziRule.kt diff --git a/vector/src/test/java/im/vector/app/screenshot/PaparazziExampleScreenshotTest.kt b/vector/src/test/java/im/vector/app/screenshot/PaparazziExampleScreenshotTest.kt index 65f89dcc6a..58658651cf 100644 --- a/vector/src/test/java/im/vector/app/screenshot/PaparazziExampleScreenshotTest.kt +++ b/vector/src/test/java/im/vector/app/screenshot/PaparazziExampleScreenshotTest.kt @@ -16,14 +16,9 @@ package im.vector.app.screenshot -import android.os.Build import android.widget.ImageView import android.widget.TextView import androidx.constraintlayout.widget.ConstraintLayout -import app.cash.paparazzi.DeviceConfig.Companion.PIXEL_3 -import app.cash.paparazzi.Paparazzi -import app.cash.paparazzi.androidHome -import app.cash.paparazzi.detectEnvironment import im.vector.app.R import org.junit.Rule import org.junit.Test @@ -31,16 +26,7 @@ import org.junit.Test class PaparazziExampleScreenshotTest { @get:Rule - val paparazzi = Paparazzi( - // Apply trick from https://github.com/cashapp/paparazzi/issues/489#issuecomment-1195674603 - environment = detectEnvironment().copy( - platformDir = "${androidHome()}/platforms/android-32", - compileSdkVersion = Build.VERSION_CODES.S_V2 /* 32 */ - ), - deviceConfig = PIXEL_3, - theme = "Theme.Vector.Light", - maxPercentDifference = 0.0, - ) + val paparazzi = createPaparazziRule() @Test fun `example paparazzi test`() { diff --git a/vector/src/test/java/im/vector/app/screenshot/PaparazziRule.kt b/vector/src/test/java/im/vector/app/screenshot/PaparazziRule.kt new file mode 100644 index 0000000000..970bb15a25 --- /dev/null +++ b/vector/src/test/java/im/vector/app/screenshot/PaparazziRule.kt @@ -0,0 +1,35 @@ +/* + * 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.screenshot + +import android.os.Build +import app.cash.paparazzi.DeviceConfig.Companion.PIXEL_3 +import app.cash.paparazzi.Paparazzi +import app.cash.paparazzi.androidHome +import app.cash.paparazzi.detectEnvironment + +fun createPaparazziRule() = Paparazzi( + // Apply trick from https://github.com/cashapp/paparazzi/issues/489#issuecomment-1195674603 + environment = detectEnvironment().copy( + platformDir = "${androidHome()}/platforms/android-32", + compileSdkVersion = Build.VERSION_CODES.S_V2 /* 32 */ + ), + deviceConfig = PIXEL_3, + theme = "Theme.Vector.Light", + maxPercentDifference = 0.0, +) + From e857407bc1dd444bbe77fa7ff7e6399171a58cd3 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Fri, 2 Dec 2022 16:50:46 +0100 Subject: [PATCH 021/108] Adding changelog entry --- changelog.d/7693.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7693.feature diff --git a/changelog.d/7693.feature b/changelog.d/7693.feature new file mode 100644 index 0000000000..271964db82 --- /dev/null +++ b/changelog.d/7693.feature @@ -0,0 +1 @@ +[Session manager] Add action to signout all the other session From ab43f4cf14fdfb0781d9afb3d3651b93605208d3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 2 Dec 2022 17:02:45 +0100 Subject: [PATCH 022/108] Add snapshot test for room item --- .../app/screenshot/RoomItemScreenshotTest.kt | 66 +++++++++++++++++++ ..._RoomItemScreenshotTest_item room test.png | 3 + ..._item room two line and highlight test.png | 3 + 3 files changed, 72 insertions(+) create mode 100644 vector/src/test/java/im/vector/app/screenshot/RoomItemScreenshotTest.kt create mode 100644 vector/src/test/snapshots/images/im.vector.app.screenshot_RoomItemScreenshotTest_item room test.png create mode 100644 vector/src/test/snapshots/images/im.vector.app.screenshot_RoomItemScreenshotTest_item room two line and highlight test.png diff --git a/vector/src/test/java/im/vector/app/screenshot/RoomItemScreenshotTest.kt b/vector/src/test/java/im/vector/app/screenshot/RoomItemScreenshotTest.kt new file mode 100644 index 0000000000..d1f4034f43 --- /dev/null +++ b/vector/src/test/java/im/vector/app/screenshot/RoomItemScreenshotTest.kt @@ -0,0 +1,66 @@ +/* + * 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.screenshot + +import android.view.View +import android.widget.TextView +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.view.isVisible +import im.vector.app.R +import im.vector.app.features.home.room.list.UnreadCounterBadgeView +import org.junit.Rule +import org.junit.Test + +class RoomItemScreenshotTest { + + @get:Rule + val paparazzi = createPaparazziRule() + + @Test + fun `item room test`() { + val view = paparazzi.inflate(R.layout.item_room) + + view.findViewById(R.id.roomUnreadIndicator).isVisible = true + view.findViewById(R.id.roomNameView).text = "Room name" + view.findViewById(R.id.roomLastEventTimeView).text = "12:34" + view.findViewById(R.id.subtitleView).text = "Latest message" + view.findViewById(R.id.roomDraftBadge).isVisible = true + view.findViewById(R.id.roomUnreadCounterBadgeView).let { + it.isVisible = true + it.render(UnreadCounterBadgeView.State.Count(8, false)) + } + + paparazzi.snapshot(view) + } + + @Test + fun `item room two line and highlight test`() { + val view = paparazzi.inflate(R.layout.item_room) + + view.findViewById(R.id.roomUnreadIndicator).isVisible = true + view.findViewById(R.id.roomNameView).text = "Room name" + view.findViewById(R.id.roomLastEventTimeView).text = "23:59" + view.findViewById(R.id.subtitleView).text = "Latest message\nOn two lines" + view.findViewById(R.id.roomDraftBadge).isVisible = true + view.findViewById(R.id.roomUnreadCounterBadgeView).let { + it.isVisible = true + it.render(UnreadCounterBadgeView.State.Count(88, true)) + } + + paparazzi.snapshot(view) + } +} diff --git a/vector/src/test/snapshots/images/im.vector.app.screenshot_RoomItemScreenshotTest_item room test.png b/vector/src/test/snapshots/images/im.vector.app.screenshot_RoomItemScreenshotTest_item room test.png new file mode 100644 index 0000000000..1e87449b3c --- /dev/null +++ b/vector/src/test/snapshots/images/im.vector.app.screenshot_RoomItemScreenshotTest_item room test.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d33e82c6647bab9dcb3745d8c5a5448d60049279c365b9f64816eb9c958360d2 +size 15015 diff --git a/vector/src/test/snapshots/images/im.vector.app.screenshot_RoomItemScreenshotTest_item room two line and highlight test.png b/vector/src/test/snapshots/images/im.vector.app.screenshot_RoomItemScreenshotTest_item room two line and highlight test.png new file mode 100644 index 0000000000..83fcb8d000 --- /dev/null +++ b/vector/src/test/snapshots/images/im.vector.app.screenshot_RoomItemScreenshotTest_item room two line and highlight test.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:91a106e2a3f7310ac05425a2413ccec0aaa07720609d77a2ecd9a9d0d602b296 +size 17232 From 62e2f06e2a4f73bf6de19062f979481979cdc95b Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Fri, 2 Dec 2022 17:08:29 +0100 Subject: [PATCH 023/108] Adding menu for current session header --- library/ui-strings/src/main/res/values/strings.xml | 1 + .../devices/v2/VectorSettingsDevicesFragment.kt | 11 +++++++---- .../main/res/layout/fragment_settings_devices.xml | 1 + .../main/res/menu/menu_current_session_header.xml | 12 ++++++++++++ 4 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 vector/src/main/res/menu/menu_current_session_header.xml diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml index 58fc62b347..684bc6f7b2 100644 --- a/library/ui-strings/src/main/res/values/strings.xml +++ b/library/ui-strings/src/main/res/values/strings.xml @@ -3359,6 +3359,7 @@ Sign out of %1$d session Sign out of %1$d sessions + Sign out of all other sessions Show IP address Hide IP address Sign out of this session diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt index b27d8a7270..c371581395 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt @@ -247,7 +247,7 @@ class VectorSettingsDevicesFragment : val otherDevices = devices?.filter { it.deviceInfo.deviceId != currentDeviceId } renderSecurityRecommendations(state.inactiveSessionsCount, state.unverifiedSessionsCount, isCurrentSessionVerified) - renderCurrentDevice(currentDeviceInfo) + renderCurrentSessionView(currentDeviceInfo) renderOtherSessionsView(otherDevices, state.isShowingIpAddress) } else { hideSecurityRecommendations() @@ -310,11 +310,11 @@ class VectorSettingsDevicesFragment : hideOtherSessionsView() } else { views.deviceListHeaderOtherSessions.isVisible = true - val color = colorProvider.getColorFromAttribute(R.attr.colorError) + val colorDestructive = colorProvider.getColorFromAttribute(R.attr.colorError) val multiSignoutItem = views.deviceListHeaderOtherSessions.menu.findItem(R.id.otherSessionsHeaderMultiSignout) val nbDevices = otherDevices.size multiSignoutItem.title = stringProvider.getQuantityString(R.plurals.device_manager_other_sessions_multi_signout_all, nbDevices, nbDevices) - multiSignoutItem.setTextColor(color) + multiSignoutItem.setTextColor(colorDestructive) views.deviceListOtherSessions.isVisible = true val devices = if (isShowingIpAddress) otherDevices else otherDevices.map { it.copy(deviceInfo = it.deviceInfo.copy(lastSeenIp = null)) } views.deviceListOtherSessions.render( @@ -335,9 +335,12 @@ class VectorSettingsDevicesFragment : views.deviceListOtherSessions.isVisible = false } - private fun renderCurrentDevice(currentDeviceInfo: DeviceFullInfo?) { + private fun renderCurrentSessionView(currentDeviceInfo: DeviceFullInfo?) { currentDeviceInfo?.let { views.deviceListHeaderCurrentSession.isVisible = true + val colorDestructive = colorProvider.getColorFromAttribute(R.attr.colorError) + val signoutOtherSessionsItem = views.deviceListHeaderCurrentSession.menu.findItem(R.id.currentSessionHeaderSignoutOtherSessions) + signoutOtherSessionsItem.setTextColor(colorDestructive) views.deviceListCurrentSession.isVisible = true val viewState = SessionInfoViewState( isCurrentSession = true, diff --git a/vector/src/main/res/layout/fragment_settings_devices.xml b/vector/src/main/res/layout/fragment_settings_devices.xml index 8134774887..731049f3a2 100644 --- a/vector/src/main/res/layout/fragment_settings_devices.xml +++ b/vector/src/main/res/layout/fragment_settings_devices.xml @@ -67,6 +67,7 @@ app:layout_constraintTop_toBottomOf="@id/deviceListSecurityRecommendationsDivider" app:sessionsListHeaderDescription="" app:sessionsListHeaderHasLearnMoreLink="false" + app:sessionsListHeaderMenu="@menu/menu_current_session_header" app:sessionsListHeaderTitle="@string/device_manager_current_session_title" /> + + + + + From 2b8dc13dcad821517cdee725895762fd39c59676 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Fri, 2 Dec 2022 17:11:10 +0100 Subject: [PATCH 024/108] Adding listener on the new menu item --- .../devices/v2/VectorSettingsDevicesFragment.kt | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt index c371581395..a208362680 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt @@ -99,6 +99,7 @@ class VectorSettingsDevicesFragment : super.onViewCreated(view, savedInstanceState) initWaitingView() + initCurrentSessionHeaderView() initOtherSessionsHeaderView() initOtherSessionsView() initSecurityRecommendationsView() @@ -139,6 +140,18 @@ class VectorSettingsDevicesFragment : views.waitingView.waitingStatusText.isVisible = true } + private fun initCurrentSessionHeaderView() { + views.deviceListHeaderCurrentSession.setOnMenuItemClickListener { menuItem -> + when (menuItem.itemId) { + R.id.currentSessionHeaderSignoutOtherSessions -> { + confirmMultiSignoutOtherSessions() + true + } + else -> false + } + } + } + private fun initOtherSessionsHeaderView() { views.deviceListHeaderOtherSessions.setOnMenuItemClickListener { menuItem -> when (menuItem.itemId) { @@ -327,7 +340,7 @@ class VectorSettingsDevicesFragment : } else { stringProvider.getString(R.string.device_manager_other_sessions_show_ip_address) } - } + } } private fun hideOtherSessionsView() { From efc436c3f5b878b115f75b88b3d3caec7896b70c Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Fri, 2 Dec 2022 17:17:44 +0100 Subject: [PATCH 025/108] Hide the action when there are no other sessions --- .../settings/devices/v2/VectorSettingsDevicesFragment.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt index a208362680..d748600416 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt @@ -53,6 +53,7 @@ import im.vector.app.features.settings.devices.v2.list.SecurityRecommendationVie import im.vector.app.features.settings.devices.v2.list.SessionInfoViewState import im.vector.app.features.settings.devices.v2.signout.BuildConfirmSignoutDialogUseCase import org.matrix.android.sdk.api.auth.data.LoginFlowTypes +import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel import javax.inject.Inject @@ -260,7 +261,7 @@ class VectorSettingsDevicesFragment : val otherDevices = devices?.filter { it.deviceInfo.deviceId != currentDeviceId } renderSecurityRecommendations(state.inactiveSessionsCount, state.unverifiedSessionsCount, isCurrentSessionVerified) - renderCurrentSessionView(currentDeviceInfo) + renderCurrentSessionView(currentDeviceInfo, hasOtherDevices = otherDevices?.isNotEmpty().orFalse()) renderOtherSessionsView(otherDevices, state.isShowingIpAddress) } else { hideSecurityRecommendations() @@ -348,12 +349,13 @@ class VectorSettingsDevicesFragment : views.deviceListOtherSessions.isVisible = false } - private fun renderCurrentSessionView(currentDeviceInfo: DeviceFullInfo?) { + private fun renderCurrentSessionView(currentDeviceInfo: DeviceFullInfo?, hasOtherDevices: Boolean) { currentDeviceInfo?.let { views.deviceListHeaderCurrentSession.isVisible = true val colorDestructive = colorProvider.getColorFromAttribute(R.attr.colorError) val signoutOtherSessionsItem = views.deviceListHeaderCurrentSession.menu.findItem(R.id.currentSessionHeaderSignoutOtherSessions) signoutOtherSessionsItem.setTextColor(colorDestructive) + signoutOtherSessionsItem.isVisible = hasOtherDevices views.deviceListCurrentSession.isVisible = true val viewState = SessionInfoViewState( isCurrentSession = true, From b8ab1b5620e071974d3600479b915e91ba72780c Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Fri, 2 Dec 2022 17:35:13 +0100 Subject: [PATCH 026/108] Adding changelog entry --- changelog.d/7697.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7697.feature diff --git a/changelog.d/7697.feature b/changelog.d/7697.feature new file mode 100644 index 0000000000..6d71a84a40 --- /dev/null +++ b/changelog.d/7697.feature @@ -0,0 +1 @@ +[Session manager] Add actions to rename and signout current session From 53ef97d9492c3aa674246918bdd8072f9be69b02 Mon Sep 17 00:00:00 2001 From: phardyle Date: Fri, 2 Dec 2022 10:05:03 +0000 Subject: [PATCH 027/108] Translated using Weblate (Chinese (Simplified)) Currently translated at 99.6% (2550 of 2558 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/zh_Hans/ --- .../src/main/res/values-zh-rCN/strings.xml | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/library/ui-strings/src/main/res/values-zh-rCN/strings.xml b/library/ui-strings/src/main/res/values-zh-rCN/strings.xml index 5ab8a351d1..0a01610c36 100644 --- a/library/ui-strings/src/main/res/values-zh-rCN/strings.xml +++ b/library/ui-strings/src/main/res/values-zh-rCN/strings.xml @@ -1007,7 +1007,7 @@ 您当前在身份服务器 %1$s 上共享电子邮件地址或电话号码。您需要重新连接到 %2$s 才能停止共享它们。 同意身份服务器 (%s) 服务条款使你可以通过电子邮件地址或电话号码被发现。 启用详细日志。 - 详细日志将通过在您发送 RageShake 时提供更多日志来帮助开发人员。即使启用,应用程序也不会记录消息内容或任何其他私人数据。 + 详细日志将通过在您发送愤怒摇动(RageShake)时提供更多日志来帮助开发人员。即使启用,应用程序也不会记录消息内容或任何其他私人数据。 接收你的主服务器条款和条件后请重试。 服务器似乎响应时间太长,这可能是由于连接不良或服务器错误引起的。请稍后再试。 发送附件 @@ -1205,7 +1205,7 @@ 高级设置 开发者模式 开发者模式激活隐藏的功能,也可能使应用不稳定。仅供开发者使用! - 摇一摇 + 愤怒摇动(Rageshake) 检测阈值 摇动手机以测试检测阈值 检测到摇动! @@ -1213,7 +1213,7 @@ 当前会话 其它会话 仅显示第一个结果,请输入更多字符… - 快速失败 + 快速失败(Fail-fast) 发生意外错误时,${app_name} 可能更经常崩溃 在明文消息前添加 ¯\\_(ツ)_/¯ 启用加密 @@ -2694,7 +2694,7 @@ 验证您当前的会话以显示此会话的验证状态。 未知的验证状态 开始语音广播 - 缓冲 + 正在缓冲…… 暂停语音广播 实时 知道了 @@ -2789,4 +2789,19 @@ 已选择 %1$d + 已创建投票。 + 已发送贴纸。 + 已发送视频。 + 已发送图片。 + 已发送语音消息。 + 已发送音频文件。 + 已发送文件。 + 已验证的会话是在输入你的口令词组或用另一个已验证的会话确认你的身份之后你使用此账户的任何地方。 +\n +\n这意味着你拥有解锁你的已加密消息和向其他用户证明你信任此会话所需的全部密钥。 + + 登出%1$d个会话 + + 登出 + 剩余%1$s \ No newline at end of file From 6d1f9408c87d2b05de052307f2994300297d996c Mon Sep 17 00:00:00 2001 From: Vri Date: Fri, 2 Dec 2022 05:54:40 +0000 Subject: [PATCH 028/108] Translated using Weblate (German) Currently translated at 100.0% (83 of 83 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/de/ --- fastlane/metadata/android/de-DE/changelogs/40105100.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/de-DE/changelogs/40105100.txt diff --git a/fastlane/metadata/android/de-DE/changelogs/40105100.txt b/fastlane/metadata/android/de-DE/changelogs/40105100.txt new file mode 100644 index 0000000000..de5f4d90e8 --- /dev/null +++ b/fastlane/metadata/android/de-DE/changelogs/40105100.txt @@ -0,0 +1,2 @@ +Die wichtigsten Änderungen in dieser Version: Der Vollbildmodus des Textverarbeitungseditors wurde neu umgesetzt und es wurden diverse Fehler behoben. +Vollständiges Änderungsprotokoll: https://github.com/vector-im/element-android/releases From 098dee1aa7a55d47107ef91cdcb4e2664eac0ab9 Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Thu, 1 Dec 2022 21:32:42 +0000 Subject: [PATCH 029/108] Translated using Weblate (Slovak) Currently translated at 100.0% (83 of 83 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/sk/ --- fastlane/metadata/android/sk/changelogs/40105100.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/sk/changelogs/40105100.txt diff --git a/fastlane/metadata/android/sk/changelogs/40105100.txt b/fastlane/metadata/android/sk/changelogs/40105100.txt new file mode 100644 index 0000000000..c286f155d4 --- /dev/null +++ b/fastlane/metadata/android/sk/changelogs/40105100.txt @@ -0,0 +1,2 @@ +Hlavné zmeny v tejto verzii: Nová implementácia celo-obrazovkového režimu pre Rozšírený textový editor a opravy chýb. +Úplný zoznam zmien: https://github.com/vector-im/element-android/releases From 873fa2a210e299bc02fe0a4bf0b34d3662424df6 Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Thu, 1 Dec 2022 09:57:42 +0000 Subject: [PATCH 030/108] Translated using Weblate (Ukrainian) Currently translated at 100.0% (83 of 83 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/uk/ --- fastlane/metadata/android/uk/changelogs/40105100.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/uk/changelogs/40105100.txt diff --git a/fastlane/metadata/android/uk/changelogs/40105100.txt b/fastlane/metadata/android/uk/changelogs/40105100.txt new file mode 100644 index 0000000000..6bb3ab95c7 --- /dev/null +++ b/fastlane/metadata/android/uk/changelogs/40105100.txt @@ -0,0 +1,2 @@ +Основні зміни в цій версії: Нова реалізація повноекранного режиму для редактора розширеного тексту та виправлення помилок. +Перелік усіх змін: https://github.com/vector-im/element-android/releases From 10d03e16afb7e291bad83bf0fd0cf98de23ea7d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Thu, 1 Dec 2022 16:57:40 +0000 Subject: [PATCH 031/108] Translated using Weblate (Estonian) Currently translated at 100.0% (83 of 83 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/et/ --- fastlane/metadata/android/et/changelogs/40105100.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/et/changelogs/40105100.txt diff --git a/fastlane/metadata/android/et/changelogs/40105100.txt b/fastlane/metadata/android/et/changelogs/40105100.txt new file mode 100644 index 0000000000..f6212db01b --- /dev/null +++ b/fastlane/metadata/android/et/changelogs/40105100.txt @@ -0,0 +1,2 @@ +Põhilised muutused selles versioonis: tekstitoimeti täisekraanivaade ja erinevate vigade parandused. +Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases From 61eb0d6b449ae62e7b50708c745d3b627274ac9b Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Fri, 2 Dec 2022 01:49:09 +0000 Subject: [PATCH 032/108] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (83 of 83 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/zh_Hant/ --- fastlane/metadata/android/zh-TW/changelogs/40105100.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/zh-TW/changelogs/40105100.txt diff --git a/fastlane/metadata/android/zh-TW/changelogs/40105100.txt b/fastlane/metadata/android/zh-TW/changelogs/40105100.txt new file mode 100644 index 0000000000..20341b84fe --- /dev/null +++ b/fastlane/metadata/android/zh-TW/changelogs/40105100.txt @@ -0,0 +1,2 @@ +此版本中的主要變動:格式化文字編輯器的全螢幕模式新實作與臭蟲修復。 +完整的變更紀錄:https://github.com/vector-im/element-android/releases From 58d10e901e689a53803ad6cb3fe0c9f796d68fc1 Mon Sep 17 00:00:00 2001 From: waclaw66 Date: Thu, 1 Dec 2022 11:18:34 +0000 Subject: [PATCH 033/108] Translated using Weblate (Czech) Currently translated at 100.0% (83 of 83 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/cs/ --- fastlane/metadata/android/cs-CZ/changelogs/40105100.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/cs-CZ/changelogs/40105100.txt diff --git a/fastlane/metadata/android/cs-CZ/changelogs/40105100.txt b/fastlane/metadata/android/cs-CZ/changelogs/40105100.txt new file mode 100644 index 0000000000..8c51742e06 --- /dev/null +++ b/fastlane/metadata/android/cs-CZ/changelogs/40105100.txt @@ -0,0 +1,2 @@ +Hlavní změny v této verzi: Nová implementace celoobrazovkového režimu pro editor formátovaného textu a opravy chyb. +Úplný seznam změn: https://github.com/vector-im/element-android/releases From 70e9f13ec23f4c17a65d1c1bf85689e8ddd59ac1 Mon Sep 17 00:00:00 2001 From: Linerly Date: Thu, 1 Dec 2022 12:00:15 +0000 Subject: [PATCH 034/108] Translated using Weblate (Indonesian) Currently translated at 100.0% (83 of 83 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/id/ --- fastlane/metadata/android/id/changelogs/40105100.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/id/changelogs/40105100.txt diff --git a/fastlane/metadata/android/id/changelogs/40105100.txt b/fastlane/metadata/android/id/changelogs/40105100.txt new file mode 100644 index 0000000000..0c7d2f5262 --- /dev/null +++ b/fastlane/metadata/android/id/changelogs/40105100.txt @@ -0,0 +1,2 @@ +Perubahan utama dalam versi ini: Penerapan baru mode layar penuh untuk Penyunting Teks Kaya dan perbaikan kutu. +Catatan perubahan lanjutan: https://github.com/vector-im/element-android/releases From 9456789047f96d0fd2aa2313a2e2ff97a96f91da Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 28 Nov 2022 11:38:06 +0100 Subject: [PATCH 035/108] Adding changelog entry --- changelog.d/7653.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7653.bugfix diff --git a/changelog.d/7653.bugfix b/changelog.d/7653.bugfix new file mode 100644 index 0000000000..ae49c4ed4e --- /dev/null +++ b/changelog.d/7653.bugfix @@ -0,0 +1 @@ +ANR when asking to select the notification method From 4dbca7858cbb49d95135a34e2fc4f84330d75460 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Tue, 29 Nov 2022 15:07:26 +0100 Subject: [PATCH 036/108] Adding new use cases to handle the Unified push registration --- .../im/vector/app/push/fcm/FdroidFcmHelper.kt | 3 +- .../im/vector/app/push/fcm/GoogleFcmHelper.kt | 13 ++-- .../EnsureFcmTokenIsRetrievedUseCase.kt | 44 ++++++++++++ .../im/vector/app/core/pushers/FcmHelper.kt | 3 +- .../pushers/RegisterUnifiedPushUseCase.kt | 70 +++++++++++++++++++ .../app/core/pushers/UnifiedPushHelper.kt | 60 ++++++++++++++-- .../pushers/UnregisterUnifiedPushUseCase.kt | 51 ++++++++++++++ .../vector/app/features/home/HomeActivity.kt | 29 ++------ .../features/home/HomeActivityViewActions.kt | 1 + .../features/home/HomeActivityViewEvents.kt | 3 + .../features/home/HomeActivityViewModel.kt | 53 +++++++------- .../features/home/HomeActivityViewState.kt | 1 - ...leNotificationsForCurrentSessionUseCase.kt | 12 ++-- ...rSettingsNotificationPreferenceFragment.kt | 10 +-- 14 files changed, 271 insertions(+), 82 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/core/pushers/EnsureFcmTokenIsRetrievedUseCase.kt create mode 100644 vector/src/main/java/im/vector/app/core/pushers/RegisterUnifiedPushUseCase.kt create mode 100644 vector/src/main/java/im/vector/app/core/pushers/UnregisterUnifiedPushUseCase.kt diff --git a/vector-app/src/fdroid/java/im/vector/app/push/fcm/FdroidFcmHelper.kt b/vector-app/src/fdroid/java/im/vector/app/push/fcm/FdroidFcmHelper.kt index 5b83769116..44fd92953e 100755 --- a/vector-app/src/fdroid/java/im/vector/app/push/fcm/FdroidFcmHelper.kt +++ b/vector-app/src/fdroid/java/im/vector/app/push/fcm/FdroidFcmHelper.kt @@ -17,7 +17,6 @@ package im.vector.app.push.fcm -import android.app.Activity import android.content.Context import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.pushers.FcmHelper @@ -44,7 +43,7 @@ class FdroidFcmHelper @Inject constructor( // No op } - override fun ensureFcmTokenIsRetrieved(activity: Activity, pushersManager: PushersManager, registerPusher: Boolean) { + override fun ensureFcmTokenIsRetrieved(pushersManager: PushersManager, registerPusher: Boolean) { // No op } diff --git a/vector-app/src/gplay/java/im/vector/app/push/fcm/GoogleFcmHelper.kt b/vector-app/src/gplay/java/im/vector/app/push/fcm/GoogleFcmHelper.kt index 7cf90cf874..53e65f88b4 100755 --- a/vector-app/src/gplay/java/im/vector/app/push/fcm/GoogleFcmHelper.kt +++ b/vector-app/src/gplay/java/im/vector/app/push/fcm/GoogleFcmHelper.kt @@ -15,7 +15,6 @@ */ package im.vector.app.push.fcm -import android.app.Activity import android.content.Context import android.content.SharedPreferences import android.widget.Toast @@ -23,6 +22,7 @@ import androidx.core.content.edit import com.google.android.gms.common.ConnectionResult import com.google.android.gms.common.GoogleApiAvailability import com.google.firebase.messaging.FirebaseMessaging +import dagger.hilt.android.qualifiers.ApplicationContext import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.DefaultPreferences @@ -36,8 +36,8 @@ import javax.inject.Inject * It has an alter ego in the fdroid variant. */ class GoogleFcmHelper @Inject constructor( - @DefaultPreferences - private val sharedPrefs: SharedPreferences, + @ApplicationContext private val context: Context, + @DefaultPreferences private val sharedPrefs: SharedPreferences, ) : FcmHelper { companion object { private const val PREFS_KEY_FCM_TOKEN = "FCM_TOKEN" @@ -56,10 +56,9 @@ class GoogleFcmHelper @Inject constructor( } } - override fun ensureFcmTokenIsRetrieved(activity: Activity, pushersManager: PushersManager, registerPusher: Boolean) { - // if (TextUtils.isEmpty(getFcmToken(activity))) { + override fun ensureFcmTokenIsRetrieved(pushersManager: PushersManager, registerPusher: Boolean) { // 'app should always check the device for a compatible Google Play services APK before accessing Google Play services features' - if (checkPlayServices(activity)) { + if (checkPlayServices(context)) { try { FirebaseMessaging.getInstance().token .addOnSuccessListener { token -> @@ -75,7 +74,7 @@ class GoogleFcmHelper @Inject constructor( Timber.e(e, "## ensureFcmTokenIsRetrieved() : failed") } } else { - Toast.makeText(activity, R.string.no_valid_google_play_services_apk, Toast.LENGTH_SHORT).show() + Toast.makeText(context, R.string.no_valid_google_play_services_apk, Toast.LENGTH_SHORT).show() Timber.e("No valid Google Play Services found. Cannot use FCM.") } } diff --git a/vector/src/main/java/im/vector/app/core/pushers/EnsureFcmTokenIsRetrievedUseCase.kt b/vector/src/main/java/im/vector/app/core/pushers/EnsureFcmTokenIsRetrievedUseCase.kt new file mode 100644 index 0000000000..4a8ff5fb39 --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/pushers/EnsureFcmTokenIsRetrievedUseCase.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.core.pushers + +import im.vector.app.core.di.ActiveSessionHolder +import timber.log.Timber +import javax.inject.Inject + +class EnsureFcmTokenIsRetrievedUseCase @Inject constructor( + private val unifiedPushHelper: UnifiedPushHelper, + private val fcmHelper: FcmHelper, + private val activeSessionHolder: ActiveSessionHolder, +) { + + // TODO add unit tests + fun execute(pushersManager: PushersManager, registerPusher: Boolean) { + if (unifiedPushHelper.isEmbeddedDistributor()) { + Timber.d("ensureFcmTokenIsRetrieved") + fcmHelper.ensureFcmTokenIsRetrieved(pushersManager, shouldAddHttpPusher(registerPusher)) + } + } + + private fun shouldAddHttpPusher(registerPusher: Boolean) = if (registerPusher) { + val currentSession = activeSessionHolder.getActiveSession() + val currentPushers = currentSession.pushersService().getPushers() + currentPushers.none { it.deviceId == currentSession.sessionParams.deviceId } + } else { + false + } +} diff --git a/vector/src/main/java/im/vector/app/core/pushers/FcmHelper.kt b/vector/src/main/java/im/vector/app/core/pushers/FcmHelper.kt index 7b2c5e3959..0cc251ce31 100644 --- a/vector/src/main/java/im/vector/app/core/pushers/FcmHelper.kt +++ b/vector/src/main/java/im/vector/app/core/pushers/FcmHelper.kt @@ -39,11 +39,10 @@ interface FcmHelper { /** * onNewToken may not be called on application upgrade, so ensure my shared pref is set. * - * @param activity the first launch Activity. * @param pushersManager the instance to register the pusher on. * @param registerPusher whether the pusher should be registered. */ - fun ensureFcmTokenIsRetrieved(activity: Activity, pushersManager: PushersManager, registerPusher: Boolean) + fun ensureFcmTokenIsRetrieved(pushersManager: PushersManager, registerPusher: Boolean) fun onEnterForeground(activeSessionHolder: ActiveSessionHolder) diff --git a/vector/src/main/java/im/vector/app/core/pushers/RegisterUnifiedPushUseCase.kt b/vector/src/main/java/im/vector/app/core/pushers/RegisterUnifiedPushUseCase.kt new file mode 100644 index 0000000000..7aafa07348 --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/pushers/RegisterUnifiedPushUseCase.kt @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.core.pushers + +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import im.vector.app.features.VectorFeatures +import org.unifiedpush.android.connector.UnifiedPush +import javax.inject.Inject + +class RegisterUnifiedPushUseCase @Inject constructor( + @ApplicationContext private val context: Context, + private val vectorFeatures: VectorFeatures, +) { + + sealed interface RegisterUnifiedPushResult { + object Success : RegisterUnifiedPushResult + data class NeedToAskUserForDistributor(val distributors: List) : RegisterUnifiedPushResult + } + + // TODO add unit tests + fun execute(distributor: String = ""): RegisterUnifiedPushResult { + if(distributor.isNotEmpty()) { + saveAndRegisterApp(distributor) + return RegisterUnifiedPushResult.Success + } + + if (!vectorFeatures.allowExternalUnifiedPushDistributors()) { + saveAndRegisterApp(context.packageName) + return RegisterUnifiedPushResult.Success + } + + if (UnifiedPush.getDistributor(context).isNotEmpty()) { + registerApp() + return RegisterUnifiedPushResult.Success + } + + val distributors = UnifiedPush.getDistributors(context) + + return if (distributors.size == 1) { + saveAndRegisterApp(distributors.first()) + RegisterUnifiedPushResult.Success + } else { + RegisterUnifiedPushResult.NeedToAskUserForDistributor(distributors) + } + } + + private fun saveAndRegisterApp(distributor: String) { + UnifiedPush.saveDistributor(context, distributor) + registerApp() + } + + private fun registerApp() { + UnifiedPush.registerApp(context) + } +} diff --git a/vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt b/vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt index aab94eca93..64d2a79494 100644 --- a/vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt +++ b/vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt @@ -17,6 +17,7 @@ package im.vector.app.core.pushers import android.content.Context +import androidx.annotation.MainThread import androidx.fragment.app.FragmentActivity import androidx.lifecycle.lifecycleScope import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -28,7 +29,9 @@ import im.vector.app.core.utils.getApplicationLabel import im.vector.app.features.VectorFeatures import im.vector.app.features.settings.BackgroundSyncMode import im.vector.app.features.settings.VectorPreferences +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.Matrix import org.matrix.android.sdk.api.cache.CacheStrategy import org.matrix.android.sdk.api.util.MatrixJsonParser @@ -49,6 +52,7 @@ class UnifiedPushHelper @Inject constructor( // Called when the home activity starts // or when notifications are enabled + // TODO remove and replace by use case fun register( activity: FragmentActivity, onDoneRunnable: Runnable? = null, @@ -66,10 +70,11 @@ class UnifiedPushHelper @Inject constructor( // The registration is forced in 2 cases : // * in the settings // * in the troubleshoot list (doFix) + // TODO remove and replace by use case fun forceRegister( activity: FragmentActivity, pushersManager: PushersManager, - onDoneRunnable: Runnable? = null + @MainThread onDoneRunnable: Runnable? = null ) { registerInternal( activity, @@ -79,17 +84,21 @@ class UnifiedPushHelper @Inject constructor( ) } + // TODO remove private fun registerInternal( activity: FragmentActivity, force: Boolean = false, pushersManager: PushersManager? = null, onDoneRunnable: Runnable? = null ) { - activity.lifecycleScope.launch { + activity.lifecycleScope.launch(Dispatchers.IO) { + Timber.d("registerInternal force=$force, $activity on thread ${Thread.currentThread()}") if (!vectorFeatures.allowExternalUnifiedPushDistributors()) { UnifiedPush.saveDistributor(context, context.packageName) UnifiedPush.registerApp(context) - onDoneRunnable?.run() + withContext(Dispatchers.Main) { + onDoneRunnable?.run() + } return@launch } if (force) { @@ -99,7 +108,9 @@ class UnifiedPushHelper @Inject constructor( // the !force should not be needed if (!force && UnifiedPush.getDistributor(context).isNotEmpty()) { UnifiedPush.registerApp(context) - onDoneRunnable?.run() + withContext(Dispatchers.Main) { + onDoneRunnable?.run() + } return@launch } @@ -108,7 +119,9 @@ class UnifiedPushHelper @Inject constructor( if (!force && distributors.size == 1) { UnifiedPush.saveDistributor(context, distributors.first()) UnifiedPush.registerApp(context) - onDoneRunnable?.run() + withContext(Dispatchers.Main) { + onDoneRunnable?.run() + } } else { openDistributorDialogInternal( activity = activity, @@ -164,6 +177,43 @@ class UnifiedPushHelper @Inject constructor( .show() } + @MainThread + fun showSelectDistributorDialog( + context: Context, + distributors: List, + onDistributorSelected: (String) -> Unit, + ) { + val internalDistributorName = stringProvider.getString( + if (fcmHelper.isFirebaseAvailable()) { + R.string.unifiedpush_distributor_fcm_fallback + } else { + R.string.unifiedpush_distributor_background_sync + } + ) + + val distributorsName = distributors.map { + if (it == context.packageName) { + internalDistributorName + } else { + context.getApplicationLabel(it) + } + } + + MaterialAlertDialogBuilder(context) + .setTitle(stringProvider.getString(R.string.unifiedpush_getdistributors_dialog_title)) + .setItems(distributorsName.toTypedArray()) { _, which -> + val distributor = distributors[which] + onDistributorSelected(distributor) + } + .setOnCancelListener { + // By default, use internal solution (fcm/background sync) + onDistributorSelected(context.packageName) + } + .setCancelable(true) + .show() + } + + // TODO remove and replace by use case suspend fun unregister(pushersManager: PushersManager? = null) { val mode = BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_REALTIME vectorPreferences.setFdroidSyncBackgroundMode(mode) diff --git a/vector/src/main/java/im/vector/app/core/pushers/UnregisterUnifiedPushUseCase.kt b/vector/src/main/java/im/vector/app/core/pushers/UnregisterUnifiedPushUseCase.kt new file mode 100644 index 0000000000..d81581679e --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/pushers/UnregisterUnifiedPushUseCase.kt @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.core.pushers + +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import im.vector.app.features.settings.BackgroundSyncMode +import im.vector.app.features.settings.VectorPreferences +import org.unifiedpush.android.connector.UnifiedPush +import timber.log.Timber +import javax.inject.Inject + +class UnregisterUnifiedPushUseCase @Inject constructor( + @ApplicationContext private val context: Context, + private val pushersManager: PushersManager, + private val vectorPreferences: VectorPreferences, + private val unifiedPushStore: UnifiedPushStore, + private val unifiedPushHelper: UnifiedPushHelper, +) { + + // TODO add unit tests + suspend fun execute() { + val mode = BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_REALTIME + vectorPreferences.setFdroidSyncBackgroundMode(mode) + try { + unifiedPushHelper.getEndpointOrToken()?.let { + Timber.d("Removing $it") + pushersManager.unregisterPusher(it) + } + } catch (e: Exception) { + Timber.d(e, "Probably unregistering a non existing pusher") + } + unifiedPushStore.storeUpEndpoint(null) + unifiedPushStore.storePushGateway(null) + UnifiedPush.unregisterApp(context) + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt index 2df94fecad..14157a1de8 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt @@ -44,8 +44,6 @@ import im.vector.app.core.extensions.restart import im.vector.app.core.extensions.validateBackPressed import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorMenuProvider -import im.vector.app.core.pushers.FcmHelper -import im.vector.app.core.pushers.PushersManager import im.vector.app.core.pushers.UnifiedPushHelper import im.vector.app.core.utils.registerForPermissionsResult import im.vector.app.core.utils.startSharePlainTextIntent @@ -128,7 +126,6 @@ class HomeActivity : private val serverBackupStatusViewModel: ServerBackupStatusViewModel by viewModel() @Inject lateinit var vectorUncaughtExceptionHandler: VectorUncaughtExceptionHandler - @Inject lateinit var pushersManager: PushersManager @Inject lateinit var notificationDrawerManager: NotificationDrawerManager @Inject lateinit var popupAlertManager: PopupAlertManager @Inject lateinit var shortcutsHandler: ShortcutsHandler @@ -137,7 +134,6 @@ class HomeActivity : @Inject lateinit var initSyncStepFormatter: InitSyncStepFormatter @Inject lateinit var spaceStateHandler: SpaceStateHandler @Inject lateinit var unifiedPushHelper: UnifiedPushHelper - @Inject lateinit var fcmHelper: FcmHelper @Inject lateinit var nightlyProxy: NightlyProxy @Inject lateinit var disclaimerDialog: DisclaimerDialog @Inject lateinit var notificationPermissionManager: NotificationPermissionManager @@ -209,16 +205,6 @@ class HomeActivity : isNewAppLayoutEnabled = vectorPreferences.isNewAppLayoutEnabled() analyticsScreenName = MobileScreen.ScreenName.Home supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, false) - unifiedPushHelper.register(this) { - if (unifiedPushHelper.isEmbeddedDistributor()) { - fcmHelper.ensureFcmTokenIsRetrieved( - this, - pushersManager, - homeActivityViewModel.shouldAddHttpPusher() - ) - } - } - sharedActionViewModel = viewModelProvider[HomeSharedActionViewModel::class.java] roomListSharedActionViewModel = viewModelProvider[RoomListSharedActionViewModel::class.java] views.drawerLayout.addDrawerListener(drawerListener) @@ -280,6 +266,7 @@ class HomeActivity : HomeActivityViewEvents.ShowReleaseNotes -> handleShowReleaseNotes() HomeActivityViewEvents.NotifyUserForThreadsMigration -> handleNotifyUserForThreadsMigration() is HomeActivityViewEvents.MigrateThreads -> migrateThreadsIfNeeded(it.checkSession) + is HomeActivityViewEvents.AskUserForPushDistributor -> askUserToSelectPushDistributor(it.distributors) } } homeActivityViewModel.onEach { renderState(it) } @@ -292,6 +279,12 @@ class HomeActivity : homeActivityViewModel.handle(HomeActivityViewActions.ViewStarted) } + private fun askUserToSelectPushDistributor(distributors: List) { + unifiedPushHelper.showSelectDistributorDialog(this, distributors) { selection -> + homeActivityViewModel.handle(HomeActivityViewActions.RegisterPushDistributor(selection)) + } + } + private fun handleShowNotificationDialog() { notificationPermissionManager.eventuallyRequestPermission(this, postPermissionLauncher) } @@ -415,14 +408,6 @@ class HomeActivity : } private fun renderState(state: HomeActivityViewState) { - lifecycleScope.launch { - if (state.areNotificationsSilenced) { - unifiedPushHelper.unregister(pushersManager) - } else { - unifiedPushHelper.register(this@HomeActivity) - } - } - when (val status = state.syncRequestState) { is SyncRequestState.InitialSyncProgressing -> { val initSyncStepStr = initSyncStepFormatter.format(status.initialSyncStep) diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewActions.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewActions.kt index 5f89c89bc9..54392d5f56 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewActions.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewActions.kt @@ -21,4 +21,5 @@ import im.vector.app.core.platform.VectorViewModelAction sealed interface HomeActivityViewActions : VectorViewModelAction { object ViewStarted : HomeActivityViewActions object PushPromptHasBeenReviewed : HomeActivityViewActions + data class RegisterPushDistributor(val distributor: String) : HomeActivityViewActions } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt index e548fdb2f3..6fdf441d1d 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt @@ -25,9 +25,11 @@ sealed interface HomeActivityViewEvents : VectorViewEvents { val userItem: MatrixItem.UserItem, val waitForIncomingRequest: Boolean = true, ) : HomeActivityViewEvents + data class CurrentSessionCannotBeVerified( val userItem: MatrixItem.UserItem, ) : HomeActivityViewEvents + data class OnCrossSignedInvalidated(val userItem: MatrixItem.UserItem) : HomeActivityViewEvents object PromptToEnableSessionPush : HomeActivityViewEvents object ShowAnalyticsOptIn : HomeActivityViewEvents @@ -37,4 +39,5 @@ sealed interface HomeActivityViewEvents : VectorViewEvents { data class MigrateThreads(val checkSession: Boolean) : HomeActivityViewEvents object StartRecoverySetupFlow : HomeActivityViewEvents data class ForceVerification(val sendRequest: Boolean) : HomeActivityViewEvents + data class AskUserForPushDistributor(val distributors: List) : HomeActivityViewEvents } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt index 49f2079625..3fd555bbea 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.home -import androidx.lifecycle.asFlow import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext @@ -27,7 +26,9 @@ import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.VectorViewModel -import im.vector.app.features.VectorFeatures +import im.vector.app.core.pushers.EnsureFcmTokenIsRetrievedUseCase +import im.vector.app.core.pushers.PushersManager +import im.vector.app.core.pushers.RegisterUnifiedPushUseCase import im.vector.app.features.analytics.AnalyticsConfig import im.vector.app.features.analytics.AnalyticsTracker import im.vector.app.features.analytics.extensions.toAnalyticsType @@ -48,12 +49,10 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.takeWhile import kotlinx.coroutines.launch -import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.auth.UserPasswordAuth @@ -62,11 +61,9 @@ import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.raw.RawService -import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningService import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap -import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.getUserOrDefault import org.matrix.android.sdk.api.session.pushrules.RuleIds import org.matrix.android.sdk.api.session.room.model.Membership @@ -92,8 +89,10 @@ class HomeActivityViewModel @AssistedInject constructor( private val analyticsTracker: AnalyticsTracker, private val analyticsConfig: AnalyticsConfig, private val releaseNotesPreferencesStore: ReleaseNotesPreferencesStore, - private val vectorFeatures: VectorFeatures, private val stopOngoingVoiceBroadcastUseCase: StopOngoingVoiceBroadcastUseCase, + private val pushersManager: PushersManager, + private val registerUnifiedPushUseCase: RegisterUnifiedPushUseCase, + private val ensureFcmTokenIsRetrievedUseCase: EnsureFcmTokenIsRetrievedUseCase, ) : VectorViewModel(initialState) { @AssistedFactory @@ -117,17 +116,32 @@ class HomeActivityViewModel @AssistedInject constructor( private fun initialize() { if (isInitialized) return isInitialized = true + registerUnifiedPush(distributor = "") cleanupFiles() observeInitialSync() checkSessionPushIsOn() observeCrossSigningReset() observeAnalytics() observeReleaseNotes() - observeLocalNotificationsSilenced() initThreadsMigration() viewModelScope.launch { stopOngoingVoiceBroadcastUseCase.execute() } } + private fun registerUnifiedPush(distributor: String) { + viewModelScope.launch { + when (val result = registerUnifiedPushUseCase.execute(distributor = distributor)) { + is RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.NeedToAskUserForDistributor -> { + Timber.d("registerUnifiedPush $distributor need to ask user") + _viewEvents.post(HomeActivityViewEvents.AskUserForPushDistributor(result.distributors)) + } + RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.Success -> { + Timber.d("registerUnifiedPush $distributor success") + ensureFcmTokenIsRetrievedUseCase.execute(pushersManager, registerPusher = vectorPreferences.areNotificationEnabledForDevice()) + } + } + } + } + private fun observeReleaseNotes() = withState { state -> if (vectorPreferences.isNewAppLayoutEnabled()) { // we don't want to show release notes for new users or after relogin @@ -146,26 +160,6 @@ class HomeActivityViewModel @AssistedInject constructor( } } - fun shouldAddHttpPusher() = if (vectorPreferences.areNotificationEnabledForDevice()) { - val currentSession = activeSessionHolder.getActiveSession() - val currentPushers = currentSession.pushersService().getPushers() - currentPushers.none { it.deviceId == currentSession.sessionParams.deviceId } - } else { - false - } - - fun observeLocalNotificationsSilenced() { - val currentSession = activeSessionHolder.getActiveSession() - val deviceId = currentSession.cryptoService().getMyDevice().deviceId - viewModelScope.launch { - currentSession.accountDataService() - .getLiveUserAccountDataEvent(UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId) - .asFlow() - .map { it.getOrNull()?.content?.toModel()?.isSilenced ?: false } - .onEach { setState { copy(areNotificationsSilenced = it) } } - } - } - private fun observeAnalytics() { if (analyticsConfig.isEnabled) { analyticsStore.didAskUserConsentFlow @@ -501,6 +495,9 @@ class HomeActivityViewModel @AssistedInject constructor( HomeActivityViewActions.ViewStarted -> { initialize() } + is HomeActivityViewActions.RegisterPushDistributor -> { + registerUnifiedPush(distributor = action.distributor) + } } } } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewState.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewState.kt index 4df2957cbc..f9c1b37ed5 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewState.kt @@ -23,5 +23,4 @@ import org.matrix.android.sdk.api.session.sync.SyncRequestState data class HomeActivityViewState( val syncRequestState: SyncRequestState = SyncRequestState.Idle, val authenticationDescription: AuthenticationDescription? = null, - val areNotificationsSilenced: Boolean = false, ) : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt index 180627a15f..91974787bd 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt @@ -18,6 +18,7 @@ package im.vector.app.features.settings.notifications import androidx.fragment.app.FragmentActivity import im.vector.app.core.di.ActiveSessionHolder +import im.vector.app.core.pushers.EnsureFcmTokenIsRetrievedUseCase import im.vector.app.core.pushers.FcmHelper import im.vector.app.core.pushers.PushersManager import im.vector.app.core.pushers.UnifiedPushHelper @@ -32,11 +33,12 @@ class EnableNotificationsForCurrentSessionUseCase @Inject constructor( private val activeSessionHolder: ActiveSessionHolder, private val unifiedPushHelper: UnifiedPushHelper, private val pushersManager: PushersManager, - private val fcmHelper: FcmHelper, private val checkIfCanTogglePushNotificationsViaPusherUseCase: CheckIfCanTogglePushNotificationsViaPusherUseCase, private val togglePushNotificationUseCase: TogglePushNotificationUseCase, + private val ensureFcmTokenIsRetrievedUseCase: EnsureFcmTokenIsRetrievedUseCase, ) { + // TODO update unit tests suspend fun execute(fragmentActivity: FragmentActivity) { val pusherForCurrentSession = pushersManager.getPusherForCurrentSession() if (pusherForCurrentSession == null) { @@ -54,13 +56,7 @@ class EnableNotificationsForCurrentSessionUseCase @Inject constructor( suspendCoroutine { continuation -> try { unifiedPushHelper.register(fragmentActivity) { - if (unifiedPushHelper.isEmbeddedDistributor()) { - fcmHelper.ensureFcmTokenIsRetrieved( - fragmentActivity, - pushersManager, - registerPusher = true - ) - } + ensureFcmTokenIsRetrievedUseCase.execute(pushersManager, registerPusher = true) continuation.resume(Unit) } } catch (error: Exception) { diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt index 58f86bc949..f8c1a9ad44 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt @@ -37,6 +37,7 @@ import im.vector.app.core.preference.VectorEditTextPreference import im.vector.app.core.preference.VectorPreference import im.vector.app.core.preference.VectorPreferenceCategory import im.vector.app.core.preference.VectorSwitchPreference +import im.vector.app.core.pushers.EnsureFcmTokenIsRetrievedUseCase import im.vector.app.core.pushers.FcmHelper import im.vector.app.core.pushers.PushersManager import im.vector.app.core.pushers.UnifiedPushHelper @@ -82,6 +83,7 @@ class VectorSettingsNotificationPreferenceFragment : @Inject lateinit var notificationPermissionManager: NotificationPermissionManager @Inject lateinit var disableNotificationsForCurrentSessionUseCase: DisableNotificationsForCurrentSessionUseCase @Inject lateinit var enableNotificationsForCurrentSessionUseCase: EnableNotificationsForCurrentSessionUseCase + @Inject lateinit var ensureFcmTokenIsRetrievedUseCase: EnsureFcmTokenIsRetrievedUseCase override var titleRes: Int = R.string.settings_notifications override val preferenceXmlRes = R.xml.vector_settings_notifications @@ -183,13 +185,7 @@ class VectorSettingsNotificationPreferenceFragment : it.summary = unifiedPushHelper.getCurrentDistributorName() it.onPreferenceClickListener = Preference.OnPreferenceClickListener { unifiedPushHelper.forceRegister(requireActivity(), pushersManager) { - if (unifiedPushHelper.isEmbeddedDistributor()) { - fcmHelper.ensureFcmTokenIsRetrieved( - requireActivity(), - pushersManager, - vectorPreferences.areNotificationEnabledForDevice() - ) - } + ensureFcmTokenIsRetrievedUseCase.execute(pushersManager, registerPusher = vectorPreferences.areNotificationEnabledForDevice()) it.summary = unifiedPushHelper.getCurrentDistributorName() session.pushersService().refreshPushers() refreshBackgroundSyncPrefs() From 2890f41f30d675b9988d12e62bcd94916da04e3c Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Tue, 29 Nov 2022 17:00:44 +0100 Subject: [PATCH 037/108] Replacing unregister method by usecase --- .../main/java/im/vector/app/core/di/ActiveSessionHolder.kt | 6 +++--- .../java/im/vector/app/core/pushers/UnifiedPushHelper.kt | 2 +- .../app/core/pushers/UnregisterUnifiedPushUseCase.kt | 5 ++--- .../DisableNotificationsForCurrentSessionUseCase.kt | 7 ++++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/di/ActiveSessionHolder.kt b/vector/src/main/java/im/vector/app/core/di/ActiveSessionHolder.kt index f1863cfa23..fead1e15b1 100644 --- a/vector/src/main/java/im/vector/app/core/di/ActiveSessionHolder.kt +++ b/vector/src/main/java/im/vector/app/core/di/ActiveSessionHolder.kt @@ -19,7 +19,7 @@ package im.vector.app.core.di import android.content.Context import im.vector.app.ActiveSessionDataSource import im.vector.app.core.extensions.startSyncing -import im.vector.app.core.pushers.UnifiedPushHelper +import im.vector.app.core.pushers.UnregisterUnifiedPushUseCase import im.vector.app.core.services.GuardServiceStarter import im.vector.app.core.session.ConfigureAndStartSessionUseCase import im.vector.app.features.call.webrtc.WebRtcCallManager @@ -46,12 +46,12 @@ class ActiveSessionHolder @Inject constructor( private val pushRuleTriggerListener: PushRuleTriggerListener, private val sessionListener: SessionListener, private val imageManager: ImageManager, - private val unifiedPushHelper: UnifiedPushHelper, private val guardServiceStarter: GuardServiceStarter, private val sessionInitializer: SessionInitializer, private val applicationContext: Context, private val authenticationService: AuthenticationService, private val configureAndStartSessionUseCase: ConfigureAndStartSessionUseCase, + private val unregisterUnifiedPushUseCase: UnregisterUnifiedPushUseCase, ) { private var activeSessionReference: AtomicReference = AtomicReference() @@ -85,7 +85,7 @@ class ActiveSessionHolder @Inject constructor( incomingVerificationRequestHandler.stop() pushRuleTriggerListener.stop() // No need to unregister the pusher, the sign out will (should?) do it server side. - unifiedPushHelper.unregister(pushersManager = null) + unregisterUnifiedPushUseCase.execute(pushersManager = null) guardServiceStarter.stop() } diff --git a/vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt b/vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt index 64d2a79494..34ba254250 100644 --- a/vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt +++ b/vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt @@ -213,7 +213,7 @@ class UnifiedPushHelper @Inject constructor( .show() } - // TODO remove and replace by use case + // TODO remove suspend fun unregister(pushersManager: PushersManager? = null) { val mode = BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_REALTIME vectorPreferences.setFdroidSyncBackgroundMode(mode) diff --git a/vector/src/main/java/im/vector/app/core/pushers/UnregisterUnifiedPushUseCase.kt b/vector/src/main/java/im/vector/app/core/pushers/UnregisterUnifiedPushUseCase.kt index d81581679e..71b1a9c033 100644 --- a/vector/src/main/java/im/vector/app/core/pushers/UnregisterUnifiedPushUseCase.kt +++ b/vector/src/main/java/im/vector/app/core/pushers/UnregisterUnifiedPushUseCase.kt @@ -26,20 +26,19 @@ import javax.inject.Inject class UnregisterUnifiedPushUseCase @Inject constructor( @ApplicationContext private val context: Context, - private val pushersManager: PushersManager, private val vectorPreferences: VectorPreferences, private val unifiedPushStore: UnifiedPushStore, private val unifiedPushHelper: UnifiedPushHelper, ) { // TODO add unit tests - suspend fun execute() { + suspend fun execute(pushersManager: PushersManager?) { val mode = BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_REALTIME vectorPreferences.setFdroidSyncBackgroundMode(mode) try { unifiedPushHelper.getEndpointOrToken()?.let { Timber.d("Removing $it") - pushersManager.unregisterPusher(it) + pushersManager?.unregisterPusher(it) } } catch (e: Exception) { Timber.d(e, "Probably unregistering a non existing pusher") diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCase.kt index 61c884f0bc..2ce2254f2e 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCase.kt @@ -18,26 +18,27 @@ package im.vector.app.features.settings.notifications import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.pushers.PushersManager -import im.vector.app.core.pushers.UnifiedPushHelper +import im.vector.app.core.pushers.UnregisterUnifiedPushUseCase import im.vector.app.features.settings.devices.v2.notification.CheckIfCanTogglePushNotificationsViaPusherUseCase import im.vector.app.features.settings.devices.v2.notification.TogglePushNotificationUseCase import javax.inject.Inject class DisableNotificationsForCurrentSessionUseCase @Inject constructor( private val activeSessionHolder: ActiveSessionHolder, - private val unifiedPushHelper: UnifiedPushHelper, private val pushersManager: PushersManager, private val checkIfCanTogglePushNotificationsViaPusherUseCase: CheckIfCanTogglePushNotificationsViaPusherUseCase, private val togglePushNotificationUseCase: TogglePushNotificationUseCase, + private val unregisterUnifiedPushUseCase: UnregisterUnifiedPushUseCase, ) { + // TODO update unit tests suspend fun execute() { val session = activeSessionHolder.getSafeActiveSession() ?: return val deviceId = session.sessionParams.deviceId ?: return if (checkIfCanTogglePushNotificationsViaPusherUseCase.execute(session)) { togglePushNotificationUseCase.execute(deviceId, enabled = false) } else { - unifiedPushHelper.unregister(pushersManager) + unregisterUnifiedPushUseCase.execute(pushersManager) } } } From 58efe90f7dfa23ffd17110eedec21ef480022fcf Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Tue, 29 Nov 2022 17:10:20 +0100 Subject: [PATCH 038/108] Removing some debug logs --- .../vector/app/core/pushers/EnsureFcmTokenIsRetrievedUseCase.kt | 2 -- .../java/im/vector/app/features/home/HomeActivityViewModel.kt | 2 -- 2 files changed, 4 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/pushers/EnsureFcmTokenIsRetrievedUseCase.kt b/vector/src/main/java/im/vector/app/core/pushers/EnsureFcmTokenIsRetrievedUseCase.kt index 4a8ff5fb39..e55d0426ba 100644 --- a/vector/src/main/java/im/vector/app/core/pushers/EnsureFcmTokenIsRetrievedUseCase.kt +++ b/vector/src/main/java/im/vector/app/core/pushers/EnsureFcmTokenIsRetrievedUseCase.kt @@ -17,7 +17,6 @@ package im.vector.app.core.pushers import im.vector.app.core.di.ActiveSessionHolder -import timber.log.Timber import javax.inject.Inject class EnsureFcmTokenIsRetrievedUseCase @Inject constructor( @@ -29,7 +28,6 @@ class EnsureFcmTokenIsRetrievedUseCase @Inject constructor( // TODO add unit tests fun execute(pushersManager: PushersManager, registerPusher: Boolean) { if (unifiedPushHelper.isEmbeddedDistributor()) { - Timber.d("ensureFcmTokenIsRetrieved") fcmHelper.ensureFcmTokenIsRetrieved(pushersManager, shouldAddHttpPusher(registerPusher)) } } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt index 3fd555bbea..2905decddf 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt @@ -131,11 +131,9 @@ class HomeActivityViewModel @AssistedInject constructor( viewModelScope.launch { when (val result = registerUnifiedPushUseCase.execute(distributor = distributor)) { is RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.NeedToAskUserForDistributor -> { - Timber.d("registerUnifiedPush $distributor need to ask user") _viewEvents.post(HomeActivityViewEvents.AskUserForPushDistributor(result.distributors)) } RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.Success -> { - Timber.d("registerUnifiedPush $distributor success") ensureFcmTokenIsRetrievedUseCase.execute(pushersManager, registerPusher = vectorPreferences.areNotificationEnabledForDevice()) } } From b29191e892bbc9d0b2f15fa106c1524da7c4f180 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Tue, 29 Nov 2022 17:28:48 +0100 Subject: [PATCH 039/108] Using use cases inside component for endpoint testing --- .../TestEndpointAsTokenRegistration.kt | 63 ++++++++++++++----- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestEndpointAsTokenRegistration.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestEndpointAsTokenRegistration.kt index 3bbff0f2fe..e6cb78d185 100644 --- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestEndpointAsTokenRegistration.kt +++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestEndpointAsTokenRegistration.kt @@ -17,14 +17,17 @@ package im.vector.app.features.settings.troubleshoot import androidx.fragment.app.FragmentActivity -import androidx.lifecycle.Observer import androidx.work.WorkInfo import androidx.work.WorkManager import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.pushers.PushersManager +import im.vector.app.core.pushers.RegisterUnifiedPushUseCase import im.vector.app.core.pushers.UnifiedPushHelper +import im.vector.app.core.pushers.UnregisterUnifiedPushUseCase import im.vector.app.core.resources.StringProvider +import im.vector.app.features.session.coroutineScope +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.pushers.PusherState import javax.inject.Inject @@ -34,6 +37,8 @@ class TestEndpointAsTokenRegistration @Inject constructor( private val pushersManager: PushersManager, private val activeSessionHolder: ActiveSessionHolder, private val unifiedPushHelper: UnifiedPushHelper, + private val registerUnifiedPushUseCase: RegisterUnifiedPushUseCase, + private val unregisterUnifiedPushUseCase: UnregisterUnifiedPushUseCase, ) : TroubleshootTest(R.string.settings_troubleshoot_test_endpoint_registration_title) { override fun perform(testParameters: TestParameters) { @@ -56,27 +61,53 @@ class TestEndpointAsTokenRegistration @Inject constructor( ) quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_endpoint_registration_quick_fix) { override fun doFix() { - unifiedPushHelper.forceRegister( - context, - pushersManager - ) - val workId = pushersManager.enqueueRegisterPusherWithFcmKey(endpoint) - WorkManager.getInstance(context).getWorkInfoByIdLiveData(workId).observe(context, Observer { workInfo -> - if (workInfo != null) { - if (workInfo.state == WorkInfo.State.SUCCEEDED) { - manager?.retry(testParameters) - } else if (workInfo.state == WorkInfo.State.FAILED) { - manager?.retry(testParameters) - } - } - }) + unregisterThenRegister(testParameters, endpoint) } } - status = TestStatus.FAILED } else { description = stringProvider.getString(R.string.settings_troubleshoot_test_endpoint_registration_success) status = TestStatus.SUCCESS } } + + private fun unregisterThenRegister(testParameters: TestParameters, pushKey: String) { + activeSessionHolder.getSafeActiveSession()?.coroutineScope?.launch { + unregisterUnifiedPushUseCase.execute(pushersManager) + registerUnifiedPush(distributor = "", testParameters, pushKey) + } + } + + private fun registerUnifiedPush( + distributor: String, + testParameters: TestParameters, + pushKey: String, + ) { + when (val result = registerUnifiedPushUseCase.execute(distributor)) { + is RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.NeedToAskUserForDistributor -> + askUserForDistributor(result.distributors, testParameters, pushKey) + RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.Success -> { + val workId = pushersManager.enqueueRegisterPusherWithFcmKey(pushKey) + WorkManager.getInstance(context).getWorkInfoByIdLiveData(workId).observe(context) { workInfo -> + if (workInfo != null) { + if (workInfo.state == WorkInfo.State.SUCCEEDED) { + manager?.retry(testParameters) + } else if (workInfo.state == WorkInfo.State.FAILED) { + manager?.retry(testParameters) + } + } + } + } + } + } + + private fun askUserForDistributor( + distributors: List, + testParameters: TestParameters, + pushKey: String, + ) { + unifiedPushHelper.showSelectDistributorDialog(context, distributors) { selection -> + registerUnifiedPush(distributor = selection, testParameters, pushKey) + } + } } From 3f944e9d36c892e88676e3e64fb061afd98b158b Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 30 Nov 2022 11:05:46 +0100 Subject: [PATCH 040/108] Extracting the logic to toggle notifications for device into a ViewModel --- .../app/core/di/MavericksViewModelModule.kt | 6 ++ .../settings/VectorSettingsBaseFragment.kt | 17 +++- ...leNotificationsForCurrentSessionUseCase.kt | 49 +++++------- ...rSettingsNotificationPreferenceFragment.kt | 73 ++++++++++++++---- ...ettingsNotificationPreferenceViewAction.kt | 25 ++++++ ...SettingsNotificationPreferenceViewEvent.kt | 26 +++++++ ...SettingsNotificationPreferenceViewModel.kt | 77 +++++++++++++++++++ 7 files changed, 227 insertions(+), 46 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewAction.kt create mode 100644 vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewEvent.kt create mode 100644 vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModel.kt diff --git a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt index 2242abb7aa..ad3e361775 100644 --- a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt @@ -105,6 +105,7 @@ import im.vector.app.features.settings.ignored.IgnoredUsersViewModel import im.vector.app.features.settings.labs.VectorSettingsLabsViewModel import im.vector.app.features.settings.legals.LegalsViewModel import im.vector.app.features.settings.locale.LocalePickerViewModel +import im.vector.app.features.settings.notifications.VectorSettingsNotificationPreferenceViewModel import im.vector.app.features.settings.push.PushGatewaysViewModel import im.vector.app.features.settings.threepids.ThreePidsSettingsViewModel import im.vector.app.features.share.IncomingShareViewModel @@ -683,4 +684,9 @@ interface MavericksViewModelModule { @IntoMap @MavericksViewModelKey(AttachmentTypeSelectorViewModel::class) fun attachmentTypeSelectorViewModelFactory(factory: AttachmentTypeSelectorViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(VectorSettingsNotificationPreferenceViewModel::class) + fun vectorSettingsNotificationPreferenceViewModelFactory(factory: VectorSettingsNotificationPreferenceViewModel.Factory): MavericksAssistedViewModelFactory<*, *> } diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsBaseFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsBaseFragment.kt index 176909b48d..38ba949a49 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsBaseFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsBaseFragment.kt @@ -28,6 +28,8 @@ import im.vector.app.R import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.extensions.singletonEntryPoint import im.vector.app.core.platform.VectorBaseActivity +import im.vector.app.core.platform.VectorViewEvents +import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.utils.toast import im.vector.app.features.analytics.AnalyticsTracker import im.vector.app.features.analytics.plan.MobileScreen @@ -60,6 +62,19 @@ abstract class VectorSettingsBaseFragment : PreferenceFragmentCompat(), Maverick protected lateinit var session: Session protected lateinit var errorFormatter: ErrorFormatter + /* ========================================================================================== + * ViewEvents + * ========================================================================================== */ + + protected fun VectorViewModel<*, *, T>.observeViewEvents(observer: (T) -> Unit) { + viewEvents + .stream() + .onEach { + observer(it) + } + .launchIn(viewLifecycleOwner.lifecycleScope) + } + /* ========================================================================================== * Views * ========================================================================================== */ @@ -148,7 +163,7 @@ abstract class VectorSettingsBaseFragment : PreferenceFragmentCompat(), Maverick } } - protected fun displayErrorDialog(throwable: Throwable) { + protected fun displayErrorDialog(throwable: Throwable?) { displayErrorDialog(errorFormatter.toHumanReadable(throwable)) } diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt index 91974787bd..2f8bdd4d0d 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt @@ -16,52 +16,45 @@ package im.vector.app.features.settings.notifications -import androidx.fragment.app.FragmentActivity import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.pushers.EnsureFcmTokenIsRetrievedUseCase -import im.vector.app.core.pushers.FcmHelper import im.vector.app.core.pushers.PushersManager -import im.vector.app.core.pushers.UnifiedPushHelper -import im.vector.app.features.settings.devices.v2.notification.CheckIfCanTogglePushNotificationsViaPusherUseCase +import im.vector.app.core.pushers.RegisterUnifiedPushUseCase import im.vector.app.features.settings.devices.v2.notification.TogglePushNotificationUseCase import javax.inject.Inject -import kotlin.coroutines.resume -import kotlin.coroutines.resumeWithException -import kotlin.coroutines.suspendCoroutine class EnableNotificationsForCurrentSessionUseCase @Inject constructor( private val activeSessionHolder: ActiveSessionHolder, - private val unifiedPushHelper: UnifiedPushHelper, private val pushersManager: PushersManager, - private val checkIfCanTogglePushNotificationsViaPusherUseCase: CheckIfCanTogglePushNotificationsViaPusherUseCase, private val togglePushNotificationUseCase: TogglePushNotificationUseCase, + private val registerUnifiedPushUseCase: RegisterUnifiedPushUseCase, private val ensureFcmTokenIsRetrievedUseCase: EnsureFcmTokenIsRetrievedUseCase, ) { + sealed interface EnableNotificationsResult { + object Success : EnableNotificationsResult + object Failure : EnableNotificationsResult + data class NeedToAskUserForDistributor(val distributors: List) : EnableNotificationsResult + } + // TODO update unit tests - suspend fun execute(fragmentActivity: FragmentActivity) { + suspend fun execute(distributor: String = ""): EnableNotificationsResult { val pusherForCurrentSession = pushersManager.getPusherForCurrentSession() if (pusherForCurrentSession == null) { - registerPusher(fragmentActivity) - } - - val session = activeSessionHolder.getSafeActiveSession() ?: return - if (checkIfCanTogglePushNotificationsViaPusherUseCase.execute(session)) { - val deviceId = session.sessionParams.deviceId ?: return - togglePushNotificationUseCase.execute(deviceId, enabled = true) - } - } - - private suspend fun registerPusher(fragmentActivity: FragmentActivity) { - suspendCoroutine { continuation -> - try { - unifiedPushHelper.register(fragmentActivity) { - ensureFcmTokenIsRetrievedUseCase.execute(pushersManager, registerPusher = true) - continuation.resume(Unit) + when (val result = registerUnifiedPushUseCase.execute(distributor)) { + is RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.NeedToAskUserForDistributor -> { + return EnableNotificationsResult.NeedToAskUserForDistributor(result.distributors) + } + RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.Success -> { + ensureFcmTokenIsRetrievedUseCase.execute(pushersManager, registerPusher = true) } - } catch (error: Exception) { - continuation.resumeWithException(error) } } + + val session = activeSessionHolder.getSafeActiveSession() ?: return EnableNotificationsResult.Failure + val deviceId = session.sessionParams.deviceId ?: return EnableNotificationsResult.Failure + togglePushNotificationUseCase.execute(deviceId, enabled = true) + + return EnableNotificationsResult.Success } } diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt index f8c1a9ad44..ae00b3864c 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt @@ -22,6 +22,7 @@ import android.content.Intent import android.media.RingtoneManager import android.net.Uri import android.os.Bundle +import android.view.View import android.widget.Toast import androidx.lifecycle.LiveData import androidx.lifecycle.distinctUntilChanged @@ -29,6 +30,7 @@ import androidx.lifecycle.lifecycleScope import androidx.lifecycle.map import androidx.preference.Preference import androidx.preference.SwitchPreference +import com.airbnb.mvrx.fragmentViewModel import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder @@ -81,8 +83,6 @@ class VectorSettingsNotificationPreferenceFragment : @Inject lateinit var guardServiceStarter: GuardServiceStarter @Inject lateinit var vectorFeatures: VectorFeatures @Inject lateinit var notificationPermissionManager: NotificationPermissionManager - @Inject lateinit var disableNotificationsForCurrentSessionUseCase: DisableNotificationsForCurrentSessionUseCase - @Inject lateinit var enableNotificationsForCurrentSessionUseCase: EnableNotificationsForCurrentSessionUseCase @Inject lateinit var ensureFcmTokenIsRetrievedUseCase: EnsureFcmTokenIsRetrievedUseCase override var titleRes: Int = R.string.settings_notifications @@ -90,6 +90,8 @@ class VectorSettingsNotificationPreferenceFragment : private var interactionListener: VectorSettingsFragmentInteractionListener? = null + private val viewModel: VectorSettingsNotificationPreferenceViewModel by fragmentViewModel() + private val notificationStartForActivityResult = registerStartForActivityResult { _ -> // No op } @@ -106,6 +108,22 @@ class VectorSettingsNotificationPreferenceFragment : analyticsScreenName = MobileScreen.ScreenName.SettingsNotifications } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + observeViewEvents() + } + + private fun observeViewEvents() { + viewModel.observeViewEvents { + when (it) { + VectorSettingsNotificationPreferenceViewEvent.NotificationForDeviceEnabled -> onNotificationsForDeviceEnabled() + VectorSettingsNotificationPreferenceViewEvent.NotificationForDeviceDisabled -> onNotificationsForDeviceDisabled() + is VectorSettingsNotificationPreferenceViewEvent.AskUserForPushDistributor -> askUserToSelectPushDistributor(it.distributors) + VectorSettingsNotificationPreferenceViewEvent.EnableNotificationForDeviceFailure -> displayErrorDialog(throwable = null) + } + } + } + override fun bindPref() { findPreference(VectorPreferences.SETTINGS_ENABLE_ALL_NOTIF_PREFERENCE_KEY)!!.let { pref -> val pushRuleService = session.pushRuleService() @@ -123,23 +141,15 @@ class VectorSettingsNotificationPreferenceFragment : } findPreference(VectorPreferences.SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY) - ?.setTransactionalSwitchChangeListener(lifecycleScope) { isChecked -> - if (isChecked) { - enableNotificationsForCurrentSessionUseCase.execute(requireActivity()) - - findPreference(VectorPreferences.SETTINGS_NOTIFICATION_METHOD_KEY) - ?.summary = unifiedPushHelper.getCurrentDistributorName() - - notificationPermissionManager.eventuallyRequestPermission( - requireActivity(), - postPermissionLauncher, - showRationale = false, - ignorePreference = true - ) + ?.setOnPreferenceChangeListener { _, isChecked -> + val action = if (isChecked as Boolean) { + VectorSettingsNotificationPreferenceViewAction.EnableNotificationsForDevice(pushDistributor = "") } else { - disableNotificationsForCurrentSessionUseCase.execute() - notificationPermissionManager.eventuallyRevokePermission(requireActivity()) + VectorSettingsNotificationPreferenceViewAction.DisableNotificationsForDevice } + viewModel.handle(action) + // preference will be updated on ViewEvent reception + false } findPreference(VectorPreferences.SETTINGS_FDROID_BACKGROUND_SYNC_MODE)?.let { @@ -184,6 +194,8 @@ class VectorSettingsNotificationPreferenceFragment : if (vectorFeatures.allowExternalUnifiedPushDistributors()) { it.summary = unifiedPushHelper.getCurrentDistributorName() it.onPreferenceClickListener = Preference.OnPreferenceClickListener { + // TODO show dialog to pick a distributor + // TODO call unregister then register only when a new distributor has been selected => use UnifiedPushHelper method unifiedPushHelper.forceRegister(requireActivity(), pushersManager) { ensureFcmTokenIsRetrievedUseCase.execute(pushersManager, registerPusher = vectorPreferences.areNotificationEnabledForDevice()) it.summary = unifiedPushHelper.getCurrentDistributorName() @@ -203,6 +215,33 @@ class VectorSettingsNotificationPreferenceFragment : handleSystemPreference() } + private fun onNotificationsForDeviceEnabled() { + findPreference(VectorPreferences.SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY) + ?.isChecked = true + findPreference(VectorPreferences.SETTINGS_NOTIFICATION_METHOD_KEY) + ?.summary = unifiedPushHelper.getCurrentDistributorName() + + notificationPermissionManager.eventuallyRequestPermission( + requireActivity(), + postPermissionLauncher, + showRationale = false, + ignorePreference = true + ) + } + + private fun onNotificationsForDeviceDisabled() { + findPreference(VectorPreferences.SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY) + ?.isChecked = false + notificationPermissionManager.eventuallyRevokePermission(requireActivity()) + } + + // TODO add an argument to know if unregister should be called + private fun askUserToSelectPushDistributor(distributors: List) { + unifiedPushHelper.showSelectDistributorDialog(requireContext(), distributors) { selection -> + viewModel.handle(VectorSettingsNotificationPreferenceViewAction.RegisterPushDistributor(selection)) + } + } + private fun bindEmailNotifications() { val initialEmails = session.getEmailsWithPushInformation() bindEmailNotificationCategory(initialEmails) diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewAction.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewAction.kt new file mode 100644 index 0000000000..949dc99993 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewAction.kt @@ -0,0 +1,25 @@ +/* + * 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.settings.notifications + +import im.vector.app.core.platform.VectorViewModelAction + +sealed interface VectorSettingsNotificationPreferenceViewAction : VectorViewModelAction { + data class EnableNotificationsForDevice(val pushDistributor: String) : VectorSettingsNotificationPreferenceViewAction + object DisableNotificationsForDevice : VectorSettingsNotificationPreferenceViewAction + data class RegisterPushDistributor(val pushDistributor: String) : VectorSettingsNotificationPreferenceViewAction +} diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewEvent.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewEvent.kt new file mode 100644 index 0000000000..4948ad6e58 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewEvent.kt @@ -0,0 +1,26 @@ +/* + * 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.settings.notifications + +import im.vector.app.core.platform.VectorViewEvents + +sealed interface VectorSettingsNotificationPreferenceViewEvent : VectorViewEvents { + object NotificationForDeviceEnabled : VectorSettingsNotificationPreferenceViewEvent + object EnableNotificationForDeviceFailure : VectorSettingsNotificationPreferenceViewEvent + object NotificationForDeviceDisabled : VectorSettingsNotificationPreferenceViewEvent + data class AskUserForPushDistributor(val distributors: List) : VectorSettingsNotificationPreferenceViewEvent +} diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModel.kt new file mode 100644 index 0000000000..0173f4846f --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModel.kt @@ -0,0 +1,77 @@ +/* + * 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.settings.notifications + +import com.airbnb.mvrx.MavericksViewModelFactory +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory +import im.vector.app.core.platform.VectorDummyViewState +import im.vector.app.core.platform.VectorViewModel +import kotlinx.coroutines.launch + +class VectorSettingsNotificationPreferenceViewModel @AssistedInject constructor( + @Assisted initialState: VectorDummyViewState, + private val enableNotificationsForCurrentSessionUseCase: EnableNotificationsForCurrentSessionUseCase, + private val disableNotificationsForCurrentSessionUseCase: DisableNotificationsForCurrentSessionUseCase, +) : VectorViewModel(initialState) { + + @AssistedFactory + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: VectorDummyViewState): VectorSettingsNotificationPreferenceViewModel + } + + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() + + // TODO add unit tests + override fun handle(action: VectorSettingsNotificationPreferenceViewAction) { + when (action) { + VectorSettingsNotificationPreferenceViewAction.DisableNotificationsForDevice -> handleDisableNotificationsForDevice() + is VectorSettingsNotificationPreferenceViewAction.EnableNotificationsForDevice -> handleEnableNotificationsForDevice(action.pushDistributor) + is VectorSettingsNotificationPreferenceViewAction.RegisterPushDistributor -> handleRegisterPushDistributor(action.pushDistributor) + } + } + + private fun handleDisableNotificationsForDevice() { + viewModelScope.launch { + disableNotificationsForCurrentSessionUseCase.execute() + _viewEvents.post(VectorSettingsNotificationPreferenceViewEvent.NotificationForDeviceDisabled) + } + } + + private fun handleEnableNotificationsForDevice(distributor: String) { + viewModelScope.launch { + when (val result = enableNotificationsForCurrentSessionUseCase.execute(distributor)) { + EnableNotificationsForCurrentSessionUseCase.EnableNotificationsResult.Failure -> { + _viewEvents.post(VectorSettingsNotificationPreferenceViewEvent.EnableNotificationForDeviceFailure) + } + is EnableNotificationsForCurrentSessionUseCase.EnableNotificationsResult.NeedToAskUserForDistributor -> { + _viewEvents.post(VectorSettingsNotificationPreferenceViewEvent.AskUserForPushDistributor(result.distributors)) + } + EnableNotificationsForCurrentSessionUseCase.EnableNotificationsResult.Success -> { + _viewEvents.post(VectorSettingsNotificationPreferenceViewEvent.NotificationForDeviceEnabled) + } + } + } + } + + private fun handleRegisterPushDistributor(distributor: String) { + handleEnableNotificationsForDevice(distributor) + } +} From 95556d25515d08b6f9fcc8154be65908110f17ef Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 30 Nov 2022 11:06:24 +0100 Subject: [PATCH 041/108] Change the distributor in dialog cancellation only if there is no existing one --- .../java/im/vector/app/core/pushers/UnifiedPushHelper.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt b/vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt index 34ba254250..87231d1d67 100644 --- a/vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt +++ b/vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt @@ -206,8 +206,11 @@ class UnifiedPushHelper @Inject constructor( onDistributorSelected(distributor) } .setOnCancelListener { - // By default, use internal solution (fcm/background sync) - onDistributorSelected(context.packageName) + // we do not want to change the distributor on behalf of the user + if (UnifiedPush.getDistributor(context).isEmpty()) { + // By default, use internal solution (fcm/background sync) + onDistributorSelected(context.packageName) + } } .setCancelable(true) .show() From 2673979ef8f7feddd60dcd6058a3790435729db0 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 30 Nov 2022 12:04:52 +0100 Subject: [PATCH 042/108] Handling change of notification method --- .../pushers/RegisterUnifiedPushUseCase.kt | 6 ++-- .../app/core/pushers/UnifiedPushHelper.kt | 7 ++-- .../vector/app/features/home/HomeActivity.kt | 6 ++-- .../features/home/HomeActivityViewEvents.kt | 2 +- .../features/home/HomeActivityViewModel.kt | 4 +-- ...leNotificationsForCurrentSessionUseCase.kt | 6 ++-- ...rSettingsNotificationPreferenceFragment.kt | 33 ++++++++++--------- ...SettingsNotificationPreferenceViewEvent.kt | 7 ++-- ...SettingsNotificationPreferenceViewModel.kt | 31 ++++++++++++++--- .../TestEndpointAsTokenRegistration.kt | 7 ++-- 10 files changed, 67 insertions(+), 42 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/pushers/RegisterUnifiedPushUseCase.kt b/vector/src/main/java/im/vector/app/core/pushers/RegisterUnifiedPushUseCase.kt index 7aafa07348..58bf0f5050 100644 --- a/vector/src/main/java/im/vector/app/core/pushers/RegisterUnifiedPushUseCase.kt +++ b/vector/src/main/java/im/vector/app/core/pushers/RegisterUnifiedPushUseCase.kt @@ -29,12 +29,12 @@ class RegisterUnifiedPushUseCase @Inject constructor( sealed interface RegisterUnifiedPushResult { object Success : RegisterUnifiedPushResult - data class NeedToAskUserForDistributor(val distributors: List) : RegisterUnifiedPushResult + object NeedToAskUserForDistributor : RegisterUnifiedPushResult } // TODO add unit tests fun execute(distributor: String = ""): RegisterUnifiedPushResult { - if(distributor.isNotEmpty()) { + if (distributor.isNotEmpty()) { saveAndRegisterApp(distributor) return RegisterUnifiedPushResult.Success } @@ -55,7 +55,7 @@ class RegisterUnifiedPushUseCase @Inject constructor( saveAndRegisterApp(distributors.first()) RegisterUnifiedPushResult.Success } else { - RegisterUnifiedPushResult.NeedToAskUserForDistributor(distributors) + RegisterUnifiedPushResult.NeedToAskUserForDistributor } } diff --git a/vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt b/vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt index 87231d1d67..efa396a980 100644 --- a/vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt +++ b/vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt @@ -52,7 +52,7 @@ class UnifiedPushHelper @Inject constructor( // Called when the home activity starts // or when notifications are enabled - // TODO remove and replace by use case + // TODO remove fun register( activity: FragmentActivity, onDoneRunnable: Runnable? = null, @@ -70,7 +70,7 @@ class UnifiedPushHelper @Inject constructor( // The registration is forced in 2 cases : // * in the settings // * in the troubleshoot list (doFix) - // TODO remove and replace by use case + // TODO remove fun forceRegister( activity: FragmentActivity, pushersManager: PushersManager, @@ -132,6 +132,7 @@ class UnifiedPushHelper @Inject constructor( } } + // TODO remove // There is no case where this function is called // with a saved distributor and/or a pusher private fun openDistributorDialogInternal( @@ -180,7 +181,6 @@ class UnifiedPushHelper @Inject constructor( @MainThread fun showSelectDistributorDialog( context: Context, - distributors: List, onDistributorSelected: (String) -> Unit, ) { val internalDistributorName = stringProvider.getString( @@ -191,6 +191,7 @@ class UnifiedPushHelper @Inject constructor( } ) + val distributors = UnifiedPush.getDistributors(context) val distributorsName = distributors.map { if (it == context.packageName) { internalDistributorName diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt index 14157a1de8..8c6daae95a 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt @@ -266,7 +266,7 @@ class HomeActivity : HomeActivityViewEvents.ShowReleaseNotes -> handleShowReleaseNotes() HomeActivityViewEvents.NotifyUserForThreadsMigration -> handleNotifyUserForThreadsMigration() is HomeActivityViewEvents.MigrateThreads -> migrateThreadsIfNeeded(it.checkSession) - is HomeActivityViewEvents.AskUserForPushDistributor -> askUserToSelectPushDistributor(it.distributors) + is HomeActivityViewEvents.AskUserForPushDistributor -> askUserToSelectPushDistributor() } } homeActivityViewModel.onEach { renderState(it) } @@ -279,8 +279,8 @@ class HomeActivity : homeActivityViewModel.handle(HomeActivityViewActions.ViewStarted) } - private fun askUserToSelectPushDistributor(distributors: List) { - unifiedPushHelper.showSelectDistributorDialog(this, distributors) { selection -> + private fun askUserToSelectPushDistributor() { + unifiedPushHelper.showSelectDistributorDialog(this) { selection -> homeActivityViewModel.handle(HomeActivityViewActions.RegisterPushDistributor(selection)) } } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt index 6fdf441d1d..be5aa7def0 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt @@ -39,5 +39,5 @@ sealed interface HomeActivityViewEvents : VectorViewEvents { data class MigrateThreads(val checkSession: Boolean) : HomeActivityViewEvents object StartRecoverySetupFlow : HomeActivityViewEvents data class ForceVerification(val sendRequest: Boolean) : HomeActivityViewEvents - data class AskUserForPushDistributor(val distributors: List) : HomeActivityViewEvents + object AskUserForPushDistributor : HomeActivityViewEvents } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt index 2905decddf..7ffc46218c 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt @@ -129,9 +129,9 @@ class HomeActivityViewModel @AssistedInject constructor( private fun registerUnifiedPush(distributor: String) { viewModelScope.launch { - when (val result = registerUnifiedPushUseCase.execute(distributor = distributor)) { + when (registerUnifiedPushUseCase.execute(distributor = distributor)) { is RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.NeedToAskUserForDistributor -> { - _viewEvents.post(HomeActivityViewEvents.AskUserForPushDistributor(result.distributors)) + _viewEvents.post(HomeActivityViewEvents.AskUserForPushDistributor) } RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.Success -> { ensureFcmTokenIsRetrievedUseCase.execute(pushersManager, registerPusher = vectorPreferences.areNotificationEnabledForDevice()) diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt index 2f8bdd4d0d..e0b0a872f8 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt @@ -34,16 +34,16 @@ class EnableNotificationsForCurrentSessionUseCase @Inject constructor( sealed interface EnableNotificationsResult { object Success : EnableNotificationsResult object Failure : EnableNotificationsResult - data class NeedToAskUserForDistributor(val distributors: List) : EnableNotificationsResult + object NeedToAskUserForDistributor : EnableNotificationsResult } // TODO update unit tests suspend fun execute(distributor: String = ""): EnableNotificationsResult { val pusherForCurrentSession = pushersManager.getPusherForCurrentSession() if (pusherForCurrentSession == null) { - when (val result = registerUnifiedPushUseCase.execute(distributor)) { + when (registerUnifiedPushUseCase.execute(distributor)) { is RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.NeedToAskUserForDistributor -> { - return EnableNotificationsResult.NeedToAskUserForDistributor(result.distributors) + return EnableNotificationsResult.NeedToAskUserForDistributor } RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.Success -> { ensureFcmTokenIsRetrievedUseCase.execute(pushersManager, registerPusher = true) diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt index ae00b3864c..238ed4218c 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt @@ -116,10 +116,11 @@ class VectorSettingsNotificationPreferenceFragment : private fun observeViewEvents() { viewModel.observeViewEvents { when (it) { - VectorSettingsNotificationPreferenceViewEvent.NotificationForDeviceEnabled -> onNotificationsForDeviceEnabled() - VectorSettingsNotificationPreferenceViewEvent.NotificationForDeviceDisabled -> onNotificationsForDeviceDisabled() - is VectorSettingsNotificationPreferenceViewEvent.AskUserForPushDistributor -> askUserToSelectPushDistributor(it.distributors) + VectorSettingsNotificationPreferenceViewEvent.NotificationsForDeviceEnabled -> onNotificationsForDeviceEnabled() + VectorSettingsNotificationPreferenceViewEvent.NotificationsForDeviceDisabled -> onNotificationsForDeviceDisabled() + is VectorSettingsNotificationPreferenceViewEvent.AskUserForPushDistributor -> askUserToSelectPushDistributor() VectorSettingsNotificationPreferenceViewEvent.EnableNotificationForDeviceFailure -> displayErrorDialog(throwable = null) + VectorSettingsNotificationPreferenceViewEvent.NotificationMethodChanged -> onNotificationMethodChanged() } } } @@ -194,14 +195,7 @@ class VectorSettingsNotificationPreferenceFragment : if (vectorFeatures.allowExternalUnifiedPushDistributors()) { it.summary = unifiedPushHelper.getCurrentDistributorName() it.onPreferenceClickListener = Preference.OnPreferenceClickListener { - // TODO show dialog to pick a distributor - // TODO call unregister then register only when a new distributor has been selected => use UnifiedPushHelper method - unifiedPushHelper.forceRegister(requireActivity(), pushersManager) { - ensureFcmTokenIsRetrievedUseCase.execute(pushersManager, registerPusher = vectorPreferences.areNotificationEnabledForDevice()) - it.summary = unifiedPushHelper.getCurrentDistributorName() - session.pushersService().refreshPushers() - refreshBackgroundSyncPrefs() - } + askUserToSelectPushDistributor(withUnregister = true) true } } else { @@ -235,13 +229,22 @@ class VectorSettingsNotificationPreferenceFragment : notificationPermissionManager.eventuallyRevokePermission(requireActivity()) } - // TODO add an argument to know if unregister should be called - private fun askUserToSelectPushDistributor(distributors: List) { - unifiedPushHelper.showSelectDistributorDialog(requireContext(), distributors) { selection -> - viewModel.handle(VectorSettingsNotificationPreferenceViewAction.RegisterPushDistributor(selection)) + private fun askUserToSelectPushDistributor(withUnregister: Boolean = false) { + unifiedPushHelper.showSelectDistributorDialog(requireContext()) { selection -> + if (withUnregister) { + viewModel.handle(VectorSettingsNotificationPreferenceViewAction.RegisterPushDistributor(selection)) + } else { + viewModel.handle(VectorSettingsNotificationPreferenceViewAction.EnableNotificationsForDevice(selection)) + } } } + private fun onNotificationMethodChanged() { + findPreference(VectorPreferences.SETTINGS_NOTIFICATION_METHOD_KEY)?.summary = unifiedPushHelper.getCurrentDistributorName() + session.pushersService().refreshPushers() + refreshBackgroundSyncPrefs() + } + private fun bindEmailNotifications() { val initialEmails = session.getEmailsWithPushInformation() bindEmailNotificationCategory(initialEmails) diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewEvent.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewEvent.kt index 4948ad6e58..e4cf8e1973 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewEvent.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewEvent.kt @@ -19,8 +19,9 @@ package im.vector.app.features.settings.notifications import im.vector.app.core.platform.VectorViewEvents sealed interface VectorSettingsNotificationPreferenceViewEvent : VectorViewEvents { - object NotificationForDeviceEnabled : VectorSettingsNotificationPreferenceViewEvent + object NotificationsForDeviceEnabled : VectorSettingsNotificationPreferenceViewEvent object EnableNotificationForDeviceFailure : VectorSettingsNotificationPreferenceViewEvent - object NotificationForDeviceDisabled : VectorSettingsNotificationPreferenceViewEvent - data class AskUserForPushDistributor(val distributors: List) : VectorSettingsNotificationPreferenceViewEvent + object NotificationsForDeviceDisabled : VectorSettingsNotificationPreferenceViewEvent + object AskUserForPushDistributor : VectorSettingsNotificationPreferenceViewEvent + object NotificationMethodChanged : VectorSettingsNotificationPreferenceViewEvent } diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModel.kt index 0173f4846f..59c26749c9 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModel.kt @@ -24,12 +24,22 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.VectorDummyViewState import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.pushers.EnsureFcmTokenIsRetrievedUseCase +import im.vector.app.core.pushers.PushersManager +import im.vector.app.core.pushers.RegisterUnifiedPushUseCase +import im.vector.app.core.pushers.UnregisterUnifiedPushUseCase +import im.vector.app.features.settings.VectorPreferences import kotlinx.coroutines.launch class VectorSettingsNotificationPreferenceViewModel @AssistedInject constructor( @Assisted initialState: VectorDummyViewState, + private val pushersManager: PushersManager, + private val vectorPreferences: VectorPreferences, private val enableNotificationsForCurrentSessionUseCase: EnableNotificationsForCurrentSessionUseCase, private val disableNotificationsForCurrentSessionUseCase: DisableNotificationsForCurrentSessionUseCase, + private val unregisterUnifiedPushUseCase: UnregisterUnifiedPushUseCase, + private val registerUnifiedPushUseCase: RegisterUnifiedPushUseCase, + private val ensureFcmTokenIsRetrievedUseCase: EnsureFcmTokenIsRetrievedUseCase, ) : VectorViewModel(initialState) { @AssistedFactory @@ -51,27 +61,38 @@ class VectorSettingsNotificationPreferenceViewModel @AssistedInject constructor( private fun handleDisableNotificationsForDevice() { viewModelScope.launch { disableNotificationsForCurrentSessionUseCase.execute() - _viewEvents.post(VectorSettingsNotificationPreferenceViewEvent.NotificationForDeviceDisabled) + _viewEvents.post(VectorSettingsNotificationPreferenceViewEvent.NotificationsForDeviceDisabled) } } private fun handleEnableNotificationsForDevice(distributor: String) { viewModelScope.launch { - when (val result = enableNotificationsForCurrentSessionUseCase.execute(distributor)) { + when (enableNotificationsForCurrentSessionUseCase.execute(distributor)) { EnableNotificationsForCurrentSessionUseCase.EnableNotificationsResult.Failure -> { _viewEvents.post(VectorSettingsNotificationPreferenceViewEvent.EnableNotificationForDeviceFailure) } is EnableNotificationsForCurrentSessionUseCase.EnableNotificationsResult.NeedToAskUserForDistributor -> { - _viewEvents.post(VectorSettingsNotificationPreferenceViewEvent.AskUserForPushDistributor(result.distributors)) + _viewEvents.post(VectorSettingsNotificationPreferenceViewEvent.AskUserForPushDistributor) } EnableNotificationsForCurrentSessionUseCase.EnableNotificationsResult.Success -> { - _viewEvents.post(VectorSettingsNotificationPreferenceViewEvent.NotificationForDeviceEnabled) + _viewEvents.post(VectorSettingsNotificationPreferenceViewEvent.NotificationsForDeviceEnabled) } } } } private fun handleRegisterPushDistributor(distributor: String) { - handleEnableNotificationsForDevice(distributor) + viewModelScope.launch { + unregisterUnifiedPushUseCase.execute(pushersManager) + when (registerUnifiedPushUseCase.execute(distributor)) { + RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.NeedToAskUserForDistributor -> { + _viewEvents.post(VectorSettingsNotificationPreferenceViewEvent.AskUserForPushDistributor) + } + RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.Success -> { + ensureFcmTokenIsRetrievedUseCase.execute(pushersManager, registerPusher = vectorPreferences.areNotificationEnabledForDevice()) + _viewEvents.post(VectorSettingsNotificationPreferenceViewEvent.NotificationMethodChanged) + } + } + } } } diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestEndpointAsTokenRegistration.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestEndpointAsTokenRegistration.kt index e6cb78d185..b355b55903 100644 --- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestEndpointAsTokenRegistration.kt +++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestEndpointAsTokenRegistration.kt @@ -83,9 +83,9 @@ class TestEndpointAsTokenRegistration @Inject constructor( testParameters: TestParameters, pushKey: String, ) { - when (val result = registerUnifiedPushUseCase.execute(distributor)) { + when (registerUnifiedPushUseCase.execute(distributor)) { is RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.NeedToAskUserForDistributor -> - askUserForDistributor(result.distributors, testParameters, pushKey) + askUserForDistributor(testParameters, pushKey) RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.Success -> { val workId = pushersManager.enqueueRegisterPusherWithFcmKey(pushKey) WorkManager.getInstance(context).getWorkInfoByIdLiveData(workId).observe(context) { workInfo -> @@ -102,11 +102,10 @@ class TestEndpointAsTokenRegistration @Inject constructor( } private fun askUserForDistributor( - distributors: List, testParameters: TestParameters, pushKey: String, ) { - unifiedPushHelper.showSelectDistributorDialog(context, distributors) { selection -> + unifiedPushHelper.showSelectDistributorDialog(context) { selection -> registerUnifiedPush(distributor = selection, testParameters, pushKey) } } From 740ed8963892ff76a4482c43c090996ca7579f76 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 30 Nov 2022 14:27:18 +0100 Subject: [PATCH 043/108] Removing the old methods from helper --- .../app/core/pushers/UnifiedPushHelper.kt | 155 ------------------ 1 file changed, 155 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt b/vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt index efa396a980..9f96f13ee7 100644 --- a/vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt +++ b/vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt @@ -18,20 +18,12 @@ package im.vector.app.core.pushers import android.content.Context import androidx.annotation.MainThread -import androidx.fragment.app.FragmentActivity -import androidx.lifecycle.lifecycleScope import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.squareup.moshi.Json import com.squareup.moshi.JsonClass import im.vector.app.R import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.getApplicationLabel -import im.vector.app.features.VectorFeatures -import im.vector.app.features.settings.BackgroundSyncMode -import im.vector.app.features.settings.VectorPreferences -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.Matrix import org.matrix.android.sdk.api.cache.CacheStrategy import org.matrix.android.sdk.api.util.MatrixJsonParser @@ -44,140 +36,10 @@ class UnifiedPushHelper @Inject constructor( private val context: Context, private val unifiedPushStore: UnifiedPushStore, private val stringProvider: StringProvider, - private val vectorPreferences: VectorPreferences, private val matrix: Matrix, - private val vectorFeatures: VectorFeatures, private val fcmHelper: FcmHelper, ) { - // Called when the home activity starts - // or when notifications are enabled - // TODO remove - fun register( - activity: FragmentActivity, - onDoneRunnable: Runnable? = null, - ) { - registerInternal( - activity, - onDoneRunnable = onDoneRunnable - ) - } - - // If registration is forced: - // * the current distributor (if any) is removed - // * The dialog is opened - // - // The registration is forced in 2 cases : - // * in the settings - // * in the troubleshoot list (doFix) - // TODO remove - fun forceRegister( - activity: FragmentActivity, - pushersManager: PushersManager, - @MainThread onDoneRunnable: Runnable? = null - ) { - registerInternal( - activity, - force = true, - pushersManager = pushersManager, - onDoneRunnable = onDoneRunnable - ) - } - - // TODO remove - private fun registerInternal( - activity: FragmentActivity, - force: Boolean = false, - pushersManager: PushersManager? = null, - onDoneRunnable: Runnable? = null - ) { - activity.lifecycleScope.launch(Dispatchers.IO) { - Timber.d("registerInternal force=$force, $activity on thread ${Thread.currentThread()}") - if (!vectorFeatures.allowExternalUnifiedPushDistributors()) { - UnifiedPush.saveDistributor(context, context.packageName) - UnifiedPush.registerApp(context) - withContext(Dispatchers.Main) { - onDoneRunnable?.run() - } - return@launch - } - if (force) { - // Un-register first - unregister(pushersManager) - } - // the !force should not be needed - if (!force && UnifiedPush.getDistributor(context).isNotEmpty()) { - UnifiedPush.registerApp(context) - withContext(Dispatchers.Main) { - onDoneRunnable?.run() - } - return@launch - } - - val distributors = UnifiedPush.getDistributors(context) - - if (!force && distributors.size == 1) { - UnifiedPush.saveDistributor(context, distributors.first()) - UnifiedPush.registerApp(context) - withContext(Dispatchers.Main) { - onDoneRunnable?.run() - } - } else { - openDistributorDialogInternal( - activity = activity, - onDoneRunnable = onDoneRunnable, - distributors = distributors - ) - } - } - } - - // TODO remove - // There is no case where this function is called - // with a saved distributor and/or a pusher - private fun openDistributorDialogInternal( - activity: FragmentActivity, - onDoneRunnable: Runnable?, - distributors: List - ) { - val internalDistributorName = stringProvider.getString( - if (fcmHelper.isFirebaseAvailable()) { - R.string.unifiedpush_distributor_fcm_fallback - } else { - R.string.unifiedpush_distributor_background_sync - } - ) - - val distributorsName = distributors.map { - if (it == context.packageName) { - internalDistributorName - } else { - context.getApplicationLabel(it) - } - } - - MaterialAlertDialogBuilder(activity) - .setTitle(stringProvider.getString(R.string.unifiedpush_getdistributors_dialog_title)) - .setItems(distributorsName.toTypedArray()) { _, which -> - val distributor = distributors[which] - - activity.lifecycleScope.launch { - UnifiedPush.saveDistributor(context, distributor) - Timber.i("Saving distributor: $distributor") - UnifiedPush.registerApp(context) - onDoneRunnable?.run() - } - } - .setOnCancelListener { - // By default, use internal solution (fcm/background sync) - UnifiedPush.saveDistributor(context, context.packageName) - UnifiedPush.registerApp(context) - onDoneRunnable?.run() - } - .setCancelable(true) - .show() - } - @MainThread fun showSelectDistributorDialog( context: Context, @@ -217,23 +79,6 @@ class UnifiedPushHelper @Inject constructor( .show() } - // TODO remove - suspend fun unregister(pushersManager: PushersManager? = null) { - val mode = BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_REALTIME - vectorPreferences.setFdroidSyncBackgroundMode(mode) - try { - getEndpointOrToken()?.let { - Timber.d("Removing $it") - pushersManager?.unregisterPusher(it) - } - } catch (e: Exception) { - Timber.d(e, "Probably unregistering a non existing pusher") - } - unifiedPushStore.storeUpEndpoint(null) - unifiedPushStore.storePushGateway(null) - UnifiedPush.unregisterApp(context) - } - @JsonClass(generateAdapter = true) internal data class DiscoveryResponse( @Json(name = "unifiedpush") val unifiedpush: DiscoveryUnifiedPush = DiscoveryUnifiedPush() From aa3a808d2cc670301b5d1920d532b30b85be5b48 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 30 Nov 2022 14:42:56 +0100 Subject: [PATCH 044/108] Do not ask to select push distributor in home if notifications are disabled --- .../im/vector/app/features/home/HomeActivityViewModel.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt index 7ffc46218c..cf4bce12f0 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt @@ -116,7 +116,7 @@ class HomeActivityViewModel @AssistedInject constructor( private fun initialize() { if (isInitialized) return isInitialized = true - registerUnifiedPush(distributor = "") + registerUnifiedPushIfNeeded() cleanupFiles() observeInitialSync() checkSessionPushIsOn() @@ -127,6 +127,12 @@ class HomeActivityViewModel @AssistedInject constructor( viewModelScope.launch { stopOngoingVoiceBroadcastUseCase.execute() } } + private fun registerUnifiedPushIfNeeded() { + if(vectorPreferences.areNotificationEnabledForDevice()) { + registerUnifiedPush(distributor = "") + } + } + private fun registerUnifiedPush(distributor: String) { viewModelScope.launch { when (registerUnifiedPushUseCase.execute(distributor = distributor)) { From a3815d70128e35639b2b5b020d96db8927306267 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 30 Nov 2022 15:12:07 +0100 Subject: [PATCH 045/108] Update unit tests --- ...leNotificationsForCurrentSessionUseCase.kt | 1 - ...leNotificationsForCurrentSessionUseCase.kt | 1 - ...tificationsForCurrentSessionUseCaseTest.kt | 12 +-- ...tificationsForCurrentSessionUseCaseTest.kt | 88 +++++++++++-------- .../im/vector/app/test/fakes/FakeFcmHelper.kt | 11 +-- .../app/test/fakes/FakeUnifiedPushHelper.kt | 23 ----- 6 files changed, 63 insertions(+), 73 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCase.kt index 2ce2254f2e..84d92c4291 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCase.kt @@ -31,7 +31,6 @@ class DisableNotificationsForCurrentSessionUseCase @Inject constructor( private val unregisterUnifiedPushUseCase: UnregisterUnifiedPushUseCase, ) { - // TODO update unit tests suspend fun execute() { val session = activeSessionHolder.getSafeActiveSession() ?: return val deviceId = session.sessionParams.deviceId ?: return diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt index e0b0a872f8..99fb249384 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt @@ -37,7 +37,6 @@ class EnableNotificationsForCurrentSessionUseCase @Inject constructor( object NeedToAskUserForDistributor : EnableNotificationsResult } - // TODO update unit tests suspend fun execute(distributor: String = ""): EnableNotificationsResult { val pusherForCurrentSession = pushersManager.getPusherForCurrentSession() if (pusherForCurrentSession == null) { diff --git a/vector/src/test/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCaseTest.kt index e460413a39..386e68ee3a 100644 --- a/vector/src/test/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCaseTest.kt @@ -16,11 +16,11 @@ package im.vector.app.features.settings.notifications +import im.vector.app.core.pushers.UnregisterUnifiedPushUseCase import im.vector.app.features.settings.devices.v2.notification.CheckIfCanTogglePushNotificationsViaPusherUseCase import im.vector.app.features.settings.devices.v2.notification.TogglePushNotificationUseCase import im.vector.app.test.fakes.FakeActiveSessionHolder import im.vector.app.test.fakes.FakePushersManager -import im.vector.app.test.fakes.FakeUnifiedPushHelper import io.mockk.coJustRun import io.mockk.coVerify import io.mockk.every @@ -33,17 +33,17 @@ private const val A_SESSION_ID = "session-id" class DisableNotificationsForCurrentSessionUseCaseTest { private val fakeActiveSessionHolder = FakeActiveSessionHolder() - private val fakeUnifiedPushHelper = FakeUnifiedPushHelper() private val fakePushersManager = FakePushersManager() private val fakeCheckIfCanTogglePushNotificationsViaPusherUseCase = mockk() private val fakeTogglePushNotificationUseCase = mockk() + private val fakeUnregisterUnifiedPushUseCase = mockk() private val disableNotificationsForCurrentSessionUseCase = DisableNotificationsForCurrentSessionUseCase( activeSessionHolder = fakeActiveSessionHolder.instance, - unifiedPushHelper = fakeUnifiedPushHelper.instance, pushersManager = fakePushersManager.instance, checkIfCanTogglePushNotificationsViaPusherUseCase = fakeCheckIfCanTogglePushNotificationsViaPusherUseCase, togglePushNotificationUseCase = fakeTogglePushNotificationUseCase, + unregisterUnifiedPushUseCase = fakeUnregisterUnifiedPushUseCase, ) @Test @@ -67,12 +67,14 @@ class DisableNotificationsForCurrentSessionUseCaseTest { val fakeSession = fakeActiveSessionHolder.fakeSession fakeSession.givenSessionId(A_SESSION_ID) every { fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute(fakeSession) } returns false - fakeUnifiedPushHelper.givenUnregister(fakePushersManager.instance) + coJustRun { fakeUnregisterUnifiedPushUseCase.execute(any()) } // When disableNotificationsForCurrentSessionUseCase.execute() // Then - fakeUnifiedPushHelper.verifyUnregister(fakePushersManager.instance) + coVerify { + fakeUnregisterUnifiedPushUseCase.execute(fakePushersManager.instance) + } } } diff --git a/vector/src/test/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCaseTest.kt index eb6629cb13..c923f0c7d6 100644 --- a/vector/src/test/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCaseTest.kt @@ -16,18 +16,18 @@ package im.vector.app.features.settings.notifications -import androidx.fragment.app.FragmentActivity -import im.vector.app.features.settings.devices.v2.notification.CheckIfCanTogglePushNotificationsViaPusherUseCase +import im.vector.app.core.pushers.EnsureFcmTokenIsRetrievedUseCase +import im.vector.app.core.pushers.RegisterUnifiedPushUseCase import im.vector.app.features.settings.devices.v2.notification.TogglePushNotificationUseCase import im.vector.app.test.fakes.FakeActiveSessionHolder -import im.vector.app.test.fakes.FakeFcmHelper import im.vector.app.test.fakes.FakePushersManager -import im.vector.app.test.fakes.FakeUnifiedPushHelper import io.mockk.coJustRun -import io.mockk.coVerify import io.mockk.every +import io.mockk.justRun import io.mockk.mockk +import io.mockk.verify import kotlinx.coroutines.test.runTest +import org.amshove.kluent.shouldBe import org.junit.Test private const val A_SESSION_ID = "session-id" @@ -35,53 +35,71 @@ private const val A_SESSION_ID = "session-id" class EnableNotificationsForCurrentSessionUseCaseTest { private val fakeActiveSessionHolder = FakeActiveSessionHolder() - private val fakeUnifiedPushHelper = FakeUnifiedPushHelper() private val fakePushersManager = FakePushersManager() - private val fakeFcmHelper = FakeFcmHelper() - private val fakeCheckIfCanTogglePushNotificationsViaPusherUseCase = mockk() private val fakeTogglePushNotificationUseCase = mockk() + private val fakeRegisterUnifiedPushUseCase = mockk() + private val fakeEnsureFcmTokenIsRetrievedUseCase = mockk() private val enableNotificationsForCurrentSessionUseCase = EnableNotificationsForCurrentSessionUseCase( activeSessionHolder = fakeActiveSessionHolder.instance, - unifiedPushHelper = fakeUnifiedPushHelper.instance, pushersManager = fakePushersManager.instance, - fcmHelper = fakeFcmHelper.instance, - checkIfCanTogglePushNotificationsViaPusherUseCase = fakeCheckIfCanTogglePushNotificationsViaPusherUseCase, togglePushNotificationUseCase = fakeTogglePushNotificationUseCase, + registerUnifiedPushUseCase = fakeRegisterUnifiedPushUseCase, + ensureFcmTokenIsRetrievedUseCase = fakeEnsureFcmTokenIsRetrievedUseCase, ) @Test - fun `given no existing pusher for current session when execute then a new pusher is registered`() = runTest { + fun `given no existing pusher and a registered distributor when execute then a new pusher is registered and result is success`() = runTest { // Given - val fragmentActivity = mockk() - fakePushersManager.givenGetPusherForCurrentSessionReturns(null) - fakeUnifiedPushHelper.givenRegister(fragmentActivity) - fakeUnifiedPushHelper.givenIsEmbeddedDistributorReturns(true) - fakeFcmHelper.givenEnsureFcmTokenIsRetrieved(fragmentActivity, fakePushersManager.instance) - every { fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute(fakeActiveSessionHolder.fakeSession) } returns false - - // When - enableNotificationsForCurrentSessionUseCase.execute(fragmentActivity) - - // Then - fakeUnifiedPushHelper.verifyRegister(fragmentActivity) - fakeFcmHelper.verifyEnsureFcmTokenIsRetrieved(fragmentActivity, fakePushersManager.instance, registerPusher = true) - } - - @Test - fun `given toggle via Pusher is possible when execute then current pusher is toggled to true`() = runTest { - // Given - val fragmentActivity = mockk() - fakePushersManager.givenGetPusherForCurrentSessionReturns(mockk()) + val aDistributor = "distributor" val fakeSession = fakeActiveSessionHolder.fakeSession fakeSession.givenSessionId(A_SESSION_ID) - every { fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute(fakeActiveSessionHolder.fakeSession) } returns true + fakePushersManager.givenGetPusherForCurrentSessionReturns(null) + every { fakeRegisterUnifiedPushUseCase.execute(any()) } returns RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.Success + justRun { fakeEnsureFcmTokenIsRetrievedUseCase.execute(any(), any()) } coJustRun { fakeTogglePushNotificationUseCase.execute(A_SESSION_ID, any()) } // When - enableNotificationsForCurrentSessionUseCase.execute(fragmentActivity) + val result = enableNotificationsForCurrentSessionUseCase.execute(aDistributor) // Then - coVerify { fakeTogglePushNotificationUseCase.execute(A_SESSION_ID, true) } + result shouldBe EnableNotificationsForCurrentSessionUseCase.EnableNotificationsResult.Success + verify { + fakeRegisterUnifiedPushUseCase.execute(aDistributor) + fakeEnsureFcmTokenIsRetrievedUseCase.execute(fakePushersManager.instance, registerPusher = true) + } + } + + @Test + fun `given no existing pusher and a no registered distributor when execute then result is need to ask user for distributor`() = runTest { + // Given + val aDistributor = "distributor" + fakePushersManager.givenGetPusherForCurrentSessionReturns(null) + every { fakeRegisterUnifiedPushUseCase.execute(any()) } returns RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.NeedToAskUserForDistributor + + // When + val result = enableNotificationsForCurrentSessionUseCase.execute(aDistributor) + + // Then + result shouldBe EnableNotificationsForCurrentSessionUseCase.EnableNotificationsResult.NeedToAskUserForDistributor + verify { + fakeRegisterUnifiedPushUseCase.execute(aDistributor) + } + } + + @Test + fun `given no deviceId for current session when execute then result is failure`() = runTest { + // Given + val aDistributor = "distributor" + val fakeSession = fakeActiveSessionHolder.fakeSession + fakeSession.givenSessionId(null) + fakePushersManager.givenGetPusherForCurrentSessionReturns(mockk()) + every { fakeRegisterUnifiedPushUseCase.execute(any()) } returns RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.NeedToAskUserForDistributor + + // When + val result = enableNotificationsForCurrentSessionUseCase.execute(aDistributor) + + // Then + result shouldBe EnableNotificationsForCurrentSessionUseCase.EnableNotificationsResult.Failure } } diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeFcmHelper.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeFcmHelper.kt index 11abf18794..07eef36dc1 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeFcmHelper.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeFcmHelper.kt @@ -16,7 +16,6 @@ package im.vector.app.test.fakes -import androidx.fragment.app.FragmentActivity import im.vector.app.core.pushers.FcmHelper import im.vector.app.core.pushers.PushersManager import io.mockk.justRun @@ -27,18 +26,14 @@ class FakeFcmHelper { val instance = mockk() - fun givenEnsureFcmTokenIsRetrieved( - fragmentActivity: FragmentActivity, - pushersManager: PushersManager, - ) { - justRun { instance.ensureFcmTokenIsRetrieved(fragmentActivity, pushersManager, any()) } + fun givenEnsureFcmTokenIsRetrieved(pushersManager: PushersManager) { + justRun { instance.ensureFcmTokenIsRetrieved(pushersManager, any()) } } fun verifyEnsureFcmTokenIsRetrieved( - fragmentActivity: FragmentActivity, pushersManager: PushersManager, registerPusher: Boolean, ) { - verify { instance.ensureFcmTokenIsRetrieved(fragmentActivity, pushersManager, registerPusher) } + verify { instance.ensureFcmTokenIsRetrieved(pushersManager, registerPusher) } } } diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeUnifiedPushHelper.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeUnifiedPushHelper.kt index 1f2cc8a1ce..5bc57ead07 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeUnifiedPushHelper.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeUnifiedPushHelper.kt @@ -16,37 +16,14 @@ package im.vector.app.test.fakes -import androidx.fragment.app.FragmentActivity -import im.vector.app.core.pushers.PushersManager import im.vector.app.core.pushers.UnifiedPushHelper -import io.mockk.coJustRun -import io.mockk.coVerify import io.mockk.every import io.mockk.mockk -import io.mockk.verify class FakeUnifiedPushHelper { val instance = mockk() - fun givenRegister(fragmentActivity: FragmentActivity) { - every { instance.register(fragmentActivity, any()) } answers { - secondArg().run() - } - } - - fun verifyRegister(fragmentActivity: FragmentActivity) { - verify { instance.register(fragmentActivity, any()) } - } - - fun givenUnregister(pushersManager: PushersManager) { - coJustRun { instance.unregister(pushersManager) } - } - - fun verifyUnregister(pushersManager: PushersManager) { - coVerify { instance.unregister(pushersManager) } - } - fun givenIsEmbeddedDistributorReturns(isEmbedded: Boolean) { every { instance.isEmbeddedDistributor() } returns isEmbedded } From 46ccf4d73fea7909b64999f7d0786e2684b33e4c Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 30 Nov 2022 16:02:55 +0100 Subject: [PATCH 046/108] Adding unit tests for register and unregister use cases --- .../pushers/RegisterUnifiedPushUseCase.kt | 1 - .../pushers/UnregisterUnifiedPushUseCase.kt | 1 - .../pushers/RegisterUnifiedPushUseCaseTest.kt | 158 ++++++++++++++++++ .../UnregisterUnifiedPushUseCaseTest.kt | 83 +++++++++ .../im/vector/app/test/fakes/FakeContext.kt | 4 + .../app/test/fakes/FakePushersManager.kt | 10 ++ .../app/test/fakes/FakeUnifiedPushHelper.kt | 4 + .../app/test/fakes/FakeUnifiedPushStore.kt | 43 +++++ .../app/test/fakes/FakeVectorFeatures.kt | 4 + .../app/test/fakes/FakeVectorPreferences.kt | 9 + 10 files changed, 315 insertions(+), 2 deletions(-) create mode 100644 vector/src/test/java/im/vector/app/core/pushers/RegisterUnifiedPushUseCaseTest.kt create mode 100644 vector/src/test/java/im/vector/app/core/pushers/UnregisterUnifiedPushUseCaseTest.kt create mode 100644 vector/src/test/java/im/vector/app/test/fakes/FakeUnifiedPushStore.kt diff --git a/vector/src/main/java/im/vector/app/core/pushers/RegisterUnifiedPushUseCase.kt b/vector/src/main/java/im/vector/app/core/pushers/RegisterUnifiedPushUseCase.kt index 58bf0f5050..aa3652a54f 100644 --- a/vector/src/main/java/im/vector/app/core/pushers/RegisterUnifiedPushUseCase.kt +++ b/vector/src/main/java/im/vector/app/core/pushers/RegisterUnifiedPushUseCase.kt @@ -32,7 +32,6 @@ class RegisterUnifiedPushUseCase @Inject constructor( object NeedToAskUserForDistributor : RegisterUnifiedPushResult } - // TODO add unit tests fun execute(distributor: String = ""): RegisterUnifiedPushResult { if (distributor.isNotEmpty()) { saveAndRegisterApp(distributor) diff --git a/vector/src/main/java/im/vector/app/core/pushers/UnregisterUnifiedPushUseCase.kt b/vector/src/main/java/im/vector/app/core/pushers/UnregisterUnifiedPushUseCase.kt index 71b1a9c033..acad3e649f 100644 --- a/vector/src/main/java/im/vector/app/core/pushers/UnregisterUnifiedPushUseCase.kt +++ b/vector/src/main/java/im/vector/app/core/pushers/UnregisterUnifiedPushUseCase.kt @@ -31,7 +31,6 @@ class UnregisterUnifiedPushUseCase @Inject constructor( private val unifiedPushHelper: UnifiedPushHelper, ) { - // TODO add unit tests suspend fun execute(pushersManager: PushersManager?) { val mode = BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_REALTIME vectorPreferences.setFdroidSyncBackgroundMode(mode) diff --git a/vector/src/test/java/im/vector/app/core/pushers/RegisterUnifiedPushUseCaseTest.kt b/vector/src/test/java/im/vector/app/core/pushers/RegisterUnifiedPushUseCaseTest.kt new file mode 100644 index 0000000000..c72c519172 --- /dev/null +++ b/vector/src/test/java/im/vector/app/core/pushers/RegisterUnifiedPushUseCaseTest.kt @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.core.pushers + +import im.vector.app.test.fakes.FakeContext +import im.vector.app.test.fakes.FakeVectorFeatures +import io.mockk.every +import io.mockk.justRun +import io.mockk.mockkStatic +import io.mockk.unmockkAll +import io.mockk.verify +import io.mockk.verifyAll +import io.mockk.verifyOrder +import kotlinx.coroutines.test.runTest +import org.amshove.kluent.shouldBe +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.unifiedpush.android.connector.UnifiedPush + +class RegisterUnifiedPushUseCaseTest { + + private val fakeContext = FakeContext() + private val fakeVectorFeatures = FakeVectorFeatures() + + private val registerUnifiedPushUseCase = RegisterUnifiedPushUseCase( + context = fakeContext.instance, + vectorFeatures = fakeVectorFeatures, + ) + + @Before + fun setup() { + mockkStatic(UnifiedPush::class) + } + + @After + fun tearDown() { + unmockkAll() + } + + @Test + fun `given non empty distributor when execute then distributor is saved and app is registered`() = runTest { + // Given + val aDistributor = "distributor" + justRun { UnifiedPush.registerApp(any()) } + justRun { UnifiedPush.saveDistributor(any(), any()) } + + // When + val result = registerUnifiedPushUseCase.execute(aDistributor) + + // Then + result shouldBe RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.Success + verifyOrder { + UnifiedPush.saveDistributor(fakeContext.instance, aDistributor) + UnifiedPush.registerApp(fakeContext.instance) + } + } + + @Test + fun `given external distributors are not allowed when execute then internal distributor is saved and app is registered`() = runTest { + // Given + val aPackageName = "packageName" + fakeContext.givenPackageName(aPackageName) + justRun { UnifiedPush.registerApp(any()) } + justRun { UnifiedPush.saveDistributor(any(), any()) } + fakeVectorFeatures.givenExternalDistributorsAreAllowed(false) + + // When + val result = registerUnifiedPushUseCase.execute() + + // Then + result shouldBe RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.Success + verifyOrder { + UnifiedPush.saveDistributor(fakeContext.instance, aPackageName) + UnifiedPush.registerApp(fakeContext.instance) + } + } + + @Test + fun `given a saved distributor and external distributors are allowed when execute then app is registered`() = runTest { + // Given + justRun { UnifiedPush.registerApp(any()) } + val aDistributor = "distributor" + every { UnifiedPush.getDistributor(any()) } returns aDistributor + fakeVectorFeatures.givenExternalDistributorsAreAllowed(true) + + // When + val result = registerUnifiedPushUseCase.execute() + + // Then + result shouldBe RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.Success + verifyAll { + UnifiedPush.getDistributor(fakeContext.instance) + UnifiedPush.registerApp(fakeContext.instance) + } + } + + @Test + fun `given no saved distributor and a unique distributor available when execute then the distributor is saved and app is registered`() = runTest { + // Given + justRun { UnifiedPush.registerApp(any()) } + justRun { UnifiedPush.saveDistributor(any(), any()) } + every { UnifiedPush.getDistributor(any()) } returns "" + fakeVectorFeatures.givenExternalDistributorsAreAllowed(true) + val aDistributor = "distributor" + every { UnifiedPush.getDistributors(any()) } returns listOf(aDistributor) + + // When + val result = registerUnifiedPushUseCase.execute() + + // Then + result shouldBe RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.Success + verifyOrder { + UnifiedPush.getDistributor(fakeContext.instance) + UnifiedPush.getDistributors(fakeContext.instance) + UnifiedPush.saveDistributor(fakeContext.instance, aDistributor) + UnifiedPush.registerApp(fakeContext.instance) + } + } + + @Test + fun `given no saved distributor and multiple distributors available when execute then result is to ask user`() = runTest { + // Given + every { UnifiedPush.getDistributor(any()) } returns "" + fakeVectorFeatures.givenExternalDistributorsAreAllowed(true) + val aDistributor1 = "distributor1" + val aDistributor2 = "distributor2" + every { UnifiedPush.getDistributors(any()) } returns listOf(aDistributor1, aDistributor2) + + // When + val result = registerUnifiedPushUseCase.execute() + + // Then + result shouldBe RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.NeedToAskUserForDistributor + verifyOrder { + UnifiedPush.getDistributor(fakeContext.instance) + UnifiedPush.getDistributors(fakeContext.instance) + } + verify(inverse = true) { + UnifiedPush.saveDistributor(any(), any()) + UnifiedPush.registerApp(any()) + } + } +} diff --git a/vector/src/test/java/im/vector/app/core/pushers/UnregisterUnifiedPushUseCaseTest.kt b/vector/src/test/java/im/vector/app/core/pushers/UnregisterUnifiedPushUseCaseTest.kt new file mode 100644 index 0000000000..bee545b3e1 --- /dev/null +++ b/vector/src/test/java/im/vector/app/core/pushers/UnregisterUnifiedPushUseCaseTest.kt @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.core.pushers + +import im.vector.app.features.settings.BackgroundSyncMode +import im.vector.app.test.fakes.FakeContext +import im.vector.app.test.fakes.FakePushersManager +import im.vector.app.test.fakes.FakeUnifiedPushHelper +import im.vector.app.test.fakes.FakeUnifiedPushStore +import im.vector.app.test.fakes.FakeVectorPreferences +import io.mockk.justRun +import io.mockk.mockkStatic +import io.mockk.unmockkAll +import io.mockk.verifyAll +import kotlinx.coroutines.test.runTest +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.unifiedpush.android.connector.UnifiedPush + +class UnregisterUnifiedPushUseCaseTest { + + private val fakeContext = FakeContext() + private val fakeVectorPreferences = FakeVectorPreferences() + private val fakeUnifiedPushStore = FakeUnifiedPushStore() + private val fakeUnifiedPushHelper = FakeUnifiedPushHelper() + + private val unregisterUnifiedPushUseCase = UnregisterUnifiedPushUseCase( + context = fakeContext.instance, + vectorPreferences = fakeVectorPreferences.instance, + unifiedPushStore = fakeUnifiedPushStore.instance, + unifiedPushHelper = fakeUnifiedPushHelper.instance, + ) + + @Before + fun setup() { + mockkStatic(UnifiedPush::class) + } + + @After + fun tearDown() { + unmockkAll() + } + + @Test + fun `given pushersManager when execute then unregister and clean everything which is needed`() = runTest { + // Given + val aEndpoint = "endpoint" + fakeUnifiedPushHelper.givenGetEndpointOrTokenReturns(aEndpoint) + val aPushersManager = FakePushersManager() + aPushersManager.givenUnregisterPusher(aEndpoint) + justRun { UnifiedPush.unregisterApp(any()) } + fakeVectorPreferences.givenSetFdroidSyncBackgroundMode(BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_REALTIME) + fakeUnifiedPushStore.givenStorePushGateway(null) + fakeUnifiedPushStore.givenStoreUpEndpoint(null) + + // When + unregisterUnifiedPushUseCase.execute(aPushersManager.instance) + + // Then + fakeVectorPreferences.verifySetFdroidSyncBackgroundMode(BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_REALTIME) + aPushersManager.verifyUnregisterPusher(aEndpoint) + verifyAll { + UnifiedPush.unregisterApp(fakeContext.instance) + } + fakeUnifiedPushStore.verifyStorePushGateway(null) + fakeUnifiedPushStore.verifyStoreUpEndpoint(null) + } +} diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeContext.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeContext.kt index 9a94313fec..f8c568e908 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeContext.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeContext.kt @@ -81,4 +81,8 @@ class FakeContext( givenService(Context.CLIPBOARD_SERVICE, ClipboardManager::class.java, fakeClipboardManager.instance) return fakeClipboardManager } + + fun givenPackageName(name: String) { + every { instance.packageName } returns name + } } diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakePushersManager.kt b/vector/src/test/java/im/vector/app/test/fakes/FakePushersManager.kt index 46d852f4f8..3dd3854a18 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakePushersManager.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakePushersManager.kt @@ -17,6 +17,8 @@ package im.vector.app.test.fakes import im.vector.app.core.pushers.PushersManager +import io.mockk.coJustRun +import io.mockk.coVerify import io.mockk.every import io.mockk.mockk import org.matrix.android.sdk.api.session.pushers.Pusher @@ -28,4 +30,12 @@ class FakePushersManager { fun givenGetPusherForCurrentSessionReturns(pusher: Pusher?) { every { instance.getPusherForCurrentSession() } returns pusher } + + fun givenUnregisterPusher(pushKey: String) { + coJustRun { instance.unregisterPusher(pushKey) } + } + + fun verifyUnregisterPusher(pushKey: String) { + coVerify { instance.unregisterPusher(pushKey) } + } } diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeUnifiedPushHelper.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeUnifiedPushHelper.kt index 5bc57ead07..99b5b75874 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeUnifiedPushHelper.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeUnifiedPushHelper.kt @@ -27,4 +27,8 @@ class FakeUnifiedPushHelper { fun givenIsEmbeddedDistributorReturns(isEmbedded: Boolean) { every { instance.isEmbeddedDistributor() } returns isEmbedded } + + fun givenGetEndpointOrTokenReturns(endpoint: String?) { + every { instance.getEndpointOrToken() } returns endpoint + } } diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeUnifiedPushStore.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeUnifiedPushStore.kt new file mode 100644 index 0000000000..9b09bec688 --- /dev/null +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeUnifiedPushStore.kt @@ -0,0 +1,43 @@ +/* + * 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.test.fakes + +import im.vector.app.core.pushers.UnifiedPushStore +import io.mockk.justRun +import io.mockk.mockk +import io.mockk.verify + +class FakeUnifiedPushStore { + + val instance = mockk() + + fun givenStoreUpEndpoint(endpoint: String?) { + justRun { instance.storeUpEndpoint(endpoint) } + } + + fun verifyStoreUpEndpoint(endpoint: String?) { + verify { instance.storeUpEndpoint(endpoint) } + } + + fun givenStorePushGateway(gateway: String?) { + justRun { instance.storePushGateway(gateway) } + } + + fun verifyStorePushGateway(gateway: String?) { + verify { instance.storePushGateway(gateway) } + } +} diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeVectorFeatures.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeVectorFeatures.kt index c3c2fa684f..b399f0baa4 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeVectorFeatures.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeVectorFeatures.kt @@ -54,4 +54,8 @@ class FakeVectorFeatures : VectorFeatures by spyk() { fun givenUnverifiedSessionsAlertEnabled(isEnabled: Boolean) { every { isUnverifiedSessionsAlertEnabled() } returns isEnabled } + + fun givenExternalDistributorsAreAllowed(allowed: Boolean) { + every { allowExternalUnifiedPushDistributors() } returns allowed + } } diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt index 77df3ffc7a..7970c14e90 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt @@ -16,6 +16,7 @@ package im.vector.app.test.fakes +import im.vector.app.features.settings.BackgroundSyncMode import im.vector.app.features.settings.VectorPreferences import io.mockk.every import io.mockk.justRun @@ -60,4 +61,12 @@ class FakeVectorPreferences { fun givenUnverifiedSessionsAlertLastShownMillis(lastShownMillis: Long) { every { instance.getUnverifiedSessionsAlertLastShownMillis(any()) } returns lastShownMillis } + + fun givenSetFdroidSyncBackgroundMode(mode: BackgroundSyncMode) { + justRun { instance.setFdroidSyncBackgroundMode(mode) } + } + + fun verifySetFdroidSyncBackgroundMode(mode: BackgroundSyncMode) { + verify { instance.setFdroidSyncBackgroundMode(mode) } + } } From 2a8c72bdcf60409b78e51fad4af53c49bcacc5d4 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 30 Nov 2022 16:05:25 +0100 Subject: [PATCH 047/108] Fixing code style issues --- .../java/im/vector/app/core/di/MavericksViewModelModule.kt | 4 +++- vector/src/main/java/im/vector/app/core/pushers/FcmHelper.kt | 1 - .../java/im/vector/app/features/home/HomeActivityViewModel.kt | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt index ad3e361775..b58d584dad 100644 --- a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt @@ -688,5 +688,7 @@ interface MavericksViewModelModule { @Binds @IntoMap @MavericksViewModelKey(VectorSettingsNotificationPreferenceViewModel::class) - fun vectorSettingsNotificationPreferenceViewModelFactory(factory: VectorSettingsNotificationPreferenceViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + fun vectorSettingsNotificationPreferenceViewModelFactory( + factory: VectorSettingsNotificationPreferenceViewModel.Factory + ): MavericksAssistedViewModelFactory<*, *> } diff --git a/vector/src/main/java/im/vector/app/core/pushers/FcmHelper.kt b/vector/src/main/java/im/vector/app/core/pushers/FcmHelper.kt index 0cc251ce31..381348638d 100644 --- a/vector/src/main/java/im/vector/app/core/pushers/FcmHelper.kt +++ b/vector/src/main/java/im/vector/app/core/pushers/FcmHelper.kt @@ -16,7 +16,6 @@ package im.vector.app.core.pushers -import android.app.Activity import im.vector.app.core.di.ActiveSessionHolder interface FcmHelper { diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt index cf4bce12f0..26034fc09c 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt @@ -128,7 +128,7 @@ class HomeActivityViewModel @AssistedInject constructor( } private fun registerUnifiedPushIfNeeded() { - if(vectorPreferences.areNotificationEnabledForDevice()) { + if (vectorPreferences.areNotificationEnabledForDevice()) { registerUnifiedPush(distributor = "") } } From e78e19285344a9b68db8542226dcacac56c110de Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 30 Nov 2022 16:37:15 +0100 Subject: [PATCH 048/108] Adding unit tests for FCM token retrieval --- .../EnsureFcmTokenIsRetrievedUseCase.kt | 1 - .../EnsureFcmTokenIsRetrievedUseCaseTest.kt | 106 ++++++++++++++++++ .../im/vector/app/test/fakes/FakeFcmHelper.kt | 3 +- 3 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 vector/src/test/java/im/vector/app/core/pushers/EnsureFcmTokenIsRetrievedUseCaseTest.kt diff --git a/vector/src/main/java/im/vector/app/core/pushers/EnsureFcmTokenIsRetrievedUseCase.kt b/vector/src/main/java/im/vector/app/core/pushers/EnsureFcmTokenIsRetrievedUseCase.kt index e55d0426ba..cb955e01f7 100644 --- a/vector/src/main/java/im/vector/app/core/pushers/EnsureFcmTokenIsRetrievedUseCase.kt +++ b/vector/src/main/java/im/vector/app/core/pushers/EnsureFcmTokenIsRetrievedUseCase.kt @@ -25,7 +25,6 @@ class EnsureFcmTokenIsRetrievedUseCase @Inject constructor( private val activeSessionHolder: ActiveSessionHolder, ) { - // TODO add unit tests fun execute(pushersManager: PushersManager, registerPusher: Boolean) { if (unifiedPushHelper.isEmbeddedDistributor()) { fcmHelper.ensureFcmTokenIsRetrieved(pushersManager, shouldAddHttpPusher(registerPusher)) diff --git a/vector/src/test/java/im/vector/app/core/pushers/EnsureFcmTokenIsRetrievedUseCaseTest.kt b/vector/src/test/java/im/vector/app/core/pushers/EnsureFcmTokenIsRetrievedUseCaseTest.kt new file mode 100644 index 0000000000..03a43a5b55 --- /dev/null +++ b/vector/src/test/java/im/vector/app/core/pushers/EnsureFcmTokenIsRetrievedUseCaseTest.kt @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.core.pushers + +import im.vector.app.test.fakes.FakeActiveSessionHolder +import im.vector.app.test.fakes.FakeFcmHelper +import im.vector.app.test.fakes.FakePushersManager +import im.vector.app.test.fakes.FakeUnifiedPushHelper +import im.vector.app.test.fixtures.PusherFixture +import io.mockk.verify +import org.junit.Test + +class EnsureFcmTokenIsRetrievedUseCaseTest { + + private val fakeUnifiedPushHelper = FakeUnifiedPushHelper() + private val fakeFcmHelper = FakeFcmHelper() + private val fakeActiveSessionHolder = FakeActiveSessionHolder() + + private val ensureFcmTokenIsRetrievedUseCase = EnsureFcmTokenIsRetrievedUseCase( + unifiedPushHelper = fakeUnifiedPushHelper.instance, + fcmHelper = fakeFcmHelper.instance, + activeSessionHolder = fakeActiveSessionHolder.instance, + ) + + @Test + fun `given no registered pusher and distributor as embedded when execute then ensure the FCM token is retrieved with register pusher option`() { + // Given + val aPushersManager = FakePushersManager() + fakeUnifiedPushHelper.givenIsEmbeddedDistributorReturns(true) + fakeFcmHelper.givenEnsureFcmTokenIsRetrieved(aPushersManager.instance) + val aSessionId = "aSessionId" + fakeActiveSessionHolder.fakeSession.givenSessionId(aSessionId) + val expectedPusher = PusherFixture.aPusher(deviceId = "") + fakeActiveSessionHolder.fakeSession.fakePushersService.givenGetPushers(listOf(expectedPusher)) + + // When + ensureFcmTokenIsRetrievedUseCase.execute(aPushersManager.instance, registerPusher = true) + + // Then + fakeFcmHelper.verifyEnsureFcmTokenIsRetrieved(aPushersManager.instance, registerPusher = true) + } + + @Test + fun `given a registered pusher and distributor as embedded when execute then ensure the FCM token is retrieved without register pusher option`() { + // Given + val aPushersManager = FakePushersManager() + fakeUnifiedPushHelper.givenIsEmbeddedDistributorReturns(true) + fakeFcmHelper.givenEnsureFcmTokenIsRetrieved(aPushersManager.instance) + val aSessionId = "aSessionId" + fakeActiveSessionHolder.fakeSession.givenSessionId(aSessionId) + val expectedPusher = PusherFixture.aPusher(deviceId = aSessionId) + fakeActiveSessionHolder.fakeSession.fakePushersService.givenGetPushers(listOf(expectedPusher)) + + // When + ensureFcmTokenIsRetrievedUseCase.execute(aPushersManager.instance, registerPusher = true) + + // Then + fakeFcmHelper.verifyEnsureFcmTokenIsRetrieved(aPushersManager.instance, registerPusher = false) + } + + @Test + fun `given no registering asked and distributor as embedded when execute then ensure the FCM token is retrieved without register pusher option`() { + // Given + val aPushersManager = FakePushersManager() + fakeUnifiedPushHelper.givenIsEmbeddedDistributorReturns(true) + fakeFcmHelper.givenEnsureFcmTokenIsRetrieved(aPushersManager.instance) + val aSessionId = "aSessionId" + fakeActiveSessionHolder.fakeSession.givenSessionId(aSessionId) + val expectedPusher = PusherFixture.aPusher(deviceId = aSessionId) + fakeActiveSessionHolder.fakeSession.fakePushersService.givenGetPushers(listOf(expectedPusher)) + + // When + ensureFcmTokenIsRetrievedUseCase.execute(aPushersManager.instance, registerPusher = false) + + // Then + fakeFcmHelper.verifyEnsureFcmTokenIsRetrieved(aPushersManager.instance, registerPusher = false) + } + + @Test + fun `given distributor as not embedded when execute then nothing is done`() { + // Given + val aPushersManager = FakePushersManager() + fakeUnifiedPushHelper.givenIsEmbeddedDistributorReturns(false) + + // When + ensureFcmTokenIsRetrievedUseCase.execute(aPushersManager.instance, registerPusher = true) + + // Then + fakeFcmHelper.verifyEnsureFcmTokenIsRetrieved(aPushersManager.instance, registerPusher = true, inverse = true) + fakeFcmHelper.verifyEnsureFcmTokenIsRetrieved(aPushersManager.instance, registerPusher = false, inverse = true) + } +} diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeFcmHelper.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeFcmHelper.kt index 07eef36dc1..4c210215ec 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeFcmHelper.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeFcmHelper.kt @@ -33,7 +33,8 @@ class FakeFcmHelper { fun verifyEnsureFcmTokenIsRetrieved( pushersManager: PushersManager, registerPusher: Boolean, + inverse: Boolean = false, ) { - verify { instance.ensureFcmTokenIsRetrieved(pushersManager, registerPusher) } + verify(inverse = inverse) { instance.ensureFcmTokenIsRetrieved(pushersManager, registerPusher) } } } From d31652e91007e67f6ea5a8065c6f032af9607e6f Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 30 Nov 2022 17:21:48 +0100 Subject: [PATCH 049/108] Adding unit tests for settings ViewModel --- ...SettingsNotificationPreferenceViewModel.kt | 1 - ...ingsNotificationPreferenceViewModelTest.kt | 202 ++++++++++++++++++ .../app/test/fakes/FakeVectorPreferences.kt | 4 + 3 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 vector/src/test/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModelTest.kt diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModel.kt index 59c26749c9..d6a9c621f2 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModel.kt @@ -49,7 +49,6 @@ class VectorSettingsNotificationPreferenceViewModel @AssistedInject constructor( companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() - // TODO add unit tests override fun handle(action: VectorSettingsNotificationPreferenceViewAction) { when (action) { VectorSettingsNotificationPreferenceViewAction.DisableNotificationsForDevice -> handleDisableNotificationsForDevice() diff --git a/vector/src/test/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModelTest.kt b/vector/src/test/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModelTest.kt new file mode 100644 index 0000000000..f9d7527316 --- /dev/null +++ b/vector/src/test/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModelTest.kt @@ -0,0 +1,202 @@ +/* + * 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.settings.notifications + +import com.airbnb.mvrx.test.MavericksTestRule +import im.vector.app.core.platform.VectorDummyViewState +import im.vector.app.core.pushers.EnsureFcmTokenIsRetrievedUseCase +import im.vector.app.core.pushers.RegisterUnifiedPushUseCase +import im.vector.app.core.pushers.UnregisterUnifiedPushUseCase +import im.vector.app.test.fakes.FakePushersManager +import im.vector.app.test.fakes.FakeVectorPreferences +import im.vector.app.test.test +import im.vector.app.test.testDispatcher +import io.mockk.coEvery +import io.mockk.coJustRun +import io.mockk.coVerify +import io.mockk.coVerifyOrder +import io.mockk.justRun +import io.mockk.mockk +import org.junit.Rule +import org.junit.Test + +class VectorSettingsNotificationPreferenceViewModelTest { + + @get:Rule + val mavericksTestRule = MavericksTestRule(testDispatcher = testDispatcher) + + private val fakePushersManager = FakePushersManager() + private val fakeVectorPreferences = FakeVectorPreferences() + private val fakeEnableNotificationsForCurrentSessionUseCase = mockk() + private val fakeDisableNotificationsForCurrentSessionUseCase = mockk() + private val fakeUnregisterUnifiedPushUseCase = mockk() + private val fakeRegisterUnifiedPushUseCase = mockk() + private val fakeEnsureFcmTokenIsRetrievedUseCase = mockk() + + private fun createViewModel() = VectorSettingsNotificationPreferenceViewModel( + initialState = VectorDummyViewState(), + pushersManager = fakePushersManager.instance, + vectorPreferences = fakeVectorPreferences.instance, + enableNotificationsForCurrentSessionUseCase = fakeEnableNotificationsForCurrentSessionUseCase, + disableNotificationsForCurrentSessionUseCase = fakeDisableNotificationsForCurrentSessionUseCase, + unregisterUnifiedPushUseCase = fakeUnregisterUnifiedPushUseCase, + registerUnifiedPushUseCase = fakeRegisterUnifiedPushUseCase, + ensureFcmTokenIsRetrievedUseCase = fakeEnsureFcmTokenIsRetrievedUseCase, + ) + + @Test + fun `given DisableNotificationsForDevice action when handling action then disable use case is called`() { + // Given + val viewModel = createViewModel() + val action = VectorSettingsNotificationPreferenceViewAction.DisableNotificationsForDevice + coJustRun { fakeDisableNotificationsForCurrentSessionUseCase.execute() } + val expectedEvent = VectorSettingsNotificationPreferenceViewEvent.NotificationsForDeviceDisabled + + // When + val viewModelTest = viewModel.test() + viewModel.handle(action) + + // Then + viewModelTest + .assertEvent { event -> event == expectedEvent } + .finish() + coVerify { + fakeDisableNotificationsForCurrentSessionUseCase.execute() + } + } + + @Test + fun `given EnableNotificationsForDevice action and enable success when handling action then enable use case is called`() { + // Given + val viewModel = createViewModel() + val aDistributor = "aDistributor" + val action = VectorSettingsNotificationPreferenceViewAction.EnableNotificationsForDevice(aDistributor) + coEvery { fakeEnableNotificationsForCurrentSessionUseCase.execute(any()) } returns + EnableNotificationsForCurrentSessionUseCase.EnableNotificationsResult.Success + val expectedEvent = VectorSettingsNotificationPreferenceViewEvent.NotificationsForDeviceEnabled + + // When + val viewModelTest = viewModel.test() + viewModel.handle(action) + + // Then + viewModelTest + .assertEvent { event -> event == expectedEvent } + .finish() + coVerify { + fakeEnableNotificationsForCurrentSessionUseCase.execute(aDistributor) + } + } + + @Test + fun `given EnableNotificationsForDevice action and enable needs user choice when handling action then enable use case is called`() { + // Given + val viewModel = createViewModel() + val aDistributor = "aDistributor" + val action = VectorSettingsNotificationPreferenceViewAction.EnableNotificationsForDevice(aDistributor) + coEvery { fakeEnableNotificationsForCurrentSessionUseCase.execute(any()) } returns + EnableNotificationsForCurrentSessionUseCase.EnableNotificationsResult.NeedToAskUserForDistributor + val expectedEvent = VectorSettingsNotificationPreferenceViewEvent.AskUserForPushDistributor + + // When + val viewModelTest = viewModel.test() + viewModel.handle(action) + + // Then + viewModelTest + .assertEvent { event -> event == expectedEvent } + .finish() + coVerify { + fakeEnableNotificationsForCurrentSessionUseCase.execute(aDistributor) + } + } + + @Test + fun `given EnableNotificationsForDevice action and enable failure when handling action then enable use case is called`() { + // Given + val viewModel = createViewModel() + val aDistributor = "aDistributor" + val action = VectorSettingsNotificationPreferenceViewAction.EnableNotificationsForDevice(aDistributor) + coEvery { fakeEnableNotificationsForCurrentSessionUseCase.execute(any()) } returns + EnableNotificationsForCurrentSessionUseCase.EnableNotificationsResult.Failure + val expectedEvent = VectorSettingsNotificationPreferenceViewEvent.EnableNotificationForDeviceFailure + + // When + val viewModelTest = viewModel.test() + viewModel.handle(action) + + // Then + viewModelTest + .assertEvent { event -> event == expectedEvent } + .finish() + coVerify { + fakeEnableNotificationsForCurrentSessionUseCase.execute(aDistributor) + } + } + + @Test + fun `given RegisterPushDistributor action and register success when handling action then register use case is called`() { + // Given + val viewModel = createViewModel() + val aDistributor = "aDistributor" + val action = VectorSettingsNotificationPreferenceViewAction.RegisterPushDistributor(aDistributor) + coEvery { fakeRegisterUnifiedPushUseCase.execute(any()) } returns RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.Success + coJustRun { fakeUnregisterUnifiedPushUseCase.execute(any()) } + val areNotificationsEnabled = true + fakeVectorPreferences.givenAreNotificationsEnabledForDevice(areNotificationsEnabled) + justRun { fakeEnsureFcmTokenIsRetrievedUseCase.execute(any(), any()) } + val expectedEvent = VectorSettingsNotificationPreferenceViewEvent.NotificationMethodChanged + + // When + val viewModelTest = viewModel.test() + viewModel.handle(action) + + // Then + viewModelTest + .assertEvent { event -> event == expectedEvent } + .finish() + coVerifyOrder { + fakeUnregisterUnifiedPushUseCase.execute(fakePushersManager.instance) + fakeRegisterUnifiedPushUseCase.execute(aDistributor) + fakeEnsureFcmTokenIsRetrievedUseCase.execute(fakePushersManager.instance, registerPusher = areNotificationsEnabled) + } + } + + @Test + fun `given RegisterPushDistributor action and register needs user choice when handling action then register use case is called`() { + // Given + val viewModel = createViewModel() + val aDistributor = "aDistributor" + val action = VectorSettingsNotificationPreferenceViewAction.RegisterPushDistributor(aDistributor) + coEvery { fakeRegisterUnifiedPushUseCase.execute(any()) } returns RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.NeedToAskUserForDistributor + coJustRun { fakeUnregisterUnifiedPushUseCase.execute(any()) } + val expectedEvent = VectorSettingsNotificationPreferenceViewEvent.AskUserForPushDistributor + + // When + val viewModelTest = viewModel.test() + viewModel.handle(action) + + // Then + viewModelTest + .assertEvent { event -> event == expectedEvent } + .finish() + coVerifyOrder { + fakeUnregisterUnifiedPushUseCase.execute(fakePushersManager.instance) + fakeRegisterUnifiedPushUseCase.execute(aDistributor) + } + } +} diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt index 7970c14e90..06efca1bf7 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt @@ -69,4 +69,8 @@ class FakeVectorPreferences { fun verifySetFdroidSyncBackgroundMode(mode: BackgroundSyncMode) { verify { instance.setFdroidSyncBackgroundMode(mode) } } + + fun givenAreNotificationsEnabledForDevice(notificationsEnabled: Boolean) { + every { instance.areNotificationEnabledForDevice() } returns notificationsEnabled + } } From f8c59f6b0c82608484463d1f3157c1203102bccc Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 1 Dec 2022 10:22:09 +0100 Subject: [PATCH 050/108] Removing unused import --- .../app/core/pushers/EnsureFcmTokenIsRetrievedUseCaseTest.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/vector/src/test/java/im/vector/app/core/pushers/EnsureFcmTokenIsRetrievedUseCaseTest.kt b/vector/src/test/java/im/vector/app/core/pushers/EnsureFcmTokenIsRetrievedUseCaseTest.kt index 03a43a5b55..fca49adc9b 100644 --- a/vector/src/test/java/im/vector/app/core/pushers/EnsureFcmTokenIsRetrievedUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/core/pushers/EnsureFcmTokenIsRetrievedUseCaseTest.kt @@ -21,7 +21,6 @@ import im.vector.app.test.fakes.FakeFcmHelper import im.vector.app.test.fakes.FakePushersManager import im.vector.app.test.fakes.FakeUnifiedPushHelper import im.vector.app.test.fixtures.PusherFixture -import io.mockk.verify import org.junit.Test class EnsureFcmTokenIsRetrievedUseCaseTest { From 0c6781e9ef1ca7784539f645a8ce7b2b508c918e Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 16 Nov 2022 14:37:48 +0100 Subject: [PATCH 051/108] Adding changelog entry --- changelog.d/7596.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7596.feature diff --git a/changelog.d/7596.feature b/changelog.d/7596.feature new file mode 100644 index 0000000000..022d86342b --- /dev/null +++ b/changelog.d/7596.feature @@ -0,0 +1 @@ +Save m.local_notification_settings. event in account_data From 9d684bc021b5af66b8309d24dd398f61bdbad7f6 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 16 Nov 2022 16:31:00 +0100 Subject: [PATCH 052/108] Check if account data has content to decide if push notifications can be toggled using account data --- ...ePushNotificationsViaAccountDataUseCase.kt | 7 +++++- ...hNotificationsViaAccountDataUseCaseTest.kt | 23 +++++++++++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanTogglePushNotificationsViaAccountDataUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanTogglePushNotificationsViaAccountDataUseCase.kt index 194a2aebbf..a1b87bc396 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanTogglePushNotificationsViaAccountDataUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanTogglePushNotificationsViaAccountDataUseCase.kt @@ -16,8 +16,10 @@ package im.vector.app.features.settings.devices.v2.notification +import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes +import org.matrix.android.sdk.api.session.events.model.toModel import javax.inject.Inject class CheckIfCanTogglePushNotificationsViaAccountDataUseCase @Inject constructor() { @@ -25,6 +27,9 @@ class CheckIfCanTogglePushNotificationsViaAccountDataUseCase @Inject constructor fun execute(session: Session, deviceId: String): Boolean { return session .accountDataService() - .getUserAccountDataEvent(UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId) != null + .getUserAccountDataEvent(UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId) + ?.content + .toModel() + ?.isSilenced != null } } diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanTogglePushNotificationsViaAccountDataUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanTogglePushNotificationsViaAccountDataUseCaseTest.kt index 37433364e8..94f142cbe6 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanTogglePushNotificationsViaAccountDataUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanTogglePushNotificationsViaAccountDataUseCaseTest.kt @@ -20,7 +20,9 @@ import im.vector.app.test.fakes.FakeSession import io.mockk.mockk import org.amshove.kluent.shouldBeEqualTo import org.junit.Test +import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes +import org.matrix.android.sdk.api.session.events.model.toContent private const val A_DEVICE_ID = "device-id" @@ -32,7 +34,24 @@ class CheckIfCanTogglePushNotificationsViaAccountDataUseCaseTest { CheckIfCanTogglePushNotificationsViaAccountDataUseCase() @Test - fun `given current session and an account data for the device id when execute then result is true`() { + fun `given current session and an account data with a content for the device id when execute then result is true`() { + // Given + fakeSession + .accountDataService() + .givenGetUserAccountDataEventReturns( + type = UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + A_DEVICE_ID, + content = LocalNotificationSettingsContent(isSilenced = true).toContent(), + ) + + // When + val result = checkIfCanTogglePushNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) + + // Then + result shouldBeEqualTo true + } + + @Test + fun `given current session and an account data with empty content for the device id when execute then result is false`() { // Given fakeSession .accountDataService() @@ -45,7 +64,7 @@ class CheckIfCanTogglePushNotificationsViaAccountDataUseCaseTest { val result = checkIfCanTogglePushNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) // Then - result shouldBeEqualTo true + result shouldBeEqualTo false } @Test From c56eb331db5cfe652d70ba4d3f293dbb118dd55c Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 16 Nov 2022 16:38:46 +0100 Subject: [PATCH 053/108] Update use cases to enable/disable push notifications for the current session --- .../DisableNotificationsForCurrentSessionUseCase.kt | 5 ++--- .../DisableNotificationsForCurrentSessionUseCaseTest.kt | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCase.kt index 84d92c4291..4d890ca678 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCase.kt @@ -34,9 +34,8 @@ class DisableNotificationsForCurrentSessionUseCase @Inject constructor( suspend fun execute() { val session = activeSessionHolder.getSafeActiveSession() ?: return val deviceId = session.sessionParams.deviceId ?: return - if (checkIfCanTogglePushNotificationsViaPusherUseCase.execute(session)) { - togglePushNotificationUseCase.execute(deviceId, enabled = false) - } else { + togglePushNotificationUseCase.execute(deviceId, enabled = false) + if (!checkIfCanTogglePushNotificationsViaPusherUseCase.execute(session)) { unregisterUnifiedPushUseCase.execute(pushersManager) } } diff --git a/vector/src/test/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCaseTest.kt index 386e68ee3a..a84aa4b055 100644 --- a/vector/src/test/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCaseTest.kt @@ -67,6 +67,7 @@ class DisableNotificationsForCurrentSessionUseCaseTest { val fakeSession = fakeActiveSessionHolder.fakeSession fakeSession.givenSessionId(A_SESSION_ID) every { fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute(fakeSession) } returns false + coJustRun { fakeTogglePushNotificationUseCase.execute(A_SESSION_ID, any()) } coJustRun { fakeUnregisterUnifiedPushUseCase.execute(any()) } // When @@ -74,6 +75,7 @@ class DisableNotificationsForCurrentSessionUseCaseTest { // Then coVerify { + fakeTogglePushNotificationUseCase.execute(A_SESSION_ID, false) fakeUnregisterUnifiedPushUseCase.execute(fakePushersManager.instance) } } From 81c64503f2f5956c3ef331128b251538c16c9b22 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 16 Nov 2022 18:02:59 +0100 Subject: [PATCH 054/108] Adding SetNotificationSettingsAccountDataUseCase --- ...tNotificationSettingsAccountDataUseCase.kt | 36 +++++++++++++ ...ificationSettingsAccountDataUseCaseTest.kt | 51 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/SetNotificationSettingsAccountDataUseCase.kt create mode 100644 vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/SetNotificationSettingsAccountDataUseCaseTest.kt diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/SetNotificationSettingsAccountDataUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/SetNotificationSettingsAccountDataUseCase.kt new file mode 100644 index 0000000000..f0ec9d5ddc --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/SetNotificationSettingsAccountDataUseCase.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.settings.devices.v2.notification + +import im.vector.app.core.di.ActiveSessionHolder +import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent +import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes +import org.matrix.android.sdk.api.session.events.model.toContent +import javax.inject.Inject + +class SetNotificationSettingsAccountDataUseCase @Inject constructor( + private val activeSessionHolder: ActiveSessionHolder, +) { + + suspend fun execute(deviceId: String, localNotificationSettingsContent: LocalNotificationSettingsContent) { + val session = activeSessionHolder.getSafeActiveSession() ?: return + session.accountDataService().updateUserAccountData( + UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId, + localNotificationSettingsContent.toContent(), + ) + } +} diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/SetNotificationSettingsAccountDataUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/SetNotificationSettingsAccountDataUseCaseTest.kt new file mode 100644 index 0000000000..8f72f0946f --- /dev/null +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/SetNotificationSettingsAccountDataUseCaseTest.kt @@ -0,0 +1,51 @@ +/* + * 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.settings.devices.v2.notification + +import im.vector.app.test.fakes.FakeActiveSessionHolder +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent +import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes +import org.matrix.android.sdk.api.session.events.model.toContent + +class SetNotificationSettingsAccountDataUseCaseTest { + + private val activeSessionHolder = FakeActiveSessionHolder() + + private val setNotificationSettingsAccountDataUseCase = SetNotificationSettingsAccountDataUseCase( + activeSessionHolder = activeSessionHolder.instance, + ) + + @Test + fun `given a content when execute then update local notification settings with this content`() = runTest { + // Given + val sessionId = "a_session_id" + val localNotificationSettingsContent = LocalNotificationSettingsContent() + val fakeSession = activeSessionHolder.fakeSession + fakeSession.accountDataService().givenUpdateUserAccountDataEventSucceeds() + + // When + setNotificationSettingsAccountDataUseCase.execute(sessionId, localNotificationSettingsContent) + + // Then + activeSessionHolder.fakeSession.accountDataService().verifyUpdateUserAccountDataEventSucceeds( + UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + sessionId, + localNotificationSettingsContent.toContent(), + ) + } +} From b163b42d3d54f7c296d723b491b2e7d067f71ff5 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 17 Nov 2022 10:14:51 +0100 Subject: [PATCH 055/108] Use new sub usecase in the TogglePushNotificationUseCase --- .../TogglePushNotificationUseCase.kt | 9 +++---- .../TogglePushNotificationUseCaseTest.kt | 27 +++++++++---------- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/TogglePushNotificationUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/TogglePushNotificationUseCase.kt index 7969bbbe9b..b8a6d7343b 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/TogglePushNotificationUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/TogglePushNotificationUseCase.kt @@ -18,14 +18,14 @@ package im.vector.app.features.settings.devices.v2.notification import im.vector.app.core.di.ActiveSessionHolder import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent -import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes -import org.matrix.android.sdk.api.session.events.model.toContent import javax.inject.Inject +// TODO rename into ToggleNotificationsUseCase class TogglePushNotificationUseCase @Inject constructor( private val activeSessionHolder: ActiveSessionHolder, private val checkIfCanTogglePushNotificationsViaPusherUseCase: CheckIfCanTogglePushNotificationsViaPusherUseCase, private val checkIfCanTogglePushNotificationsViaAccountDataUseCase: CheckIfCanTogglePushNotificationsViaAccountDataUseCase, + private val setNotificationSettingsAccountDataUseCase: SetNotificationSettingsAccountDataUseCase, ) { suspend fun execute(deviceId: String, enabled: Boolean) { @@ -40,10 +40,7 @@ class TogglePushNotificationUseCase @Inject constructor( if (checkIfCanTogglePushNotificationsViaAccountDataUseCase.execute(session, deviceId)) { val newNotificationSettingsContent = LocalNotificationSettingsContent(isSilenced = !enabled) - session.accountDataService().updateUserAccountData( - UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId, - newNotificationSettingsContent.toContent(), - ) + setNotificationSettingsAccountDataUseCase.execute(deviceId, newNotificationSettingsContent) } } } diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/TogglePushNotificationUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/TogglePushNotificationUseCaseTest.kt index 35c5979e53..e443dc3e94 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/TogglePushNotificationUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/TogglePushNotificationUseCaseTest.kt @@ -18,13 +18,13 @@ package im.vector.app.features.settings.devices.v2.notification import im.vector.app.test.fakes.FakeActiveSessionHolder import im.vector.app.test.fixtures.PusherFixture +import io.mockk.coJustRun +import io.mockk.coVerify import io.mockk.every import io.mockk.mockk import kotlinx.coroutines.test.runTest import org.junit.Test import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent -import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes -import org.matrix.android.sdk.api.session.events.model.toContent class TogglePushNotificationUseCaseTest { @@ -33,12 +33,15 @@ class TogglePushNotificationUseCaseTest { mockk() private val fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase = mockk() + private val fakeSetNotificationSettingsAccountDataUseCase = + mockk() private val togglePushNotificationUseCase = TogglePushNotificationUseCase( activeSessionHolder = activeSessionHolder.instance, checkIfCanTogglePushNotificationsViaPusherUseCase = fakeCheckIfCanTogglePushNotificationsViaPusherUseCase, checkIfCanTogglePushNotificationsViaAccountDataUseCase = fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase, + setNotificationSettingsAccountDataUseCase = fakeSetNotificationSettingsAccountDataUseCase, ) @Test @@ -66,26 +69,20 @@ class TogglePushNotificationUseCaseTest { fun `when execute, then toggle local notification settings`() = runTest { // Given val sessionId = "a_session_id" - val pushers = listOf( - PusherFixture.aPusher(deviceId = sessionId, enabled = false), - PusherFixture.aPusher(deviceId = "another id", enabled = false) - ) val fakeSession = activeSessionHolder.fakeSession - fakeSession.pushersService().givenPushersLive(pushers) - fakeSession.accountDataService().givenGetUserAccountDataEventReturns( - UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + sessionId, - LocalNotificationSettingsContent(isSilenced = true).toContent() - ) every { fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute(fakeSession) } returns false every { fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase.execute(fakeSession, sessionId) } returns true + coJustRun { fakeSetNotificationSettingsAccountDataUseCase.execute(any(), any()) } + val expectedLocalNotificationSettingsContent = LocalNotificationSettingsContent( + isSilenced = false + ) // When togglePushNotificationUseCase.execute(sessionId, true) // Then - activeSessionHolder.fakeSession.accountDataService().verifyUpdateUserAccountDataEventSucceeds( - UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + sessionId, - LocalNotificationSettingsContent(isSilenced = false).toContent(), - ) + coVerify { + fakeSetNotificationSettingsAccountDataUseCase.execute(sessionId, expectedLocalNotificationSettingsContent) + } } } From 14b21dc0397c5b4e77a5ad5c94e220847b82cb14 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 17 Nov 2022 10:34:11 +0100 Subject: [PATCH 056/108] Adding use cases to create and delete notifications settings in account data --- .../LocalNotificationSettingsContent.kt | 3 +- ...eNotificationSettingsAccountDataUseCase.kt | 38 ++++++++++++ ...eNotificationSettingsAccountDataUseCase.kt | 33 +++++++++++ ...ificationSettingsAccountDataUseCaseTest.kt | 59 +++++++++++++++++++ ...ificationSettingsAccountDataUseCaseTest.kt | 49 +++++++++++++++ 5 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CreateNotificationSettingsAccountDataUseCase.kt create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/DeleteNotificationSettingsAccountDataUseCase.kt create mode 100644 vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CreateNotificationSettingsAccountDataUseCaseTest.kt create mode 100644 vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/DeleteNotificationSettingsAccountDataUseCaseTest.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/account/LocalNotificationSettingsContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/account/LocalNotificationSettingsContent.kt index 2a95ccce7a..6998d9dcf2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/account/LocalNotificationSettingsContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/account/LocalNotificationSettingsContent.kt @@ -21,5 +21,6 @@ import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) data class LocalNotificationSettingsContent( - @Json(name = "is_silenced") val isSilenced: Boolean = false + @Json(name = "is_silenced") + val isSilenced: Boolean? = false ) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CreateNotificationSettingsAccountDataUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CreateNotificationSettingsAccountDataUseCase.kt new file mode 100644 index 0000000000..e2ee19e5cd --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CreateNotificationSettingsAccountDataUseCase.kt @@ -0,0 +1,38 @@ +/* + * 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.settings.devices.v2.notification + +import im.vector.app.features.settings.VectorPreferences +import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent +import org.matrix.android.sdk.api.session.Session +import javax.inject.Inject + +class CreateNotificationSettingsAccountDataUseCase @Inject constructor( + private val vectorPreferences: VectorPreferences, + private val setNotificationSettingsAccountDataUseCase: SetNotificationSettingsAccountDataUseCase +) { + + // TODO to be called on session start when background sync is enabled + when switching to background sync + suspend fun execute(session: Session) { + val deviceId = session.sessionParams.deviceId ?: return + val isSilenced = !vectorPreferences.areNotificationEnabledForDevice() + val notificationSettingsContent = LocalNotificationSettingsContent( + isSilenced = isSilenced + ) + setNotificationSettingsAccountDataUseCase.execute(deviceId, notificationSettingsContent) + } +} diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/DeleteNotificationSettingsAccountDataUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/DeleteNotificationSettingsAccountDataUseCase.kt new file mode 100644 index 0000000000..51c24e500c --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/DeleteNotificationSettingsAccountDataUseCase.kt @@ -0,0 +1,33 @@ +/* + * 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.settings.devices.v2.notification + +import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent +import javax.inject.Inject + +class DeleteNotificationSettingsAccountDataUseCase @Inject constructor( + private val setNotificationSettingsAccountDataUseCase: SetNotificationSettingsAccountDataUseCase, +) { + + // TODO to be called when switching to push notifications method + suspend fun execute(deviceId: String) { + val emptyNotificationSettingsContent = LocalNotificationSettingsContent( + isSilenced = null + ) + setNotificationSettingsAccountDataUseCase.execute(deviceId, emptyNotificationSettingsContent) + } +} diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CreateNotificationSettingsAccountDataUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CreateNotificationSettingsAccountDataUseCaseTest.kt new file mode 100644 index 0000000000..e4cadaa005 --- /dev/null +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CreateNotificationSettingsAccountDataUseCaseTest.kt @@ -0,0 +1,59 @@ +/* + * 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.settings.devices.v2.notification + +import im.vector.app.test.fakes.FakeSession +import im.vector.app.test.fakes.FakeVectorPreferences +import io.mockk.coJustRun +import io.mockk.coVerify +import io.mockk.mockk +import io.mockk.verify +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent + +class CreateNotificationSettingsAccountDataUseCaseTest { + + private val fakeVectorPreferences = FakeVectorPreferences() + private val fakeSetNotificationSettingsAccountDataUseCase = mockk() + + private val createNotificationSettingsAccountDataUseCase = CreateNotificationSettingsAccountDataUseCase( + vectorPreferences = fakeVectorPreferences.instance, + setNotificationSettingsAccountDataUseCase = fakeSetNotificationSettingsAccountDataUseCase, + ) + + @Test + fun `given a device id when execute then content with the current notification preference is set for the account data`() = runTest { + // Given + val aDeviceId = "device-id" + val session = FakeSession() + session.givenSessionId(aDeviceId) + coJustRun { fakeSetNotificationSettingsAccountDataUseCase.execute(any(), any()) } + val areNotificationsEnabled = true + fakeVectorPreferences.givenAreNotificationEnabled(areNotificationsEnabled) + val expectedContent = LocalNotificationSettingsContent( + isSilenced = !areNotificationsEnabled + ) + + // When + createNotificationSettingsAccountDataUseCase.execute(session) + + // Then + verify { fakeVectorPreferences.instance.areNotificationEnabledForDevice() } + coVerify { fakeSetNotificationSettingsAccountDataUseCase.execute(aDeviceId, expectedContent) } + } +} diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/DeleteNotificationSettingsAccountDataUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/DeleteNotificationSettingsAccountDataUseCaseTest.kt new file mode 100644 index 0000000000..038a1f436a --- /dev/null +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/DeleteNotificationSettingsAccountDataUseCaseTest.kt @@ -0,0 +1,49 @@ +/* + * 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.settings.devices.v2.notification + +import io.mockk.coJustRun +import io.mockk.coVerify +import io.mockk.mockk +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent + +class DeleteNotificationSettingsAccountDataUseCaseTest { + + private val fakeSetNotificationSettingsAccountDataUseCase = mockk() + + private val deleteNotificationSettingsAccountDataUseCase = DeleteNotificationSettingsAccountDataUseCase( + setNotificationSettingsAccountDataUseCase = fakeSetNotificationSettingsAccountDataUseCase, + ) + + @Test + fun `given a device id when execute then empty content is set for the account data`() = runTest { + // Given + val aDeviceId = "device-id" + coJustRun { fakeSetNotificationSettingsAccountDataUseCase.execute(any(), any()) } + val expectedContent = LocalNotificationSettingsContent( + isSilenced = null + ) + + // When + deleteNotificationSettingsAccountDataUseCase.execute(aDeviceId) + + // Then + coVerify { fakeSetNotificationSettingsAccountDataUseCase.execute(aDeviceId, expectedContent) } + } +} From 7c51174d7ef3940cbb550dcbd89dcce942ae4bec Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 17 Nov 2022 11:04:21 +0100 Subject: [PATCH 057/108] Renaming some use cases to be consistent --- ...CanToggleNotificationsViaPusherUseCase.kt} | 2 +- ...ggleNotificationsViaAccountDataUseCase.kt} | 2 +- ...CanToggleNotificationsViaPusherUseCase.kt} | 2 +- .../GetNotificationsStatusUseCase.kt | 8 ++--- ...seCase.kt => ToggleNotificationUseCase.kt} | 11 ++++--- .../v2/overview/SessionOverviewViewModel.kt | 6 ++-- ...leNotificationsForCurrentSessionUseCase.kt | 12 ++++---- ...leNotificationsForCurrentSessionUseCase.kt | 6 ++-- ...oggleNotificationsViaPusherUseCaseTest.kt} | 8 ++--- ...NotificationsViaAccountDataUseCaseTest.kt} | 12 ++++---- ...oggleNotificationsViaPusherUseCaseTest.kt} | 8 ++--- .../GetNotificationsStatusUseCaseTest.kt | 28 ++++++++--------- ...st.kt => ToggleNotificationUseCaseTest.kt} | 30 +++++++++---------- ...tificationsForCurrentSessionUseCaseTest.kt | 22 +++++++------- ...tificationsForCurrentSessionUseCaseTest.kt | 8 ++--- .../FakeTogglePushNotificationUseCase.kt | 4 +-- 16 files changed, 85 insertions(+), 84 deletions(-) rename vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/{CanTogglePushNotificationsViaPusherUseCase.kt => CanToggleNotificationsViaPusherUseCase.kt} (94%) rename vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/{CheckIfCanTogglePushNotificationsViaAccountDataUseCase.kt => CheckIfCanToggleNotificationsViaAccountDataUseCase.kt} (93%) rename vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/{CheckIfCanTogglePushNotificationsViaPusherUseCase.kt => CheckIfCanToggleNotificationsViaPusherUseCase.kt} (92%) rename vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/{TogglePushNotificationUseCase.kt => ToggleNotificationUseCase.kt} (74%) rename vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/{CanTogglePushNotificationsViaPusherUseCaseTest.kt => CanToggleNotificationsViaPusherUseCaseTest.kt} (87%) rename vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/{CheckIfCanTogglePushNotificationsViaAccountDataUseCaseTest.kt => CheckIfCanToggleNotificationsViaAccountDataUseCaseTest.kt} (83%) rename vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/{CheckIfCanTogglePushNotificationsViaPusherUseCaseTest.kt => CheckIfCanToggleNotificationsViaPusherUseCaseTest.kt} (82%) rename vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/{TogglePushNotificationUseCaseTest.kt => ToggleNotificationUseCaseTest.kt} (70%) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CanTogglePushNotificationsViaPusherUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CanToggleNotificationsViaPusherUseCase.kt similarity index 94% rename from vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CanTogglePushNotificationsViaPusherUseCase.kt rename to vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CanToggleNotificationsViaPusherUseCase.kt index 0125d92ba6..96521ec78c 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CanTogglePushNotificationsViaPusherUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CanToggleNotificationsViaPusherUseCase.kt @@ -24,7 +24,7 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.flow.unwrap import javax.inject.Inject -class CanTogglePushNotificationsViaPusherUseCase @Inject constructor() { +class CanToggleNotificationsViaPusherUseCase @Inject constructor() { fun execute(session: Session): Flow { return session diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanTogglePushNotificationsViaAccountDataUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanToggleNotificationsViaAccountDataUseCase.kt similarity index 93% rename from vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanTogglePushNotificationsViaAccountDataUseCase.kt rename to vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanToggleNotificationsViaAccountDataUseCase.kt index a1b87bc396..b006e3da45 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanTogglePushNotificationsViaAccountDataUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanToggleNotificationsViaAccountDataUseCase.kt @@ -22,7 +22,7 @@ import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes import org.matrix.android.sdk.api.session.events.model.toModel import javax.inject.Inject -class CheckIfCanTogglePushNotificationsViaAccountDataUseCase @Inject constructor() { +class CheckIfCanToggleNotificationsViaAccountDataUseCase @Inject constructor() { fun execute(session: Session, deviceId: String): Boolean { return session diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanTogglePushNotificationsViaPusherUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanToggleNotificationsViaPusherUseCase.kt similarity index 92% rename from vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanTogglePushNotificationsViaPusherUseCase.kt rename to vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanToggleNotificationsViaPusherUseCase.kt index ca314bf145..1dc186be7c 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanTogglePushNotificationsViaPusherUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanToggleNotificationsViaPusherUseCase.kt @@ -19,7 +19,7 @@ package im.vector.app.features.settings.devices.v2.notification import org.matrix.android.sdk.api.session.Session import javax.inject.Inject -class CheckIfCanTogglePushNotificationsViaPusherUseCase @Inject constructor() { +class CheckIfCanToggleNotificationsViaPusherUseCase @Inject constructor() { fun execute(session: Session): Boolean { return session diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCase.kt index 03e4e31f2e..f98fd63efb 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCase.kt @@ -30,13 +30,13 @@ import org.matrix.android.sdk.flow.unwrap import javax.inject.Inject class GetNotificationsStatusUseCase @Inject constructor( - private val canTogglePushNotificationsViaPusherUseCase: CanTogglePushNotificationsViaPusherUseCase, - private val checkIfCanTogglePushNotificationsViaAccountDataUseCase: CheckIfCanTogglePushNotificationsViaAccountDataUseCase, + private val canToggleNotificationsViaPusherUseCase: CanToggleNotificationsViaPusherUseCase, + private val checkIfCanToggleNotificationsViaAccountDataUseCase: CheckIfCanToggleNotificationsViaAccountDataUseCase, ) { fun execute(session: Session, deviceId: String): Flow { return when { - checkIfCanTogglePushNotificationsViaAccountDataUseCase.execute(session, deviceId) -> { + checkIfCanToggleNotificationsViaAccountDataUseCase.execute(session, deviceId) -> { session.flow() .liveUserAccountData(UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId) .unwrap() @@ -44,7 +44,7 @@ class GetNotificationsStatusUseCase @Inject constructor( .map { if (it == true) NotificationsStatus.ENABLED else NotificationsStatus.DISABLED } .distinctUntilChanged() } - else -> canTogglePushNotificationsViaPusherUseCase.execute(session) + else -> canToggleNotificationsViaPusherUseCase.execute(session) .flatMapLatest { canToggle -> if (canToggle) { session.flow() diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/TogglePushNotificationUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/ToggleNotificationUseCase.kt similarity index 74% rename from vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/TogglePushNotificationUseCase.kt rename to vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/ToggleNotificationUseCase.kt index b8a6d7343b..d0e1ea2a7a 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/TogglePushNotificationUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/ToggleNotificationUseCase.kt @@ -20,25 +20,24 @@ import im.vector.app.core.di.ActiveSessionHolder import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent import javax.inject.Inject -// TODO rename into ToggleNotificationsUseCase -class TogglePushNotificationUseCase @Inject constructor( +class ToggleNotificationUseCase @Inject constructor( private val activeSessionHolder: ActiveSessionHolder, - private val checkIfCanTogglePushNotificationsViaPusherUseCase: CheckIfCanTogglePushNotificationsViaPusherUseCase, - private val checkIfCanTogglePushNotificationsViaAccountDataUseCase: CheckIfCanTogglePushNotificationsViaAccountDataUseCase, + private val checkIfCanToggleNotificationsViaPusherUseCase: CheckIfCanToggleNotificationsViaPusherUseCase, + private val checkIfCanToggleNotificationsViaAccountDataUseCase: CheckIfCanToggleNotificationsViaAccountDataUseCase, private val setNotificationSettingsAccountDataUseCase: SetNotificationSettingsAccountDataUseCase, ) { suspend fun execute(deviceId: String, enabled: Boolean) { val session = activeSessionHolder.getSafeActiveSession() ?: return - if (checkIfCanTogglePushNotificationsViaPusherUseCase.execute(session)) { + if (checkIfCanToggleNotificationsViaPusherUseCase.execute(session)) { val devicePusher = session.pushersService().getPushers().firstOrNull { it.deviceId == deviceId } devicePusher?.let { pusher -> session.pushersService().togglePusher(pusher, enabled) } } - if (checkIfCanTogglePushNotificationsViaAccountDataUseCase.execute(session, deviceId)) { + if (checkIfCanToggleNotificationsViaAccountDataUseCase.execute(session, deviceId)) { val newNotificationSettingsContent = LocalNotificationSettingsContent(isSilenced = !enabled) setNotificationSettingsAccountDataUseCase.execute(deviceId, newNotificationSettingsContent) } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt index 472e0a4269..0ddf688514 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt @@ -31,7 +31,7 @@ import im.vector.app.features.settings.devices.v2.RefreshDevicesUseCase import im.vector.app.features.settings.devices.v2.ToggleIpAddressVisibilityUseCase import im.vector.app.features.settings.devices.v2.VectorSessionsListViewModel import im.vector.app.features.settings.devices.v2.notification.GetNotificationsStatusUseCase -import im.vector.app.features.settings.devices.v2.notification.TogglePushNotificationUseCase +import im.vector.app.features.settings.devices.v2.notification.ToggleNotificationUseCase import im.vector.app.features.settings.devices.v2.signout.InterceptSignoutFlowResponseUseCase import im.vector.app.features.settings.devices.v2.signout.SignoutSessionsReAuthNeeded import im.vector.app.features.settings.devices.v2.signout.SignoutSessionsUseCase @@ -54,7 +54,7 @@ class SessionOverviewViewModel @AssistedInject constructor( private val interceptSignoutFlowResponseUseCase: InterceptSignoutFlowResponseUseCase, private val pendingAuthHandler: PendingAuthHandler, private val activeSessionHolder: ActiveSessionHolder, - private val togglePushNotificationUseCase: TogglePushNotificationUseCase, + private val toggleNotificationUseCase: ToggleNotificationUseCase, private val getNotificationsStatusUseCase: GetNotificationsStatusUseCase, refreshDevicesUseCase: RefreshDevicesUseCase, private val vectorPreferences: VectorPreferences, @@ -228,7 +228,7 @@ class SessionOverviewViewModel @AssistedInject constructor( private fun handleTogglePusherAction(action: SessionOverviewAction.TogglePushNotifications) { viewModelScope.launch { - togglePushNotificationUseCase.execute(action.deviceId, action.enabled) + toggleNotificationUseCase.execute(action.deviceId, action.enabled) } } } diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCase.kt index 4d890ca678..380c3b5e4e 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCase.kt @@ -19,23 +19,23 @@ package im.vector.app.features.settings.notifications import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.pushers.PushersManager import im.vector.app.core.pushers.UnregisterUnifiedPushUseCase -import im.vector.app.features.settings.devices.v2.notification.CheckIfCanTogglePushNotificationsViaPusherUseCase -import im.vector.app.features.settings.devices.v2.notification.TogglePushNotificationUseCase +import im.vector.app.features.settings.devices.v2.notification.CheckIfCanToggleNotificationsViaPusherUseCase +import im.vector.app.features.settings.devices.v2.notification.ToggleNotificationUseCase import javax.inject.Inject class DisableNotificationsForCurrentSessionUseCase @Inject constructor( private val activeSessionHolder: ActiveSessionHolder, private val pushersManager: PushersManager, - private val checkIfCanTogglePushNotificationsViaPusherUseCase: CheckIfCanTogglePushNotificationsViaPusherUseCase, - private val togglePushNotificationUseCase: TogglePushNotificationUseCase, + private val checkIfCanToggleNotificationsViaPusherUseCase: CheckIfCanToggleNotificationsViaPusherUseCase, + private val toggleNotificationUseCase: ToggleNotificationUseCase, private val unregisterUnifiedPushUseCase: UnregisterUnifiedPushUseCase, ) { suspend fun execute() { val session = activeSessionHolder.getSafeActiveSession() ?: return val deviceId = session.sessionParams.deviceId ?: return - togglePushNotificationUseCase.execute(deviceId, enabled = false) - if (!checkIfCanTogglePushNotificationsViaPusherUseCase.execute(session)) { + toggleNotificationUseCase.execute(deviceId, enabled = false) + if (!checkIfCanToggleNotificationsViaPusherUseCase.execute(session)) { unregisterUnifiedPushUseCase.execute(pushersManager) } } diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt index 99fb249384..89633a10c2 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt @@ -20,13 +20,13 @@ import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.pushers.EnsureFcmTokenIsRetrievedUseCase import im.vector.app.core.pushers.PushersManager import im.vector.app.core.pushers.RegisterUnifiedPushUseCase -import im.vector.app.features.settings.devices.v2.notification.TogglePushNotificationUseCase +import im.vector.app.features.settings.devices.v2.notification.ToggleNotificationUseCase import javax.inject.Inject class EnableNotificationsForCurrentSessionUseCase @Inject constructor( private val activeSessionHolder: ActiveSessionHolder, private val pushersManager: PushersManager, - private val togglePushNotificationUseCase: TogglePushNotificationUseCase, + private val toggleNotificationUseCase: ToggleNotificationUseCase, private val registerUnifiedPushUseCase: RegisterUnifiedPushUseCase, private val ensureFcmTokenIsRetrievedUseCase: EnsureFcmTokenIsRetrievedUseCase, ) { @@ -52,7 +52,7 @@ class EnableNotificationsForCurrentSessionUseCase @Inject constructor( val session = activeSessionHolder.getSafeActiveSession() ?: return EnableNotificationsResult.Failure val deviceId = session.sessionParams.deviceId ?: return EnableNotificationsResult.Failure - togglePushNotificationUseCase.execute(deviceId, enabled = true) + toggleNotificationUseCase.execute(deviceId, enabled = true) return EnableNotificationsResult.Success } diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CanTogglePushNotificationsViaPusherUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CanToggleNotificationsViaPusherUseCaseTest.kt similarity index 87% rename from vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CanTogglePushNotificationsViaPusherUseCaseTest.kt rename to vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CanToggleNotificationsViaPusherUseCaseTest.kt index 997fa827f5..3284adb32d 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CanTogglePushNotificationsViaPusherUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CanToggleNotificationsViaPusherUseCaseTest.kt @@ -30,13 +30,13 @@ import org.junit.Test private val A_HOMESERVER_CAPABILITIES = aHomeServerCapabilities(canRemotelyTogglePushNotificationsOfDevices = true) -class CanTogglePushNotificationsViaPusherUseCaseTest { +class CanToggleNotificationsViaPusherUseCaseTest { private val fakeSession = FakeSession() private val fakeFlowLiveDataConversions = FakeFlowLiveDataConversions() - private val canTogglePushNotificationsViaPusherUseCase = - CanTogglePushNotificationsViaPusherUseCase() + private val canToggleNotificationsViaPusherUseCase = + CanToggleNotificationsViaPusherUseCase() @Before fun setUp() { @@ -57,7 +57,7 @@ class CanTogglePushNotificationsViaPusherUseCaseTest { .givenAsFlow() // When - val result = canTogglePushNotificationsViaPusherUseCase.execute(fakeSession).firstOrNull() + val result = canToggleNotificationsViaPusherUseCase.execute(fakeSession).firstOrNull() // Then result shouldBeEqualTo A_HOMESERVER_CAPABILITIES.canRemotelyTogglePushNotificationsOfDevices diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanTogglePushNotificationsViaAccountDataUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanToggleNotificationsViaAccountDataUseCaseTest.kt similarity index 83% rename from vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanTogglePushNotificationsViaAccountDataUseCaseTest.kt rename to vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanToggleNotificationsViaAccountDataUseCaseTest.kt index 94f142cbe6..de225d36ac 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanTogglePushNotificationsViaAccountDataUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanToggleNotificationsViaAccountDataUseCaseTest.kt @@ -26,12 +26,12 @@ import org.matrix.android.sdk.api.session.events.model.toContent private const val A_DEVICE_ID = "device-id" -class CheckIfCanTogglePushNotificationsViaAccountDataUseCaseTest { +class CheckIfCanToggleNotificationsViaAccountDataUseCaseTest { private val fakeSession = FakeSession() - private val checkIfCanTogglePushNotificationsViaAccountDataUseCase = - CheckIfCanTogglePushNotificationsViaAccountDataUseCase() + private val checkIfCanToggleNotificationsViaAccountDataUseCase = + CheckIfCanToggleNotificationsViaAccountDataUseCase() @Test fun `given current session and an account data with a content for the device id when execute then result is true`() { @@ -44,7 +44,7 @@ class CheckIfCanTogglePushNotificationsViaAccountDataUseCaseTest { ) // When - val result = checkIfCanTogglePushNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) + val result = checkIfCanToggleNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) // Then result shouldBeEqualTo true @@ -61,7 +61,7 @@ class CheckIfCanTogglePushNotificationsViaAccountDataUseCaseTest { ) // When - val result = checkIfCanTogglePushNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) + val result = checkIfCanToggleNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) // Then result shouldBeEqualTo false @@ -78,7 +78,7 @@ class CheckIfCanTogglePushNotificationsViaAccountDataUseCaseTest { ) // When - val result = checkIfCanTogglePushNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) + val result = checkIfCanToggleNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) // Then result shouldBeEqualTo false diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanTogglePushNotificationsViaPusherUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanToggleNotificationsViaPusherUseCaseTest.kt similarity index 82% rename from vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanTogglePushNotificationsViaPusherUseCaseTest.kt rename to vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanToggleNotificationsViaPusherUseCaseTest.kt index 508a05acd6..64beb7b8e2 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanTogglePushNotificationsViaPusherUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanToggleNotificationsViaPusherUseCaseTest.kt @@ -23,12 +23,12 @@ import org.junit.Test private val A_HOMESERVER_CAPABILITIES = aHomeServerCapabilities(canRemotelyTogglePushNotificationsOfDevices = true) -class CheckIfCanTogglePushNotificationsViaPusherUseCaseTest { +class CheckIfCanToggleNotificationsViaPusherUseCaseTest { private val fakeSession = FakeSession() - private val checkIfCanTogglePushNotificationsViaPusherUseCase = - CheckIfCanTogglePushNotificationsViaPusherUseCase() + private val checkIfCanToggleNotificationsViaPusherUseCase = + CheckIfCanToggleNotificationsViaPusherUseCase() @Test fun `given current session when execute then toggle capability is returned`() { @@ -38,7 +38,7 @@ class CheckIfCanTogglePushNotificationsViaPusherUseCaseTest { .givenCapabilities(A_HOMESERVER_CAPABILITIES) // When - val result = checkIfCanTogglePushNotificationsViaPusherUseCase.execute(fakeSession) + val result = checkIfCanToggleNotificationsViaPusherUseCase.execute(fakeSession) // Then result shouldBeEqualTo A_HOMESERVER_CAPABILITIES.canRemotelyTogglePushNotificationsOfDevices diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCaseTest.kt index b38367b098..7c0b0a8693 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCaseTest.kt @@ -46,15 +46,15 @@ class GetNotificationsStatusUseCaseTest { val instantTaskExecutorRule = InstantTaskExecutorRule() private val fakeSession = FakeSession() - private val fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase = - mockk() - private val fakeCanTogglePushNotificationsViaPusherUseCase = - mockk() + private val fakeCheckIfCanToggleNotificationsViaAccountDataUseCase = + mockk() + private val fakeCanToggleNotificationsViaPusherUseCase = + mockk() private val getNotificationsStatusUseCase = GetNotificationsStatusUseCase( - checkIfCanTogglePushNotificationsViaAccountDataUseCase = fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase, - canTogglePushNotificationsViaPusherUseCase = fakeCanTogglePushNotificationsViaPusherUseCase, + checkIfCanTogglePushNotificationsViaAccountDataUseCase = fakeCheckIfCanToggleNotificationsViaAccountDataUseCase, + canTogglePushNotificationsViaPusherUseCase = fakeCanToggleNotificationsViaPusherUseCase, ) @Before @@ -70,8 +70,8 @@ class GetNotificationsStatusUseCaseTest { @Test fun `given current session and toggle is not supported when execute then resulting flow contains NOT_SUPPORTED value`() = runTest { // Given - every { fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) } returns false - every { fakeCanTogglePushNotificationsViaPusherUseCase.execute(fakeSession) } returns flowOf(false) + every { fakeCheckIfCanToggleNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) } returns false + every { fakeCanToggleNotificationsViaPusherUseCase.execute(fakeSession) } returns flowOf(false) // When val result = getNotificationsStatusUseCase.execute(fakeSession, A_DEVICE_ID) @@ -80,8 +80,8 @@ class GetNotificationsStatusUseCaseTest { result.firstOrNull() shouldBeEqualTo NotificationsStatus.NOT_SUPPORTED verifyOrder { // we should first check account data - fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) - fakeCanTogglePushNotificationsViaPusherUseCase.execute(fakeSession) + fakeCheckIfCanToggleNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) + fakeCanToggleNotificationsViaPusherUseCase.execute(fakeSession) } } @@ -95,8 +95,8 @@ class GetNotificationsStatusUseCaseTest { ) ) fakeSession.pushersService().givenPushersLive(pushers) - every { fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) } returns false - every { fakeCanTogglePushNotificationsViaPusherUseCase.execute(fakeSession) } returns flowOf(true) + every { fakeCheckIfCanToggleNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) } returns false + every { fakeCanToggleNotificationsViaPusherUseCase.execute(fakeSession) } returns flowOf(true) // When val result = getNotificationsStatusUseCase.execute(fakeSession, A_DEVICE_ID) @@ -116,8 +116,8 @@ class GetNotificationsStatusUseCaseTest { isSilenced = false ).toContent(), ) - every { fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) } returns true - every { fakeCanTogglePushNotificationsViaPusherUseCase.execute(fakeSession) } returns flowOf(false) + every { fakeCheckIfCanToggleNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) } returns true + every { fakeCanToggleNotificationsViaPusherUseCase.execute(fakeSession) } returns flowOf(false) // When val result = getNotificationsStatusUseCase.execute(fakeSession, A_DEVICE_ID) diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/TogglePushNotificationUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/ToggleNotificationUseCaseTest.kt similarity index 70% rename from vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/TogglePushNotificationUseCaseTest.kt rename to vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/ToggleNotificationUseCaseTest.kt index e443dc3e94..99be7c7ea9 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/TogglePushNotificationUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/ToggleNotificationUseCaseTest.kt @@ -26,21 +26,21 @@ import kotlinx.coroutines.test.runTest import org.junit.Test import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent -class TogglePushNotificationUseCaseTest { +class ToggleNotificationUseCaseTest { private val activeSessionHolder = FakeActiveSessionHolder() - private val fakeCheckIfCanTogglePushNotificationsViaPusherUseCase = - mockk() - private val fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase = - mockk() + private val fakeCheckIfCanToggleNotificationsViaPusherUseCase = + mockk() + private val fakeCheckIfCanToggleNotificationsViaAccountDataUseCase = + mockk() private val fakeSetNotificationSettingsAccountDataUseCase = mockk() - private val togglePushNotificationUseCase = - TogglePushNotificationUseCase( + private val toggleNotificationUseCase = + ToggleNotificationUseCase( activeSessionHolder = activeSessionHolder.instance, - checkIfCanTogglePushNotificationsViaPusherUseCase = fakeCheckIfCanTogglePushNotificationsViaPusherUseCase, - checkIfCanTogglePushNotificationsViaAccountDataUseCase = fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase, + checkIfCanTogglePushNotificationsViaPusherUseCase = fakeCheckIfCanToggleNotificationsViaPusherUseCase, + checkIfCanTogglePushNotificationsViaAccountDataUseCase = fakeCheckIfCanToggleNotificationsViaAccountDataUseCase, setNotificationSettingsAccountDataUseCase = fakeSetNotificationSettingsAccountDataUseCase, ) @@ -55,11 +55,11 @@ class TogglePushNotificationUseCaseTest { val fakeSession = activeSessionHolder.fakeSession fakeSession.pushersService().givenPushersLive(pushers) fakeSession.pushersService().givenGetPushers(pushers) - every { fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute(fakeSession) } returns true - every { fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase.execute(fakeSession, sessionId) } returns false + every { fakeCheckIfCanToggleNotificationsViaPusherUseCase.execute(fakeSession) } returns true + every { fakeCheckIfCanToggleNotificationsViaAccountDataUseCase.execute(fakeSession, sessionId) } returns false // When - togglePushNotificationUseCase.execute(sessionId, true) + toggleNotificationUseCase.execute(sessionId, true) // Then activeSessionHolder.fakeSession.pushersService().verifyTogglePusherCalled(pushers.first(), true) @@ -70,15 +70,15 @@ class TogglePushNotificationUseCaseTest { // Given val sessionId = "a_session_id" val fakeSession = activeSessionHolder.fakeSession - every { fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute(fakeSession) } returns false - every { fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase.execute(fakeSession, sessionId) } returns true + every { fakeCheckIfCanToggleNotificationsViaPusherUseCase.execute(fakeSession) } returns false + every { fakeCheckIfCanToggleNotificationsViaAccountDataUseCase.execute(fakeSession, sessionId) } returns true coJustRun { fakeSetNotificationSettingsAccountDataUseCase.execute(any(), any()) } val expectedLocalNotificationSettingsContent = LocalNotificationSettingsContent( isSilenced = false ) // When - togglePushNotificationUseCase.execute(sessionId, true) + toggleNotificationUseCase.execute(sessionId, true) // Then coVerify { diff --git a/vector/src/test/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCaseTest.kt index a84aa4b055..153b79f1a8 100644 --- a/vector/src/test/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCaseTest.kt @@ -16,6 +16,8 @@ package im.vector.app.features.settings.notifications +import im.vector.app.features.settings.devices.v2.notification.CheckIfCanToggleNotificationsViaPusherUseCase +import im.vector.app.features.settings.devices.v2.notification.ToggleNotificationUseCase import im.vector.app.core.pushers.UnregisterUnifiedPushUseCase import im.vector.app.features.settings.devices.v2.notification.CheckIfCanTogglePushNotificationsViaPusherUseCase import im.vector.app.features.settings.devices.v2.notification.TogglePushNotificationUseCase @@ -34,15 +36,15 @@ class DisableNotificationsForCurrentSessionUseCaseTest { private val fakeActiveSessionHolder = FakeActiveSessionHolder() private val fakePushersManager = FakePushersManager() - private val fakeCheckIfCanTogglePushNotificationsViaPusherUseCase = mockk() - private val fakeTogglePushNotificationUseCase = mockk() + private val fakeCheckIfCanToggleNotificationsViaPusherUseCase = mockk() + private val fakeToggleNotificationUseCase = mockk() private val fakeUnregisterUnifiedPushUseCase = mockk() private val disableNotificationsForCurrentSessionUseCase = DisableNotificationsForCurrentSessionUseCase( activeSessionHolder = fakeActiveSessionHolder.instance, pushersManager = fakePushersManager.instance, - checkIfCanTogglePushNotificationsViaPusherUseCase = fakeCheckIfCanTogglePushNotificationsViaPusherUseCase, - togglePushNotificationUseCase = fakeTogglePushNotificationUseCase, + checkIfCanToggleNotificationsViaPusherUseCase = fakeCheckIfCanToggleNotificationsViaPusherUseCase, + toggleNotificationUseCase = fakeToggleNotificationUseCase, unregisterUnifiedPushUseCase = fakeUnregisterUnifiedPushUseCase, ) @@ -51,14 +53,14 @@ class DisableNotificationsForCurrentSessionUseCaseTest { // Given val fakeSession = fakeActiveSessionHolder.fakeSession fakeSession.givenSessionId(A_SESSION_ID) - every { fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute(fakeSession) } returns true - coJustRun { fakeTogglePushNotificationUseCase.execute(A_SESSION_ID, any()) } + every { fakeCheckIfCanToggleNotificationsViaPusherUseCase.execute(fakeSession) } returns true + coJustRun { fakeToggleNotificationUseCase.execute(A_SESSION_ID, any()) } // When disableNotificationsForCurrentSessionUseCase.execute() // Then - coVerify { fakeTogglePushNotificationUseCase.execute(A_SESSION_ID, false) } + coVerify { fakeToggleNotificationUseCase.execute(A_SESSION_ID, false) } } @Test @@ -66,8 +68,8 @@ class DisableNotificationsForCurrentSessionUseCaseTest { // Given val fakeSession = fakeActiveSessionHolder.fakeSession fakeSession.givenSessionId(A_SESSION_ID) - every { fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute(fakeSession) } returns false - coJustRun { fakeTogglePushNotificationUseCase.execute(A_SESSION_ID, any()) } + every { fakeCheckIfCanToggleNotificationsViaPusherUseCase.execute(fakeSession) } returns false + coJustRun { fakeToggleNotificationUseCase.execute(A_SESSION_ID, any()) } coJustRun { fakeUnregisterUnifiedPushUseCase.execute(any()) } // When @@ -75,7 +77,7 @@ class DisableNotificationsForCurrentSessionUseCaseTest { // Then coVerify { - fakeTogglePushNotificationUseCase.execute(A_SESSION_ID, false) + fakeToggleNotificationUseCase.execute(A_SESSION_ID, false) fakeUnregisterUnifiedPushUseCase.execute(fakePushersManager.instance) } } diff --git a/vector/src/test/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCaseTest.kt index c923f0c7d6..f10b0777cb 100644 --- a/vector/src/test/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCaseTest.kt @@ -18,7 +18,7 @@ package im.vector.app.features.settings.notifications import im.vector.app.core.pushers.EnsureFcmTokenIsRetrievedUseCase import im.vector.app.core.pushers.RegisterUnifiedPushUseCase -import im.vector.app.features.settings.devices.v2.notification.TogglePushNotificationUseCase +import im.vector.app.features.settings.devices.v2.notification.ToggleNotificationUseCase import im.vector.app.test.fakes.FakeActiveSessionHolder import im.vector.app.test.fakes.FakePushersManager import io.mockk.coJustRun @@ -36,14 +36,14 @@ class EnableNotificationsForCurrentSessionUseCaseTest { private val fakeActiveSessionHolder = FakeActiveSessionHolder() private val fakePushersManager = FakePushersManager() - private val fakeTogglePushNotificationUseCase = mockk() + private val fakeToggleNotificationUseCase = mockk() private val fakeRegisterUnifiedPushUseCase = mockk() private val fakeEnsureFcmTokenIsRetrievedUseCase = mockk() private val enableNotificationsForCurrentSessionUseCase = EnableNotificationsForCurrentSessionUseCase( activeSessionHolder = fakeActiveSessionHolder.instance, pushersManager = fakePushersManager.instance, - togglePushNotificationUseCase = fakeTogglePushNotificationUseCase, + toggleNotificationUseCase = fakeToggleNotificationUseCase, registerUnifiedPushUseCase = fakeRegisterUnifiedPushUseCase, ensureFcmTokenIsRetrievedUseCase = fakeEnsureFcmTokenIsRetrievedUseCase, ) @@ -57,7 +57,7 @@ class EnableNotificationsForCurrentSessionUseCaseTest { fakePushersManager.givenGetPusherForCurrentSessionReturns(null) every { fakeRegisterUnifiedPushUseCase.execute(any()) } returns RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.Success justRun { fakeEnsureFcmTokenIsRetrievedUseCase.execute(any(), any()) } - coJustRun { fakeTogglePushNotificationUseCase.execute(A_SESSION_ID, any()) } + coJustRun { fakeToggleNotificationUseCase.execute(A_SESSION_ID, any()) } // When val result = enableNotificationsForCurrentSessionUseCase.execute(aDistributor) diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeTogglePushNotificationUseCase.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeTogglePushNotificationUseCase.kt index bfbbb87705..cc42193fe1 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeTogglePushNotificationUseCase.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeTogglePushNotificationUseCase.kt @@ -16,14 +16,14 @@ package im.vector.app.test.fakes -import im.vector.app.features.settings.devices.v2.notification.TogglePushNotificationUseCase +import im.vector.app.features.settings.devices.v2.notification.ToggleNotificationUseCase import io.mockk.coJustRun import io.mockk.coVerify import io.mockk.mockk class FakeTogglePushNotificationUseCase { - val instance = mockk { + val instance = mockk { coJustRun { execute(any(), any()) } } From ab6a6b53c88b4336bb3b96edced8beb852399c1f Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Fri, 18 Nov 2022 15:19:34 +0100 Subject: [PATCH 058/108] Some refactorings + update unit tests --- .../ConfigureAndStartSessionUseCase.kt | 17 +++- ...oggleNotificationsViaAccountDataUseCase.kt | 14 +-- ...eNotificationSettingsAccountDataUseCase.kt | 11 ++- ...tNotificationSettingsAccountDataUseCase.kt | 34 +++++++ ...tNotificationSettingsAccountDataUseCase.kt | 9 +- .../notification/ToggleNotificationUseCase.kt | 2 +- ...NotificationSettingsAccountDataUseCase.kt} | 21 ++-- .../ConfigureAndStartSessionUseCaseTest.kt | 56 +++++++---- ...eNotificationsViaAccountDataUseCaseTest.kt | 32 +++--- ...ificationSettingsAccountDataUseCaseTest.kt | 59 ----------- ...ificationSettingsAccountDataUseCaseTest.kt | 9 +- ...ificationSettingsAccountDataUseCaseTest.kt | 49 ++++++++++ .../GetNotificationsStatusUseCaseTest.kt | 4 +- ...ificationSettingsAccountDataUseCaseTest.kt | 14 +-- .../ToggleNotificationUseCaseTest.kt | 8 +- ...ificationSettingsAccountDataUseCaseTest.kt | 97 +++++++++++++++++++ .../overview/SessionOverviewViewModelTest.kt | 8 +- ...tificationsForCurrentSessionUseCaseTest.kt | 4 +- ...se.kt => FakeToggleNotificationUseCase.kt} | 2 +- .../app/test/fakes/FakeVectorPreferences.kt | 4 + 20 files changed, 300 insertions(+), 154 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataUseCase.kt rename vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/{CreateNotificationSettingsAccountDataUseCase.kt => UpdateNotificationSettingsAccountDataUseCase.kt} (57%) delete mode 100644 vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CreateNotificationSettingsAccountDataUseCaseTest.kt create mode 100644 vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataUseCaseTest.kt create mode 100644 vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/UpdateNotificationSettingsAccountDataUseCaseTest.kt rename vector/src/test/java/im/vector/app/test/fakes/{FakeTogglePushNotificationUseCase.kt => FakeToggleNotificationUseCase.kt} (96%) diff --git a/vector/src/main/java/im/vector/app/core/session/ConfigureAndStartSessionUseCase.kt b/vector/src/main/java/im/vector/app/core/session/ConfigureAndStartSessionUseCase.kt index 96c3f8a6ce..d9688a45ed 100644 --- a/vector/src/main/java/im/vector/app/core/session/ConfigureAndStartSessionUseCase.kt +++ b/vector/src/main/java/im/vector/app/core/session/ConfigureAndStartSessionUseCase.kt @@ -25,6 +25,7 @@ import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.session.coroutineScope import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.sync.SyncUtils +import im.vector.app.features.settings.devices.v2.notification.UpdateNotificationSettingsAccountDataUseCase import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import timber.log.Timber @@ -36,6 +37,7 @@ class ConfigureAndStartSessionUseCase @Inject constructor( private val updateMatrixClientInfoUseCase: UpdateMatrixClientInfoUseCase, private val vectorPreferences: VectorPreferences, private val enableNotificationsSettingUpdater: EnableNotificationsSettingUpdater, + private val updateNotificationSettingsAccountDataUseCase: UpdateNotificationSettingsAccountDataUseCase, ) { fun execute(session: Session, startSyncing: Boolean = true) { @@ -49,11 +51,24 @@ class ConfigureAndStartSessionUseCase @Inject constructor( } session.pushersService().refreshPushers() webRtcCallManager.checkForProtocolsSupportIfNeeded() + updateMatrixClientInfoIfNeeded(session) + createNotificationSettingsAccountDataIfNeeded(session) + enableNotificationsSettingUpdater.onSessionsStarted(session) + } + + private fun updateMatrixClientInfoIfNeeded(session: Session) { session.coroutineScope.launch { if (vectorPreferences.isClientInfoRecordingEnabled()) { updateMatrixClientInfoUseCase.execute(session) } } - enableNotificationsSettingUpdater.onSessionsStarted(session) + } + + private fun createNotificationSettingsAccountDataIfNeeded(session: Session) { + session.coroutineScope.launch { + if (vectorPreferences.isBackgroundSyncEnabled()) { + updateNotificationSettingsAccountDataUseCase.execute(session) + } + } } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanToggleNotificationsViaAccountDataUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanToggleNotificationsViaAccountDataUseCase.kt index b006e3da45..58289495a4 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanToggleNotificationsViaAccountDataUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanToggleNotificationsViaAccountDataUseCase.kt @@ -16,20 +16,14 @@ package im.vector.app.features.settings.devices.v2.notification -import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes -import org.matrix.android.sdk.api.session.events.model.toModel import javax.inject.Inject -class CheckIfCanToggleNotificationsViaAccountDataUseCase @Inject constructor() { +class CheckIfCanToggleNotificationsViaAccountDataUseCase @Inject constructor( + private val getNotificationSettingsAccountDataUseCase: GetNotificationSettingsAccountDataUseCase, +) { fun execute(session: Session, deviceId: String): Boolean { - return session - .accountDataService() - .getUserAccountDataEvent(UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId) - ?.content - .toModel() - ?.isSilenced != null + return getNotificationSettingsAccountDataUseCase.execute(session, deviceId)?.isSilenced != null } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/DeleteNotificationSettingsAccountDataUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/DeleteNotificationSettingsAccountDataUseCase.kt index 51c24e500c..d71eebdf8a 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/DeleteNotificationSettingsAccountDataUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/DeleteNotificationSettingsAccountDataUseCase.kt @@ -17,17 +17,22 @@ package im.vector.app.features.settings.devices.v2.notification import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent +import org.matrix.android.sdk.api.session.Session import javax.inject.Inject +/** + * Delete the content of any associated notification settings to the current session. + */ class DeleteNotificationSettingsAccountDataUseCase @Inject constructor( private val setNotificationSettingsAccountDataUseCase: SetNotificationSettingsAccountDataUseCase, ) { - // TODO to be called when switching to push notifications method - suspend fun execute(deviceId: String) { + // TODO to be called when switching to push notifications method (check notification method setting) + suspend fun execute(session: Session) { + val deviceId = session.sessionParams.deviceId ?: return val emptyNotificationSettingsContent = LocalNotificationSettingsContent( isSilenced = null ) - setNotificationSettingsAccountDataUseCase.execute(deviceId, emptyNotificationSettingsContent) + setNotificationSettingsAccountDataUseCase.execute(session, deviceId, emptyNotificationSettingsContent) } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataUseCase.kt new file mode 100644 index 0000000000..5517fa0978 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataUseCase.kt @@ -0,0 +1,34 @@ +/* + * 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.settings.devices.v2.notification + +import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes +import org.matrix.android.sdk.api.session.events.model.toModel +import javax.inject.Inject + +class GetNotificationSettingsAccountDataUseCase @Inject constructor() { + + fun execute(session: Session, deviceId: String): LocalNotificationSettingsContent? { + return session + .accountDataService() + .getUserAccountDataEvent(UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId) + ?.content + .toModel() + } +} diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/SetNotificationSettingsAccountDataUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/SetNotificationSettingsAccountDataUseCase.kt index f0ec9d5ddc..7306794f16 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/SetNotificationSettingsAccountDataUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/SetNotificationSettingsAccountDataUseCase.kt @@ -16,18 +16,15 @@ package im.vector.app.features.settings.devices.v2.notification -import im.vector.app.core.di.ActiveSessionHolder import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent +import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes import org.matrix.android.sdk.api.session.events.model.toContent import javax.inject.Inject -class SetNotificationSettingsAccountDataUseCase @Inject constructor( - private val activeSessionHolder: ActiveSessionHolder, -) { +class SetNotificationSettingsAccountDataUseCase @Inject constructor() { - suspend fun execute(deviceId: String, localNotificationSettingsContent: LocalNotificationSettingsContent) { - val session = activeSessionHolder.getSafeActiveSession() ?: return + suspend fun execute(session: Session, deviceId: String, localNotificationSettingsContent: LocalNotificationSettingsContent) { session.accountDataService().updateUserAccountData( UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId, localNotificationSettingsContent.toContent(), diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/ToggleNotificationUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/ToggleNotificationUseCase.kt index d0e1ea2a7a..73a81a6de1 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/ToggleNotificationUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/ToggleNotificationUseCase.kt @@ -39,7 +39,7 @@ class ToggleNotificationUseCase @Inject constructor( if (checkIfCanToggleNotificationsViaAccountDataUseCase.execute(session, deviceId)) { val newNotificationSettingsContent = LocalNotificationSettingsContent(isSilenced = !enabled) - setNotificationSettingsAccountDataUseCase.execute(deviceId, newNotificationSettingsContent) + setNotificationSettingsAccountDataUseCase.execute(session, deviceId, newNotificationSettingsContent) } } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CreateNotificationSettingsAccountDataUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/UpdateNotificationSettingsAccountDataUseCase.kt similarity index 57% rename from vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CreateNotificationSettingsAccountDataUseCase.kt rename to vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/UpdateNotificationSettingsAccountDataUseCase.kt index e2ee19e5cd..596be90abb 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CreateNotificationSettingsAccountDataUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/UpdateNotificationSettingsAccountDataUseCase.kt @@ -21,18 +21,25 @@ import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent import org.matrix.android.sdk.api.session.Session import javax.inject.Inject -class CreateNotificationSettingsAccountDataUseCase @Inject constructor( +/** + * Update the notification settings account data for the current session. + */ +class UpdateNotificationSettingsAccountDataUseCase @Inject constructor( private val vectorPreferences: VectorPreferences, + private val getNotificationSettingsAccountDataUseCase: GetNotificationSettingsAccountDataUseCase, private val setNotificationSettingsAccountDataUseCase: SetNotificationSettingsAccountDataUseCase ) { - // TODO to be called on session start when background sync is enabled + when switching to background sync + // TODO to be called when switching to background sync (in notification method setting) suspend fun execute(session: Session) { val deviceId = session.sessionParams.deviceId ?: return - val isSilenced = !vectorPreferences.areNotificationEnabledForDevice() - val notificationSettingsContent = LocalNotificationSettingsContent( - isSilenced = isSilenced - ) - setNotificationSettingsAccountDataUseCase.execute(deviceId, notificationSettingsContent) + val isSilencedLocal = !vectorPreferences.areNotificationEnabledForDevice() + val isSilencedRemote = getNotificationSettingsAccountDataUseCase.execute(session, deviceId)?.isSilenced + if (isSilencedLocal != isSilencedRemote) { + val notificationSettingsContent = LocalNotificationSettingsContent( + isSilenced = isSilencedLocal + ) + setNotificationSettingsAccountDataUseCase.execute(session, deviceId, notificationSettingsContent) + } } } diff --git a/vector/src/test/java/im/vector/app/core/session/ConfigureAndStartSessionUseCaseTest.kt b/vector/src/test/java/im/vector/app/core/session/ConfigureAndStartSessionUseCaseTest.kt index 01596e796d..23a3629efe 100644 --- a/vector/src/test/java/im/vector/app/core/session/ConfigureAndStartSessionUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/core/session/ConfigureAndStartSessionUseCaseTest.kt @@ -20,6 +20,7 @@ import im.vector.app.core.extensions.startSyncing import im.vector.app.core.session.clientinfo.UpdateMatrixClientInfoUseCase import im.vector.app.features.session.coroutineScope import im.vector.app.features.sync.SyncUtils +import im.vector.app.features.settings.devices.v2.notification.UpdateNotificationSettingsAccountDataUseCase import im.vector.app.test.fakes.FakeContext import im.vector.app.test.fakes.FakeEnableNotificationsSettingUpdater import im.vector.app.test.fakes.FakeSession @@ -47,6 +48,7 @@ class ConfigureAndStartSessionUseCaseTest { private val fakeUpdateMatrixClientInfoUseCase = mockk() private val fakeVectorPreferences = FakeVectorPreferences() private val fakeEnableNotificationsSettingUpdater = FakeEnableNotificationsSettingUpdater() + private val fakeUpdateNotificationSettingsAccountDataUseCase = mockk() private val configureAndStartSessionUseCase = ConfigureAndStartSessionUseCase( context = fakeContext.instance, @@ -54,6 +56,7 @@ class ConfigureAndStartSessionUseCaseTest { updateMatrixClientInfoUseCase = fakeUpdateMatrixClientInfoUseCase, vectorPreferences = fakeVectorPreferences.instance, enableNotificationsSettingUpdater = fakeEnableNotificationsSettingUpdater.instance, + updateNotificationSettingsAccountDataUseCase = fakeUpdateNotificationSettingsAccountDataUseCase, ) @Before @@ -68,47 +71,55 @@ class ConfigureAndStartSessionUseCaseTest { } @Test - fun `given start sync needed and client info recording enabled when execute then it should be configured properly`() = runTest { + fun `given start sync needed and enabled related preferences when execute then it should be configured properly`() = runTest { // Given - val fakeSession = givenASession() - every { fakeSession.coroutineScope } returns this + val aSession = givenASession() + every { aSession.coroutineScope } returns this fakeWebRtcCallManager.givenCheckForProtocolsSupportIfNeededSucceeds() coJustRun { fakeUpdateMatrixClientInfoUseCase.execute(any()) } + coJustRun { fakeUpdateNotificationSettingsAccountDataUseCase.execute(any()) } fakeVectorPreferences.givenIsClientInfoRecordingEnabled(isEnabled = true) - fakeEnableNotificationsSettingUpdater.givenOnSessionsStarted(fakeSession) + fakeVectorPreferences.givenIsBackgroundSyncEnabled(isEnabled = true) + fakeEnableNotificationsSettingUpdater.givenOnSessionsStarted(aSession) // When - configureAndStartSessionUseCase.execute(fakeSession, startSyncing = true) + configureAndStartSessionUseCase.execute(aSession, startSyncing = true) advanceUntilIdle() // Then - verify { fakeSession.startSyncing(fakeContext.instance) } - fakeSession.fakeFilterService.verifySetSyncFilter(SyncUtils.getSyncFilterBuilder()) - fakeSession.fakePushersService.verifyRefreshPushers() + verify { aSession.startSyncing(fakeContext.instance) } + aSession.fakeFilterService.verifySetSyncFilter(SyncUtils.getSyncFilterBuilder()) + aSession.fakePushersService.verifyRefreshPushers() fakeWebRtcCallManager.verifyCheckForProtocolsSupportIfNeeded() - coVerify { fakeUpdateMatrixClientInfoUseCase.execute(fakeSession) } + coVerify { + fakeUpdateMatrixClientInfoUseCase.execute(aSession) + fakeUpdateNotificationSettingsAccountDataUseCase.execute(aSession) + } } @Test - fun `given start sync needed and client info recording disabled when execute then it should be configured properly`() = runTest { + fun `given start sync needed and disabled related preferences when execute then it should be configured properly`() = runTest { // Given - val fakeSession = givenASession() - every { fakeSession.coroutineScope } returns this + val aSession = givenASession() + every { aSession.coroutineScope } returns this fakeWebRtcCallManager.givenCheckForProtocolsSupportIfNeededSucceeds() - coJustRun { fakeUpdateMatrixClientInfoUseCase.execute(any()) } fakeVectorPreferences.givenIsClientInfoRecordingEnabled(isEnabled = false) - fakeEnableNotificationsSettingUpdater.givenOnSessionsStarted(fakeSession) + fakeVectorPreferences.givenIsBackgroundSyncEnabled(isEnabled = false) + fakeEnableNotificationsSettingUpdater.givenOnSessionsStarted(aSession) // When - configureAndStartSessionUseCase.execute(fakeSession, startSyncing = true) + configureAndStartSessionUseCase.execute(aSession, startSyncing = true) advanceUntilIdle() // Then - verify { fakeSession.startSyncing(fakeContext.instance) } - fakeSession.fakeFilterService.verifySetSyncFilter(SyncUtils.getSyncFilterBuilder()) - fakeSession.fakePushersService.verifyRefreshPushers() + verify { aSession.startSyncing(fakeContext.instance) } + aSession.fakeFilterService.verifySetSyncFilter(SyncUtils.getSyncFilterBuilder()) + aSession.fakePushersService.verifyRefreshPushers() fakeWebRtcCallManager.verifyCheckForProtocolsSupportIfNeeded() - coVerify(inverse = true) { fakeUpdateMatrixClientInfoUseCase.execute(fakeSession) } + coVerify(inverse = true) { + fakeUpdateMatrixClientInfoUseCase.execute(aSession) + fakeUpdateNotificationSettingsAccountDataUseCase.execute(aSession) + } } @Test @@ -118,7 +129,9 @@ class ConfigureAndStartSessionUseCaseTest { every { fakeSession.coroutineScope } returns this fakeWebRtcCallManager.givenCheckForProtocolsSupportIfNeededSucceeds() coJustRun { fakeUpdateMatrixClientInfoUseCase.execute(any()) } + coJustRun { fakeUpdateNotificationSettingsAccountDataUseCase.execute(any()) } fakeVectorPreferences.givenIsClientInfoRecordingEnabled(isEnabled = true) + fakeVectorPreferences.givenIsBackgroundSyncEnabled(isEnabled = true) fakeEnableNotificationsSettingUpdater.givenOnSessionsStarted(fakeSession) // When @@ -130,7 +143,10 @@ class ConfigureAndStartSessionUseCaseTest { fakeSession.fakeFilterService.verifySetSyncFilter(SyncUtils.getSyncFilterBuilder()) fakeSession.fakePushersService.verifyRefreshPushers() fakeWebRtcCallManager.verifyCheckForProtocolsSupportIfNeeded() - coVerify { fakeUpdateMatrixClientInfoUseCase.execute(fakeSession) } + coVerify { + fakeUpdateMatrixClientInfoUseCase.execute(fakeSession) + fakeUpdateNotificationSettingsAccountDataUseCase.execute(fakeSession) + } } private fun givenASession(): FakeSession { diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanToggleNotificationsViaAccountDataUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanToggleNotificationsViaAccountDataUseCaseTest.kt index de225d36ac..f97e326a02 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanToggleNotificationsViaAccountDataUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CheckIfCanToggleNotificationsViaAccountDataUseCaseTest.kt @@ -17,31 +17,29 @@ package im.vector.app.features.settings.devices.v2.notification import im.vector.app.test.fakes.FakeSession +import io.mockk.every import io.mockk.mockk import org.amshove.kluent.shouldBeEqualTo import org.junit.Test import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent -import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes -import org.matrix.android.sdk.api.session.events.model.toContent private const val A_DEVICE_ID = "device-id" class CheckIfCanToggleNotificationsViaAccountDataUseCaseTest { + private val fakeGetNotificationSettingsAccountDataUseCase = mockk() private val fakeSession = FakeSession() private val checkIfCanToggleNotificationsViaAccountDataUseCase = - CheckIfCanToggleNotificationsViaAccountDataUseCase() + CheckIfCanToggleNotificationsViaAccountDataUseCase( + getNotificationSettingsAccountDataUseCase = fakeGetNotificationSettingsAccountDataUseCase, + ) @Test fun `given current session and an account data with a content for the device id when execute then result is true`() { // Given - fakeSession - .accountDataService() - .givenGetUserAccountDataEventReturns( - type = UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + A_DEVICE_ID, - content = LocalNotificationSettingsContent(isSilenced = true).toContent(), - ) + val content = LocalNotificationSettingsContent(isSilenced = true) + every { fakeGetNotificationSettingsAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) } returns content // When val result = checkIfCanToggleNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) @@ -53,12 +51,8 @@ class CheckIfCanToggleNotificationsViaAccountDataUseCaseTest { @Test fun `given current session and an account data with empty content for the device id when execute then result is false`() { // Given - fakeSession - .accountDataService() - .givenGetUserAccountDataEventReturns( - type = UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + A_DEVICE_ID, - content = mockk(), - ) + val content = LocalNotificationSettingsContent(isSilenced = null) + every { fakeGetNotificationSettingsAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) } returns content // When val result = checkIfCanToggleNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) @@ -70,12 +64,8 @@ class CheckIfCanToggleNotificationsViaAccountDataUseCaseTest { @Test fun `given current session and NO account data for the device id when execute then result is false`() { // Given - fakeSession - .accountDataService() - .givenGetUserAccountDataEventReturns( - type = UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + A_DEVICE_ID, - content = null, - ) + val content = null + every { fakeGetNotificationSettingsAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) } returns content // When val result = checkIfCanToggleNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CreateNotificationSettingsAccountDataUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CreateNotificationSettingsAccountDataUseCaseTest.kt deleted file mode 100644 index e4cadaa005..0000000000 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CreateNotificationSettingsAccountDataUseCaseTest.kt +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2022 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.app.features.settings.devices.v2.notification - -import im.vector.app.test.fakes.FakeSession -import im.vector.app.test.fakes.FakeVectorPreferences -import io.mockk.coJustRun -import io.mockk.coVerify -import io.mockk.mockk -import io.mockk.verify -import kotlinx.coroutines.test.runTest -import org.junit.Test -import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent - -class CreateNotificationSettingsAccountDataUseCaseTest { - - private val fakeVectorPreferences = FakeVectorPreferences() - private val fakeSetNotificationSettingsAccountDataUseCase = mockk() - - private val createNotificationSettingsAccountDataUseCase = CreateNotificationSettingsAccountDataUseCase( - vectorPreferences = fakeVectorPreferences.instance, - setNotificationSettingsAccountDataUseCase = fakeSetNotificationSettingsAccountDataUseCase, - ) - - @Test - fun `given a device id when execute then content with the current notification preference is set for the account data`() = runTest { - // Given - val aDeviceId = "device-id" - val session = FakeSession() - session.givenSessionId(aDeviceId) - coJustRun { fakeSetNotificationSettingsAccountDataUseCase.execute(any(), any()) } - val areNotificationsEnabled = true - fakeVectorPreferences.givenAreNotificationEnabled(areNotificationsEnabled) - val expectedContent = LocalNotificationSettingsContent( - isSilenced = !areNotificationsEnabled - ) - - // When - createNotificationSettingsAccountDataUseCase.execute(session) - - // Then - verify { fakeVectorPreferences.instance.areNotificationEnabledForDevice() } - coVerify { fakeSetNotificationSettingsAccountDataUseCase.execute(aDeviceId, expectedContent) } - } -} diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/DeleteNotificationSettingsAccountDataUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/DeleteNotificationSettingsAccountDataUseCaseTest.kt index 038a1f436a..d84ff8c6ac 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/DeleteNotificationSettingsAccountDataUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/DeleteNotificationSettingsAccountDataUseCaseTest.kt @@ -16,6 +16,7 @@ package im.vector.app.features.settings.devices.v2.notification +import im.vector.app.test.fakes.FakeSession import io.mockk.coJustRun import io.mockk.coVerify import io.mockk.mockk @@ -35,15 +36,17 @@ class DeleteNotificationSettingsAccountDataUseCaseTest { fun `given a device id when execute then empty content is set for the account data`() = runTest { // Given val aDeviceId = "device-id" - coJustRun { fakeSetNotificationSettingsAccountDataUseCase.execute(any(), any()) } + val aSession = FakeSession() + aSession.givenSessionId(aDeviceId) + coJustRun { fakeSetNotificationSettingsAccountDataUseCase.execute(any(), any(), any()) } val expectedContent = LocalNotificationSettingsContent( isSilenced = null ) // When - deleteNotificationSettingsAccountDataUseCase.execute(aDeviceId) + deleteNotificationSettingsAccountDataUseCase.execute(aSession) // Then - coVerify { fakeSetNotificationSettingsAccountDataUseCase.execute(aDeviceId, expectedContent) } + coVerify { fakeSetNotificationSettingsAccountDataUseCase.execute(aSession, aDeviceId, expectedContent) } } } diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataUseCaseTest.kt new file mode 100644 index 0000000000..75179b5679 --- /dev/null +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataUseCaseTest.kt @@ -0,0 +1,49 @@ +/* + * 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.settings.devices.v2.notification + +import im.vector.app.test.fakes.FakeSession +import org.amshove.kluent.shouldBeEqualTo +import org.junit.Test +import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent +import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes +import org.matrix.android.sdk.api.session.events.model.toContent + +class GetNotificationSettingsAccountDataUseCaseTest { + + private val getNotificationSettingsAccountDataUseCase = GetNotificationSettingsAccountDataUseCase() + + @Test + fun `given a device id when execute then retrieve the account data event corresponding to this id if any`() { + // Given + val aDeviceId = "device-id" + val aSession = FakeSession() + val expectedContent = LocalNotificationSettingsContent() + aSession + .accountDataService() + .givenGetUserAccountDataEventReturns( + type = UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + aDeviceId, + content = expectedContent.toContent(), + ) + + // When + val result = getNotificationSettingsAccountDataUseCase.execute(aSession, aDeviceId) + + // Then + result shouldBeEqualTo expectedContent + } +} diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCaseTest.kt index 7c0b0a8693..d4c3aa5788 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCaseTest.kt @@ -53,8 +53,8 @@ class GetNotificationsStatusUseCaseTest { private val getNotificationsStatusUseCase = GetNotificationsStatusUseCase( - checkIfCanTogglePushNotificationsViaAccountDataUseCase = fakeCheckIfCanToggleNotificationsViaAccountDataUseCase, - canTogglePushNotificationsViaPusherUseCase = fakeCanToggleNotificationsViaPusherUseCase, + checkIfCanToggleNotificationsViaAccountDataUseCase = fakeCheckIfCanToggleNotificationsViaAccountDataUseCase, + canToggleNotificationsViaPusherUseCase = fakeCanToggleNotificationsViaPusherUseCase, ) @Before diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/SetNotificationSettingsAccountDataUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/SetNotificationSettingsAccountDataUseCaseTest.kt index 8f72f0946f..d26271e59d 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/SetNotificationSettingsAccountDataUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/SetNotificationSettingsAccountDataUseCaseTest.kt @@ -16,7 +16,7 @@ package im.vector.app.features.settings.devices.v2.notification -import im.vector.app.test.fakes.FakeActiveSessionHolder +import im.vector.app.test.fakes.FakeSession import kotlinx.coroutines.test.runTest import org.junit.Test import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent @@ -25,25 +25,21 @@ import org.matrix.android.sdk.api.session.events.model.toContent class SetNotificationSettingsAccountDataUseCaseTest { - private val activeSessionHolder = FakeActiveSessionHolder() - - private val setNotificationSettingsAccountDataUseCase = SetNotificationSettingsAccountDataUseCase( - activeSessionHolder = activeSessionHolder.instance, - ) + private val setNotificationSettingsAccountDataUseCase = SetNotificationSettingsAccountDataUseCase() @Test fun `given a content when execute then update local notification settings with this content`() = runTest { // Given val sessionId = "a_session_id" val localNotificationSettingsContent = LocalNotificationSettingsContent() - val fakeSession = activeSessionHolder.fakeSession + val fakeSession = FakeSession() fakeSession.accountDataService().givenUpdateUserAccountDataEventSucceeds() // When - setNotificationSettingsAccountDataUseCase.execute(sessionId, localNotificationSettingsContent) + setNotificationSettingsAccountDataUseCase.execute(fakeSession, sessionId, localNotificationSettingsContent) // Then - activeSessionHolder.fakeSession.accountDataService().verifyUpdateUserAccountDataEventSucceeds( + fakeSession.accountDataService().verifyUpdateUserAccountDataEventSucceeds( UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + sessionId, localNotificationSettingsContent.toContent(), ) diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/ToggleNotificationUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/ToggleNotificationUseCaseTest.kt index 99be7c7ea9..1e3517c776 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/ToggleNotificationUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/ToggleNotificationUseCaseTest.kt @@ -39,8 +39,8 @@ class ToggleNotificationUseCaseTest { private val toggleNotificationUseCase = ToggleNotificationUseCase( activeSessionHolder = activeSessionHolder.instance, - checkIfCanTogglePushNotificationsViaPusherUseCase = fakeCheckIfCanToggleNotificationsViaPusherUseCase, - checkIfCanTogglePushNotificationsViaAccountDataUseCase = fakeCheckIfCanToggleNotificationsViaAccountDataUseCase, + checkIfCanToggleNotificationsViaPusherUseCase = fakeCheckIfCanToggleNotificationsViaPusherUseCase, + checkIfCanToggleNotificationsViaAccountDataUseCase = fakeCheckIfCanToggleNotificationsViaAccountDataUseCase, setNotificationSettingsAccountDataUseCase = fakeSetNotificationSettingsAccountDataUseCase, ) @@ -72,7 +72,7 @@ class ToggleNotificationUseCaseTest { val fakeSession = activeSessionHolder.fakeSession every { fakeCheckIfCanToggleNotificationsViaPusherUseCase.execute(fakeSession) } returns false every { fakeCheckIfCanToggleNotificationsViaAccountDataUseCase.execute(fakeSession, sessionId) } returns true - coJustRun { fakeSetNotificationSettingsAccountDataUseCase.execute(any(), any()) } + coJustRun { fakeSetNotificationSettingsAccountDataUseCase.execute(any(), any(), any()) } val expectedLocalNotificationSettingsContent = LocalNotificationSettingsContent( isSilenced = false ) @@ -82,7 +82,7 @@ class ToggleNotificationUseCaseTest { // Then coVerify { - fakeSetNotificationSettingsAccountDataUseCase.execute(sessionId, expectedLocalNotificationSettingsContent) + fakeSetNotificationSettingsAccountDataUseCase.execute(fakeSession, sessionId, expectedLocalNotificationSettingsContent) } } } diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/UpdateNotificationSettingsAccountDataUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/UpdateNotificationSettingsAccountDataUseCaseTest.kt new file mode 100644 index 0000000000..41c5ab9081 --- /dev/null +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/UpdateNotificationSettingsAccountDataUseCaseTest.kt @@ -0,0 +1,97 @@ +/* + * 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.settings.devices.v2.notification + +import im.vector.app.test.fakes.FakeSession +import im.vector.app.test.fakes.FakeVectorPreferences +import io.mockk.coJustRun +import io.mockk.coVerify +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent + +class UpdateNotificationSettingsAccountDataUseCaseTest { + + private val fakeVectorPreferences = FakeVectorPreferences() + private val fakeGetNotificationSettingsAccountDataUseCase = mockk() + private val fakeSetNotificationSettingsAccountDataUseCase = mockk() + + private val updateNotificationSettingsAccountDataUseCase = UpdateNotificationSettingsAccountDataUseCase( + vectorPreferences = fakeVectorPreferences.instance, + getNotificationSettingsAccountDataUseCase = fakeGetNotificationSettingsAccountDataUseCase, + setNotificationSettingsAccountDataUseCase = fakeSetNotificationSettingsAccountDataUseCase, + ) + + @Test + fun `given a device id and a different local setting compared to remote when execute then content is updated`() = runTest { + // Given + val aDeviceId = "device-id" + val aSession = FakeSession() + aSession.givenSessionId(aDeviceId) + coJustRun { fakeSetNotificationSettingsAccountDataUseCase.execute(any(), any(), any()) } + val areNotificationsEnabled = true + fakeVectorPreferences.givenAreNotificationEnabled(areNotificationsEnabled) + every { fakeGetNotificationSettingsAccountDataUseCase.execute(any(), any()) } returns + LocalNotificationSettingsContent( + isSilenced = null + ) + val expectedContent = LocalNotificationSettingsContent( + isSilenced = !areNotificationsEnabled + ) + + // When + updateNotificationSettingsAccountDataUseCase.execute(aSession) + + // Then + verify { + fakeVectorPreferences.instance.areNotificationEnabledForDevice() + fakeGetNotificationSettingsAccountDataUseCase.execute(aSession, aDeviceId) + } + coVerify { fakeSetNotificationSettingsAccountDataUseCase.execute(aSession, aDeviceId, expectedContent) } + } + + @Test + fun `given a device id and a same local setting compared to remote when execute then content is not updated`() = runTest { + // Given + val aDeviceId = "device-id" + val aSession = FakeSession() + aSession.givenSessionId(aDeviceId) + coJustRun { fakeSetNotificationSettingsAccountDataUseCase.execute(any(), any(), any()) } + val areNotificationsEnabled = true + fakeVectorPreferences.givenAreNotificationEnabled(areNotificationsEnabled) + every { fakeGetNotificationSettingsAccountDataUseCase.execute(any(), any()) } returns + LocalNotificationSettingsContent( + isSilenced = false + ) + val expectedContent = LocalNotificationSettingsContent( + isSilenced = !areNotificationsEnabled + ) + + // When + updateNotificationSettingsAccountDataUseCase.execute(aSession) + + // Then + verify { + fakeVectorPreferences.instance.areNotificationEnabledForDevice() + fakeGetNotificationSettingsAccountDataUseCase.execute(aSession, aDeviceId) + } + coVerify(inverse = true) { fakeSetNotificationSettingsAccountDataUseCase.execute(aSession, aDeviceId, expectedContent) } + } +} diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt index 287bdd159c..901c0331c5 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt @@ -30,7 +30,7 @@ import im.vector.app.test.fakes.FakeActiveSessionHolder import im.vector.app.test.fakes.FakeGetNotificationsStatusUseCase import im.vector.app.test.fakes.FakePendingAuthHandler import im.vector.app.test.fakes.FakeSignoutSessionsUseCase -import im.vector.app.test.fakes.FakeTogglePushNotificationUseCase +import im.vector.app.test.fakes.FakeToggleNotificationUseCase import im.vector.app.test.fakes.FakeVectorPreferences import im.vector.app.test.fakes.FakeVerificationService import im.vector.app.test.test @@ -76,7 +76,7 @@ class SessionOverviewViewModelTest { private val interceptSignoutFlowResponseUseCase = mockk() private val fakePendingAuthHandler = FakePendingAuthHandler() private val refreshDevicesUseCase = mockk(relaxed = true) - private val togglePushNotificationUseCase = FakeTogglePushNotificationUseCase() + private val toggleNotificationUseCase = FakeToggleNotificationUseCase() private val fakeGetNotificationsStatusUseCase = FakeGetNotificationsStatusUseCase() private val notificationsStatus = NotificationsStatus.ENABLED private val fakeVectorPreferences = FakeVectorPreferences() @@ -91,7 +91,7 @@ class SessionOverviewViewModelTest { pendingAuthHandler = fakePendingAuthHandler.instance, activeSessionHolder = fakeActiveSessionHolder.instance, refreshDevicesUseCase = refreshDevicesUseCase, - togglePushNotificationUseCase = togglePushNotificationUseCase.instance, + toggleNotificationUseCase = toggleNotificationUseCase.instance, getNotificationsStatusUseCase = fakeGetNotificationsStatusUseCase.instance, vectorPreferences = fakeVectorPreferences.instance, toggleIpAddressVisibilityUseCase = toggleIpAddressVisibilityUseCase, @@ -436,7 +436,7 @@ class SessionOverviewViewModelTest { viewModel.handle(SessionOverviewAction.TogglePushNotifications(A_SESSION_ID_1, true)) - togglePushNotificationUseCase.verifyExecute(A_SESSION_ID_1, true) + toggleNotificationUseCase.verifyExecute(A_SESSION_ID_1, true) viewModel.test().assertLatestState { state -> state.notificationsStatus == NotificationsStatus.ENABLED }.finish() } } diff --git a/vector/src/test/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCaseTest.kt index 153b79f1a8..e53874858a 100644 --- a/vector/src/test/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCaseTest.kt @@ -16,11 +16,9 @@ package im.vector.app.features.settings.notifications +import im.vector.app.core.pushers.UnregisterUnifiedPushUseCase import im.vector.app.features.settings.devices.v2.notification.CheckIfCanToggleNotificationsViaPusherUseCase import im.vector.app.features.settings.devices.v2.notification.ToggleNotificationUseCase -import im.vector.app.core.pushers.UnregisterUnifiedPushUseCase -import im.vector.app.features.settings.devices.v2.notification.CheckIfCanTogglePushNotificationsViaPusherUseCase -import im.vector.app.features.settings.devices.v2.notification.TogglePushNotificationUseCase import im.vector.app.test.fakes.FakeActiveSessionHolder import im.vector.app.test.fakes.FakePushersManager import io.mockk.coJustRun diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeTogglePushNotificationUseCase.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeToggleNotificationUseCase.kt similarity index 96% rename from vector/src/test/java/im/vector/app/test/fakes/FakeTogglePushNotificationUseCase.kt rename to vector/src/test/java/im/vector/app/test/fakes/FakeToggleNotificationUseCase.kt index cc42193fe1..527625144e 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeTogglePushNotificationUseCase.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeToggleNotificationUseCase.kt @@ -21,7 +21,7 @@ import io.mockk.coJustRun import io.mockk.coVerify import io.mockk.mockk -class FakeTogglePushNotificationUseCase { +class FakeToggleNotificationUseCase { val instance = mockk { coJustRun { execute(any(), any()) } diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt index 06efca1bf7..58bc1a18b8 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt @@ -73,4 +73,8 @@ class FakeVectorPreferences { fun givenAreNotificationsEnabledForDevice(notificationsEnabled: Boolean) { every { instance.areNotificationEnabledForDevice() } returns notificationsEnabled } + + fun givenIsBackgroundSyncEnabled(isEnabled: Boolean) { + every { instance.isBackgroundSyncEnabled() } returns isEnabled + } } From e99dc1d163008123520b5e9cfb58d7ac7795fef9 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Fri, 18 Nov 2022 17:17:05 +0100 Subject: [PATCH 059/108] Remove unused parameters from some ViewModel --- .../vector/app/features/settings/devices/v2/DevicesViewModel.kt | 1 - .../settings/devices/v2/overview/SessionOverviewViewModel.kt | 1 - .../app/features/settings/devices/v2/DevicesViewModelTest.kt | 2 -- .../devices/v2/overview/SessionOverviewViewModelTest.kt | 2 -- 4 files changed, 6 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt index f42d5af398..bfccd2f9d3 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt @@ -48,7 +48,6 @@ class DevicesViewModel @AssistedInject constructor( private val refreshDevicesOnCryptoDevicesChangeUseCase: RefreshDevicesOnCryptoDevicesChangeUseCase, private val checkIfCurrentSessionCanBeVerifiedUseCase: CheckIfCurrentSessionCanBeVerifiedUseCase, private val signoutSessionsUseCase: SignoutSessionsUseCase, - private val interceptSignoutFlowResponseUseCase: InterceptSignoutFlowResponseUseCase, private val pendingAuthHandler: PendingAuthHandler, refreshDevicesUseCase: RefreshDevicesUseCase, private val vectorPreferences: VectorPreferences, diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt index 0ddf688514..74f962b464 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt @@ -51,7 +51,6 @@ class SessionOverviewViewModel @AssistedInject constructor( private val getDeviceFullInfoUseCase: GetDeviceFullInfoUseCase, private val checkIfCurrentSessionCanBeVerifiedUseCase: CheckIfCurrentSessionCanBeVerifiedUseCase, private val signoutSessionsUseCase: SignoutSessionsUseCase, - private val interceptSignoutFlowResponseUseCase: InterceptSignoutFlowResponseUseCase, private val pendingAuthHandler: PendingAuthHandler, private val activeSessionHolder: ActiveSessionHolder, private val toggleNotificationUseCase: ToggleNotificationUseCase, diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt index 03177aac47..aa5ebd73eb 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt @@ -72,7 +72,6 @@ class DevicesViewModelTest { private val refreshDevicesOnCryptoDevicesChangeUseCase = mockk(relaxed = true) private val checkIfCurrentSessionCanBeVerifiedUseCase = mockk() private val fakeSignoutSessionsUseCase = FakeSignoutSessionsUseCase() - private val fakeInterceptSignoutFlowResponseUseCase = mockk() private val fakePendingAuthHandler = FakePendingAuthHandler() private val fakeRefreshDevicesUseCase = mockk(relaxUnitFun = true) private val fakeVectorPreferences = FakeVectorPreferences() @@ -87,7 +86,6 @@ class DevicesViewModelTest { refreshDevicesOnCryptoDevicesChangeUseCase = refreshDevicesOnCryptoDevicesChangeUseCase, checkIfCurrentSessionCanBeVerifiedUseCase = checkIfCurrentSessionCanBeVerifiedUseCase, signoutSessionsUseCase = fakeSignoutSessionsUseCase.instance, - interceptSignoutFlowResponseUseCase = fakeInterceptSignoutFlowResponseUseCase, pendingAuthHandler = fakePendingAuthHandler.instance, refreshDevicesUseCase = fakeRefreshDevicesUseCase, vectorPreferences = fakeVectorPreferences.instance, diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt index 901c0331c5..2ddc91cdac 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt @@ -73,7 +73,6 @@ class SessionOverviewViewModelTest { private val fakeActiveSessionHolder = FakeActiveSessionHolder() private val checkIfCurrentSessionCanBeVerifiedUseCase = mockk() private val fakeSignoutSessionsUseCase = FakeSignoutSessionsUseCase() - private val interceptSignoutFlowResponseUseCase = mockk() private val fakePendingAuthHandler = FakePendingAuthHandler() private val refreshDevicesUseCase = mockk(relaxed = true) private val toggleNotificationUseCase = FakeToggleNotificationUseCase() @@ -87,7 +86,6 @@ class SessionOverviewViewModelTest { getDeviceFullInfoUseCase = getDeviceFullInfoUseCase, checkIfCurrentSessionCanBeVerifiedUseCase = checkIfCurrentSessionCanBeVerifiedUseCase, signoutSessionsUseCase = fakeSignoutSessionsUseCase.instance, - interceptSignoutFlowResponseUseCase = interceptSignoutFlowResponseUseCase, pendingAuthHandler = fakePendingAuthHandler.instance, activeSessionHolder = fakeActiveSessionHolder.instance, refreshDevicesUseCase = refreshDevicesUseCase, From 637961bbb1043fd39ac0844d09eeb65dc7b30f0a Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 23 Nov 2022 14:45:37 +0100 Subject: [PATCH 060/108] Update related account data event on notification method change --- .../LocalNotificationSettingsContent.kt | 2 +- .../EnableNotificationsSettingUpdater.kt | 39 ---------- .../NotificationsSettingUpdater.kt | 74 +++++++++++++++++++ .../ConfigureAndStartSessionUseCase.kt | 10 +-- ...eNotificationSettingsAccountDataUseCase.kt | 12 +-- ...eNotificationSettingsAccountDataUseCase.kt | 27 +++++-- .../ConfigureAndStartSessionUseCaseTest.kt | 38 +++++----- ...ificationSettingsAccountDataUseCaseTest.kt | 28 ++++++- ...ificationSettingsAccountDataUseCaseTest.kt | 22 +++++- ...ificationSettingsAccountDataUseCaseTest.kt | 2 +- ...ificationSettingsAccountDataUseCaseTest.kt | 32 +++++++- ....kt => FakeNotificationsSettingUpdater.kt} | 6 +- 12 files changed, 207 insertions(+), 85 deletions(-) delete mode 100644 vector/src/main/java/im/vector/app/core/notification/EnableNotificationsSettingUpdater.kt create mode 100644 vector/src/main/java/im/vector/app/core/notification/NotificationsSettingUpdater.kt rename vector/src/test/java/im/vector/app/test/fakes/{FakeEnableNotificationsSettingUpdater.kt => FakeNotificationsSettingUpdater.kt} (82%) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/account/LocalNotificationSettingsContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/account/LocalNotificationSettingsContent.kt index 6998d9dcf2..75d04f340a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/account/LocalNotificationSettingsContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/account/LocalNotificationSettingsContent.kt @@ -22,5 +22,5 @@ import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) data class LocalNotificationSettingsContent( @Json(name = "is_silenced") - val isSilenced: Boolean? = false + val isSilenced: Boolean? ) diff --git a/vector/src/main/java/im/vector/app/core/notification/EnableNotificationsSettingUpdater.kt b/vector/src/main/java/im/vector/app/core/notification/EnableNotificationsSettingUpdater.kt deleted file mode 100644 index 81b524cde9..0000000000 --- a/vector/src/main/java/im/vector/app/core/notification/EnableNotificationsSettingUpdater.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2022 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.app.core.notification - -import im.vector.app.features.session.coroutineScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch -import org.matrix.android.sdk.api.session.Session -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class EnableNotificationsSettingUpdater @Inject constructor( - private val updateEnableNotificationsSettingOnChangeUseCase: UpdateEnableNotificationsSettingOnChangeUseCase, -) { - - private var job: Job? = null - - fun onSessionsStarted(session: Session) { - job?.cancel() - job = session.coroutineScope.launch { - updateEnableNotificationsSettingOnChangeUseCase.execute(session) - } - } -} diff --git a/vector/src/main/java/im/vector/app/core/notification/NotificationsSettingUpdater.kt b/vector/src/main/java/im/vector/app/core/notification/NotificationsSettingUpdater.kt new file mode 100644 index 0000000000..c6738edddb --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/notification/NotificationsSettingUpdater.kt @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.core.notification + +import android.content.SharedPreferences.OnSharedPreferenceChangeListener +import im.vector.app.features.session.coroutineScope +import im.vector.app.features.settings.VectorPreferences +import im.vector.app.features.settings.VectorPreferences.Companion.SETTINGS_FDROID_BACKGROUND_SYNC_MODE +import im.vector.app.features.settings.devices.v2.notification.UpdateNotificationSettingsAccountDataUseCase +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.session.Session +import javax.inject.Inject +import javax.inject.Singleton + +/** + * Listen changes in Pusher or Account Data to update the local setting for notification toggle. + * Listen changes on background sync mode preference to update the corresponding Account Data event. + */ +@Singleton +class NotificationsSettingUpdater @Inject constructor( + private val updateEnableNotificationsSettingOnChangeUseCase: UpdateEnableNotificationsSettingOnChangeUseCase, + private val vectorPreferences: VectorPreferences, + private val updateNotificationSettingsAccountDataUseCase: UpdateNotificationSettingsAccountDataUseCase, +) { + + private var job: Job? = null + private var prefChangeListener: OnSharedPreferenceChangeListener? = null + + // TODO add unit tests + fun onSessionsStarted(session: Session) { + updateEnableNotificationsSettingOnChange(session) + updateNotificationSettingsAccountDataOnChange(session) + } + + private fun updateEnableNotificationsSettingOnChange(session: Session) { + job?.cancel() + job = session.coroutineScope.launch { + updateEnableNotificationsSettingOnChangeUseCase.execute(session) + } + } + + private fun updateNotificationSettingsAccountDataOnChange(session: Session) { + prefChangeListener?.let { vectorPreferences.unsubscribeToChanges(it) } + prefChangeListener = null + prefChangeListener = createPrefListener(session).also { + vectorPreferences.subscribeToChanges(it) + } + } + + private fun createPrefListener(session: Session): OnSharedPreferenceChangeListener { + return OnSharedPreferenceChangeListener { _, key -> + session.coroutineScope.launch { + if (key == SETTINGS_FDROID_BACKGROUND_SYNC_MODE) { + updateNotificationSettingsAccountDataUseCase.execute(session) + } + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/core/session/ConfigureAndStartSessionUseCase.kt b/vector/src/main/java/im/vector/app/core/session/ConfigureAndStartSessionUseCase.kt index d9688a45ed..623f7d83a9 100644 --- a/vector/src/main/java/im/vector/app/core/session/ConfigureAndStartSessionUseCase.kt +++ b/vector/src/main/java/im/vector/app/core/session/ConfigureAndStartSessionUseCase.kt @@ -19,7 +19,7 @@ package im.vector.app.core.session import android.content.Context import dagger.hilt.android.qualifiers.ApplicationContext import im.vector.app.core.extensions.startSyncing -import im.vector.app.core.notification.EnableNotificationsSettingUpdater +import im.vector.app.core.notification.NotificationsSettingUpdater import im.vector.app.core.session.clientinfo.UpdateMatrixClientInfoUseCase import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.session.coroutineScope @@ -36,7 +36,7 @@ class ConfigureAndStartSessionUseCase @Inject constructor( private val webRtcCallManager: WebRtcCallManager, private val updateMatrixClientInfoUseCase: UpdateMatrixClientInfoUseCase, private val vectorPreferences: VectorPreferences, - private val enableNotificationsSettingUpdater: EnableNotificationsSettingUpdater, + private val notificationsSettingUpdater: NotificationsSettingUpdater, private val updateNotificationSettingsAccountDataUseCase: UpdateNotificationSettingsAccountDataUseCase, ) { @@ -53,7 +53,7 @@ class ConfigureAndStartSessionUseCase @Inject constructor( webRtcCallManager.checkForProtocolsSupportIfNeeded() updateMatrixClientInfoIfNeeded(session) createNotificationSettingsAccountDataIfNeeded(session) - enableNotificationsSettingUpdater.onSessionsStarted(session) + notificationsSettingUpdater.onSessionsStarted(session) } private fun updateMatrixClientInfoIfNeeded(session: Session) { @@ -66,9 +66,7 @@ class ConfigureAndStartSessionUseCase @Inject constructor( private fun createNotificationSettingsAccountDataIfNeeded(session: Session) { session.coroutineScope.launch { - if (vectorPreferences.isBackgroundSyncEnabled()) { - updateNotificationSettingsAccountDataUseCase.execute(session) - } + updateNotificationSettingsAccountDataUseCase.execute(session) } } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/DeleteNotificationSettingsAccountDataUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/DeleteNotificationSettingsAccountDataUseCase.kt index d71eebdf8a..3c086fe111 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/DeleteNotificationSettingsAccountDataUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/DeleteNotificationSettingsAccountDataUseCase.kt @@ -24,15 +24,17 @@ import javax.inject.Inject * Delete the content of any associated notification settings to the current session. */ class DeleteNotificationSettingsAccountDataUseCase @Inject constructor( + private val getNotificationSettingsAccountDataUseCase: GetNotificationSettingsAccountDataUseCase, private val setNotificationSettingsAccountDataUseCase: SetNotificationSettingsAccountDataUseCase, ) { - // TODO to be called when switching to push notifications method (check notification method setting) suspend fun execute(session: Session) { val deviceId = session.sessionParams.deviceId ?: return - val emptyNotificationSettingsContent = LocalNotificationSettingsContent( - isSilenced = null - ) - setNotificationSettingsAccountDataUseCase.execute(session, deviceId, emptyNotificationSettingsContent) + if (getNotificationSettingsAccountDataUseCase.execute(session, deviceId)?.isSilenced != null) { + val emptyNotificationSettingsContent = LocalNotificationSettingsContent( + isSilenced = null + ) + setNotificationSettingsAccountDataUseCase.execute(session, deviceId, emptyNotificationSettingsContent) + } } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/UpdateNotificationSettingsAccountDataUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/UpdateNotificationSettingsAccountDataUseCase.kt index 596be90abb..7791c1dd4b 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/UpdateNotificationSettingsAccountDataUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/UpdateNotificationSettingsAccountDataUseCase.kt @@ -22,24 +22,37 @@ import org.matrix.android.sdk.api.session.Session import javax.inject.Inject /** - * Update the notification settings account data for the current session. + * Update the notification settings account data for the current session depending on whether + * the background sync is enabled or not. */ class UpdateNotificationSettingsAccountDataUseCase @Inject constructor( private val vectorPreferences: VectorPreferences, private val getNotificationSettingsAccountDataUseCase: GetNotificationSettingsAccountDataUseCase, - private val setNotificationSettingsAccountDataUseCase: SetNotificationSettingsAccountDataUseCase + private val setNotificationSettingsAccountDataUseCase: SetNotificationSettingsAccountDataUseCase, + private val deleteNotificationSettingsAccountDataUseCase: DeleteNotificationSettingsAccountDataUseCase, ) { - // TODO to be called when switching to background sync (in notification method setting) suspend fun execute(session: Session) { + if (vectorPreferences.isBackgroundSyncEnabled()) { + setCurrentNotificationStatus(session) + } else { + deleteCurrentNotificationStatus(session) + } + } + + private suspend fun setCurrentNotificationStatus(session: Session) { val deviceId = session.sessionParams.deviceId ?: return - val isSilencedLocal = !vectorPreferences.areNotificationEnabledForDevice() - val isSilencedRemote = getNotificationSettingsAccountDataUseCase.execute(session, deviceId)?.isSilenced - if (isSilencedLocal != isSilencedRemote) { + val areNotificationsSilenced = !vectorPreferences.areNotificationEnabledForDevice() + val isSilencedAccountData = getNotificationSettingsAccountDataUseCase.execute(session, deviceId)?.isSilenced + if (areNotificationsSilenced != isSilencedAccountData) { val notificationSettingsContent = LocalNotificationSettingsContent( - isSilenced = isSilencedLocal + isSilenced = areNotificationsSilenced ) setNotificationSettingsAccountDataUseCase.execute(session, deviceId, notificationSettingsContent) } } + + private suspend fun deleteCurrentNotificationStatus(session: Session) { + deleteNotificationSettingsAccountDataUseCase.execute(session) + } } diff --git a/vector/src/test/java/im/vector/app/core/session/ConfigureAndStartSessionUseCaseTest.kt b/vector/src/test/java/im/vector/app/core/session/ConfigureAndStartSessionUseCaseTest.kt index 23a3629efe..4071afaf3f 100644 --- a/vector/src/test/java/im/vector/app/core/session/ConfigureAndStartSessionUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/core/session/ConfigureAndStartSessionUseCaseTest.kt @@ -22,7 +22,7 @@ import im.vector.app.features.session.coroutineScope import im.vector.app.features.sync.SyncUtils import im.vector.app.features.settings.devices.v2.notification.UpdateNotificationSettingsAccountDataUseCase import im.vector.app.test.fakes.FakeContext -import im.vector.app.test.fakes.FakeEnableNotificationsSettingUpdater +import im.vector.app.test.fakes.FakeNotificationsSettingUpdater import im.vector.app.test.fakes.FakeSession import im.vector.app.test.fakes.FakeVectorPreferences import im.vector.app.test.fakes.FakeWebRtcCallManager @@ -47,7 +47,7 @@ class ConfigureAndStartSessionUseCaseTest { private val fakeWebRtcCallManager = FakeWebRtcCallManager() private val fakeUpdateMatrixClientInfoUseCase = mockk() private val fakeVectorPreferences = FakeVectorPreferences() - private val fakeEnableNotificationsSettingUpdater = FakeEnableNotificationsSettingUpdater() + private val fakeNotificationsSettingUpdater = FakeNotificationsSettingUpdater() private val fakeUpdateNotificationSettingsAccountDataUseCase = mockk() private val configureAndStartSessionUseCase = ConfigureAndStartSessionUseCase( @@ -55,7 +55,7 @@ class ConfigureAndStartSessionUseCaseTest { webRtcCallManager = fakeWebRtcCallManager.instance, updateMatrixClientInfoUseCase = fakeUpdateMatrixClientInfoUseCase, vectorPreferences = fakeVectorPreferences.instance, - enableNotificationsSettingUpdater = fakeEnableNotificationsSettingUpdater.instance, + notificationsSettingUpdater = fakeNotificationsSettingUpdater.instance, updateNotificationSettingsAccountDataUseCase = fakeUpdateNotificationSettingsAccountDataUseCase, ) @@ -71,7 +71,7 @@ class ConfigureAndStartSessionUseCaseTest { } @Test - fun `given start sync needed and enabled related preferences when execute then it should be configured properly`() = runTest { + fun `given start sync needed and client info recording enabled when execute then it should be configured properly`() = runTest { // Given val aSession = givenASession() every { aSession.coroutineScope } returns this @@ -79,8 +79,7 @@ class ConfigureAndStartSessionUseCaseTest { coJustRun { fakeUpdateMatrixClientInfoUseCase.execute(any()) } coJustRun { fakeUpdateNotificationSettingsAccountDataUseCase.execute(any()) } fakeVectorPreferences.givenIsClientInfoRecordingEnabled(isEnabled = true) - fakeVectorPreferences.givenIsBackgroundSyncEnabled(isEnabled = true) - fakeEnableNotificationsSettingUpdater.givenOnSessionsStarted(aSession) + fakeNotificationsSettingUpdater.givenOnSessionsStarted(aSession) // When configureAndStartSessionUseCase.execute(aSession, startSyncing = true) @@ -98,14 +97,14 @@ class ConfigureAndStartSessionUseCaseTest { } @Test - fun `given start sync needed and disabled related preferences when execute then it should be configured properly`() = runTest { + fun `given start sync needed and client info recording disabled when execute then it should be configured properly`() = runTest { // Given val aSession = givenASession() every { aSession.coroutineScope } returns this fakeWebRtcCallManager.givenCheckForProtocolsSupportIfNeededSucceeds() + coJustRun { fakeUpdateNotificationSettingsAccountDataUseCase.execute(any()) } fakeVectorPreferences.givenIsClientInfoRecordingEnabled(isEnabled = false) - fakeVectorPreferences.givenIsBackgroundSyncEnabled(isEnabled = false) - fakeEnableNotificationsSettingUpdater.givenOnSessionsStarted(aSession) + fakeNotificationsSettingUpdater.givenOnSessionsStarted(aSession) // When configureAndStartSessionUseCase.execute(aSession, startSyncing = true) @@ -118,6 +117,8 @@ class ConfigureAndStartSessionUseCaseTest { fakeWebRtcCallManager.verifyCheckForProtocolsSupportIfNeeded() coVerify(inverse = true) { fakeUpdateMatrixClientInfoUseCase.execute(aSession) + } + coVerify { fakeUpdateNotificationSettingsAccountDataUseCase.execute(aSession) } } @@ -125,27 +126,26 @@ class ConfigureAndStartSessionUseCaseTest { @Test fun `given a session and no start sync needed when execute then it should be configured properly`() = runTest { // Given - val fakeSession = givenASession() - every { fakeSession.coroutineScope } returns this + val aSession = givenASession() + every { aSession.coroutineScope } returns this fakeWebRtcCallManager.givenCheckForProtocolsSupportIfNeededSucceeds() coJustRun { fakeUpdateMatrixClientInfoUseCase.execute(any()) } coJustRun { fakeUpdateNotificationSettingsAccountDataUseCase.execute(any()) } fakeVectorPreferences.givenIsClientInfoRecordingEnabled(isEnabled = true) - fakeVectorPreferences.givenIsBackgroundSyncEnabled(isEnabled = true) - fakeEnableNotificationsSettingUpdater.givenOnSessionsStarted(fakeSession) + fakeNotificationsSettingUpdater.givenOnSessionsStarted(aSession) // When - configureAndStartSessionUseCase.execute(fakeSession, startSyncing = false) + configureAndStartSessionUseCase.execute(aSession, startSyncing = false) advanceUntilIdle() // Then - verify(inverse = true) { fakeSession.startSyncing(fakeContext.instance) } - fakeSession.fakeFilterService.verifySetSyncFilter(SyncUtils.getSyncFilterBuilder()) - fakeSession.fakePushersService.verifyRefreshPushers() + verify(inverse = true) { aSession.startSyncing(fakeContext.instance) } + aSession.fakeFilterService.verifySetSyncFilter(SyncUtils.getSyncFilterBuilder()) + aSession.fakePushersService.verifyRefreshPushers() fakeWebRtcCallManager.verifyCheckForProtocolsSupportIfNeeded() coVerify { - fakeUpdateMatrixClientInfoUseCase.execute(fakeSession) - fakeUpdateNotificationSettingsAccountDataUseCase.execute(fakeSession) + fakeUpdateMatrixClientInfoUseCase.execute(aSession) + fakeUpdateNotificationSettingsAccountDataUseCase.execute(aSession) } } diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/DeleteNotificationSettingsAccountDataUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/DeleteNotificationSettingsAccountDataUseCaseTest.kt index d84ff8c6ac..600ba2ba48 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/DeleteNotificationSettingsAccountDataUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/DeleteNotificationSettingsAccountDataUseCaseTest.kt @@ -19,7 +19,9 @@ package im.vector.app.features.settings.devices.v2.notification import im.vector.app.test.fakes.FakeSession import io.mockk.coJustRun import io.mockk.coVerify +import io.mockk.every import io.mockk.mockk +import io.mockk.verify import kotlinx.coroutines.test.runTest import org.junit.Test import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent @@ -27,17 +29,22 @@ import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent class DeleteNotificationSettingsAccountDataUseCaseTest { private val fakeSetNotificationSettingsAccountDataUseCase = mockk() + private val fakeGetNotificationSettingsAccountDataUseCase = mockk() private val deleteNotificationSettingsAccountDataUseCase = DeleteNotificationSettingsAccountDataUseCase( setNotificationSettingsAccountDataUseCase = fakeSetNotificationSettingsAccountDataUseCase, + getNotificationSettingsAccountDataUseCase = fakeGetNotificationSettingsAccountDataUseCase, ) @Test - fun `given a device id when execute then empty content is set for the account data`() = runTest { + fun `given a device id and existing account data content when execute then empty content is set for the account data`() = runTest { // Given val aDeviceId = "device-id" val aSession = FakeSession() aSession.givenSessionId(aDeviceId) + every { fakeGetNotificationSettingsAccountDataUseCase.execute(any(), any()) } returns LocalNotificationSettingsContent( + isSilenced = true, + ) coJustRun { fakeSetNotificationSettingsAccountDataUseCase.execute(any(), any(), any()) } val expectedContent = LocalNotificationSettingsContent( isSilenced = null @@ -47,6 +54,25 @@ class DeleteNotificationSettingsAccountDataUseCaseTest { deleteNotificationSettingsAccountDataUseCase.execute(aSession) // Then + verify { fakeGetNotificationSettingsAccountDataUseCase.execute(aSession, aDeviceId) } coVerify { fakeSetNotificationSettingsAccountDataUseCase.execute(aSession, aDeviceId, expectedContent) } } + + @Test + fun `given a device id and empty existing account data content when execute then nothing is done`() = runTest { + // Given + val aDeviceId = "device-id" + val aSession = FakeSession() + aSession.givenSessionId(aDeviceId) + every { fakeGetNotificationSettingsAccountDataUseCase.execute(any(), any()) } returns LocalNotificationSettingsContent( + isSilenced = null, + ) + + // When + deleteNotificationSettingsAccountDataUseCase.execute(aSession) + + // Then + verify { fakeGetNotificationSettingsAccountDataUseCase.execute(aSession, aDeviceId) } + coVerify(inverse = true) { fakeSetNotificationSettingsAccountDataUseCase.execute(aSession, aDeviceId, any()) } + } } diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataUseCaseTest.kt index 75179b5679..2adb0d8599 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataUseCaseTest.kt @@ -32,7 +32,27 @@ class GetNotificationSettingsAccountDataUseCaseTest { // Given val aDeviceId = "device-id" val aSession = FakeSession() - val expectedContent = LocalNotificationSettingsContent() + val expectedContent = LocalNotificationSettingsContent(isSilenced = true) + aSession + .accountDataService() + .givenGetUserAccountDataEventReturns( + type = UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + aDeviceId, + content = expectedContent.toContent(), + ) + + // When + val result = getNotificationSettingsAccountDataUseCase.execute(aSession, aDeviceId) + + // Then + result shouldBeEqualTo expectedContent + } + + @Test + fun `given a device id and empty content when execute then retrieve the account data event corresponding to this id if any`() { + // Given + val aDeviceId = "device-id" + val aSession = FakeSession() + val expectedContent = LocalNotificationSettingsContent(isSilenced = null) aSession .accountDataService() .givenGetUserAccountDataEventReturns( diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/SetNotificationSettingsAccountDataUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/SetNotificationSettingsAccountDataUseCaseTest.kt index d26271e59d..89fcd5e512 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/SetNotificationSettingsAccountDataUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/SetNotificationSettingsAccountDataUseCaseTest.kt @@ -31,7 +31,7 @@ class SetNotificationSettingsAccountDataUseCaseTest { fun `given a content when execute then update local notification settings with this content`() = runTest { // Given val sessionId = "a_session_id" - val localNotificationSettingsContent = LocalNotificationSettingsContent() + val localNotificationSettingsContent = LocalNotificationSettingsContent(isSilenced = true) val fakeSession = FakeSession() fakeSession.accountDataService().givenUpdateUserAccountDataEventSucceeds() diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/UpdateNotificationSettingsAccountDataUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/UpdateNotificationSettingsAccountDataUseCaseTest.kt index 41c5ab9081..3bca0da84e 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/UpdateNotificationSettingsAccountDataUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/UpdateNotificationSettingsAccountDataUseCaseTest.kt @@ -32,15 +32,17 @@ class UpdateNotificationSettingsAccountDataUseCaseTest { private val fakeVectorPreferences = FakeVectorPreferences() private val fakeGetNotificationSettingsAccountDataUseCase = mockk() private val fakeSetNotificationSettingsAccountDataUseCase = mockk() + private val fakeDeleteNotificationSettingsAccountDataUseCase = mockk() private val updateNotificationSettingsAccountDataUseCase = UpdateNotificationSettingsAccountDataUseCase( vectorPreferences = fakeVectorPreferences.instance, getNotificationSettingsAccountDataUseCase = fakeGetNotificationSettingsAccountDataUseCase, setNotificationSettingsAccountDataUseCase = fakeSetNotificationSettingsAccountDataUseCase, + deleteNotificationSettingsAccountDataUseCase = fakeDeleteNotificationSettingsAccountDataUseCase, ) @Test - fun `given a device id and a different local setting compared to remote when execute then content is updated`() = runTest { + fun `given back sync enabled, a device id and a different local setting compared to remote when execute then content is updated`() = runTest { // Given val aDeviceId = "device-id" val aSession = FakeSession() @@ -48,6 +50,7 @@ class UpdateNotificationSettingsAccountDataUseCaseTest { coJustRun { fakeSetNotificationSettingsAccountDataUseCase.execute(any(), any(), any()) } val areNotificationsEnabled = true fakeVectorPreferences.givenAreNotificationEnabled(areNotificationsEnabled) + fakeVectorPreferences.givenIsBackgroundSyncEnabled(true) every { fakeGetNotificationSettingsAccountDataUseCase.execute(any(), any()) } returns LocalNotificationSettingsContent( isSilenced = null @@ -61,14 +64,16 @@ class UpdateNotificationSettingsAccountDataUseCaseTest { // Then verify { + fakeVectorPreferences.instance.isBackgroundSyncEnabled() fakeVectorPreferences.instance.areNotificationEnabledForDevice() fakeGetNotificationSettingsAccountDataUseCase.execute(aSession, aDeviceId) } + coVerify(inverse = true) { fakeDeleteNotificationSettingsAccountDataUseCase.execute(aSession) } coVerify { fakeSetNotificationSettingsAccountDataUseCase.execute(aSession, aDeviceId, expectedContent) } } @Test - fun `given a device id and a same local setting compared to remote when execute then content is not updated`() = runTest { + fun `given back sync enabled, a device id and a same local setting compared to remote when execute then content is not updated`() = runTest { // Given val aDeviceId = "device-id" val aSession = FakeSession() @@ -76,6 +81,7 @@ class UpdateNotificationSettingsAccountDataUseCaseTest { coJustRun { fakeSetNotificationSettingsAccountDataUseCase.execute(any(), any(), any()) } val areNotificationsEnabled = true fakeVectorPreferences.givenAreNotificationEnabled(areNotificationsEnabled) + fakeVectorPreferences.givenIsBackgroundSyncEnabled(true) every { fakeGetNotificationSettingsAccountDataUseCase.execute(any(), any()) } returns LocalNotificationSettingsContent( isSilenced = false @@ -89,9 +95,31 @@ class UpdateNotificationSettingsAccountDataUseCaseTest { // Then verify { + fakeVectorPreferences.instance.isBackgroundSyncEnabled() fakeVectorPreferences.instance.areNotificationEnabledForDevice() fakeGetNotificationSettingsAccountDataUseCase.execute(aSession, aDeviceId) } + coVerify(inverse = true) { fakeDeleteNotificationSettingsAccountDataUseCase.execute(aSession) } coVerify(inverse = true) { fakeSetNotificationSettingsAccountDataUseCase.execute(aSession, aDeviceId, expectedContent) } } + + @Test + fun `given back sync disabled and a device id when execute then content is deleted`() = runTest { + // Given + val aDeviceId = "device-id" + val aSession = FakeSession() + aSession.givenSessionId(aDeviceId) + coJustRun { fakeDeleteNotificationSettingsAccountDataUseCase.execute(any()) } + fakeVectorPreferences.givenIsBackgroundSyncEnabled(false) + + // When + updateNotificationSettingsAccountDataUseCase.execute(aSession) + + // Then + verify { + fakeVectorPreferences.instance.isBackgroundSyncEnabled() + } + coVerify { fakeDeleteNotificationSettingsAccountDataUseCase.execute(aSession) } + coVerify(inverse = true) { fakeSetNotificationSettingsAccountDataUseCase.execute(aSession, aDeviceId, any()) } + } } diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeEnableNotificationsSettingUpdater.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeNotificationsSettingUpdater.kt similarity index 82% rename from vector/src/test/java/im/vector/app/test/fakes/FakeEnableNotificationsSettingUpdater.kt rename to vector/src/test/java/im/vector/app/test/fakes/FakeNotificationsSettingUpdater.kt index a78dd1a34b..f9f38e6c2a 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeEnableNotificationsSettingUpdater.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeNotificationsSettingUpdater.kt @@ -16,14 +16,14 @@ package im.vector.app.test.fakes -import im.vector.app.core.notification.EnableNotificationsSettingUpdater +import im.vector.app.core.notification.NotificationsSettingUpdater import io.mockk.justRun import io.mockk.mockk import org.matrix.android.sdk.api.session.Session -class FakeEnableNotificationsSettingUpdater { +class FakeNotificationsSettingUpdater { - val instance = mockk() + val instance = mockk() fun givenOnSessionsStarted(session: Session) { justRun { instance.onSessionsStarted(session) } From 7c10a4cb21a0bc0a044698ea633c5a76626bdbd6 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 23 Nov 2022 15:12:51 +0100 Subject: [PATCH 061/108] Adding tests for notifications setting updater --- .../NotificationsSettingUpdater.kt | 7 +- .../ConfigureAndStartSessionUseCase.kt | 2 +- .../NotificationsSettingUpdaterTest.kt | 106 ++++++++++++++++++ .../fakes/FakeNotificationsSettingUpdater.kt | 2 +- .../app/test/fakes/FakeVectorPreferences.kt | 7 ++ 5 files changed, 118 insertions(+), 6 deletions(-) create mode 100644 vector/src/test/java/im/vector/app/core/notification/NotificationsSettingUpdaterTest.kt diff --git a/vector/src/main/java/im/vector/app/core/notification/NotificationsSettingUpdater.kt b/vector/src/main/java/im/vector/app/core/notification/NotificationsSettingUpdater.kt index c6738edddb..4a16f37cfe 100644 --- a/vector/src/main/java/im/vector/app/core/notification/NotificationsSettingUpdater.kt +++ b/vector/src/main/java/im/vector/app/core/notification/NotificationsSettingUpdater.kt @@ -41,10 +41,9 @@ class NotificationsSettingUpdater @Inject constructor( private var job: Job? = null private var prefChangeListener: OnSharedPreferenceChangeListener? = null - // TODO add unit tests - fun onSessionsStarted(session: Session) { + fun onSessionStarted(session: Session) { updateEnableNotificationsSettingOnChange(session) - updateNotificationSettingsAccountDataOnChange(session) + updateAccountDataOnBackgroundSyncChange(session) } private fun updateEnableNotificationsSettingOnChange(session: Session) { @@ -54,7 +53,7 @@ class NotificationsSettingUpdater @Inject constructor( } } - private fun updateNotificationSettingsAccountDataOnChange(session: Session) { + private fun updateAccountDataOnBackgroundSyncChange(session: Session) { prefChangeListener?.let { vectorPreferences.unsubscribeToChanges(it) } prefChangeListener = null prefChangeListener = createPrefListener(session).also { diff --git a/vector/src/main/java/im/vector/app/core/session/ConfigureAndStartSessionUseCase.kt b/vector/src/main/java/im/vector/app/core/session/ConfigureAndStartSessionUseCase.kt index 623f7d83a9..d167b02d05 100644 --- a/vector/src/main/java/im/vector/app/core/session/ConfigureAndStartSessionUseCase.kt +++ b/vector/src/main/java/im/vector/app/core/session/ConfigureAndStartSessionUseCase.kt @@ -53,7 +53,7 @@ class ConfigureAndStartSessionUseCase @Inject constructor( webRtcCallManager.checkForProtocolsSupportIfNeeded() updateMatrixClientInfoIfNeeded(session) createNotificationSettingsAccountDataIfNeeded(session) - notificationsSettingUpdater.onSessionsStarted(session) + notificationsSettingUpdater.onSessionStarted(session) } private fun updateMatrixClientInfoIfNeeded(session: Session) { diff --git a/vector/src/test/java/im/vector/app/core/notification/NotificationsSettingUpdaterTest.kt b/vector/src/test/java/im/vector/app/core/notification/NotificationsSettingUpdaterTest.kt new file mode 100644 index 0000000000..386b52e61e --- /dev/null +++ b/vector/src/test/java/im/vector/app/core/notification/NotificationsSettingUpdaterTest.kt @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.core.notification + +import im.vector.app.features.session.coroutineScope +import im.vector.app.features.settings.VectorPreferences +import im.vector.app.features.settings.devices.v2.notification.UpdateNotificationSettingsAccountDataUseCase +import im.vector.app.test.fakes.FakeSession +import im.vector.app.test.fakes.FakeVectorPreferences +import io.mockk.coJustRun +import io.mockk.coVerify +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkStatic +import io.mockk.unmockkAll +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest +import org.junit.After +import org.junit.Before +import org.junit.Test + +class NotificationsSettingUpdaterTest { + + private val fakeUpdateEnableNotificationsSettingOnChangeUseCase = mockk() + private val fakeVectorPreferences = FakeVectorPreferences() + private val fakeUpdateNotificationSettingsAccountDataUseCase = mockk() + + private val notificationsSettingUpdater = NotificationsSettingUpdater( + updateEnableNotificationsSettingOnChangeUseCase = fakeUpdateEnableNotificationsSettingOnChangeUseCase, + vectorPreferences = fakeVectorPreferences.instance, + updateNotificationSettingsAccountDataUseCase = fakeUpdateNotificationSettingsAccountDataUseCase, + ) + + @Before + fun setup() { + mockkStatic("im.vector.app.features.session.SessionCoroutineScopesKt") + } + + @After + fun tearDown() { + unmockkAll() + } + + @Test + fun `given a session when calling onSessionStarted then update enable notification on change`() = runTest { + // Given + val aSession = FakeSession() + every { aSession.coroutineScope } returns this + coJustRun { fakeUpdateEnableNotificationsSettingOnChangeUseCase.execute(any()) } + + // When + notificationsSettingUpdater.onSessionStarted(aSession) + advanceUntilIdle() + + // Then + coVerify { fakeUpdateEnableNotificationsSettingOnChangeUseCase.execute(aSession) } + } + + @Test + fun `given a session when calling onSessionStarted then update account data on background sync preference change`() = runTest { + // Given + val aSession = FakeSession() + every { aSession.coroutineScope } returns this + coJustRun { fakeUpdateEnableNotificationsSettingOnChangeUseCase.execute(any()) } + coJustRun { fakeUpdateNotificationSettingsAccountDataUseCase.execute(any()) } + fakeVectorPreferences.givenChangeOnPreference(VectorPreferences.SETTINGS_FDROID_BACKGROUND_SYNC_MODE) + + // When + notificationsSettingUpdater.onSessionStarted(aSession) + advanceUntilIdle() + + // Then + coVerify { fakeUpdateNotificationSettingsAccountDataUseCase.execute(aSession) } + } + + @Test + fun `given a session when calling onSessionStarted then account data is not updated on other preference change`() = runTest { + // Given + val aSession = FakeSession() + every { aSession.coroutineScope } returns this + coJustRun { fakeUpdateEnableNotificationsSettingOnChangeUseCase.execute(any()) } + coJustRun { fakeUpdateNotificationSettingsAccountDataUseCase.execute(any()) } + fakeVectorPreferences.givenChangeOnPreference("key") + + // When + notificationsSettingUpdater.onSessionStarted(aSession) + advanceUntilIdle() + + // Then + coVerify(inverse = true) { fakeUpdateNotificationSettingsAccountDataUseCase.execute(aSession) } + } +} diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeNotificationsSettingUpdater.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeNotificationsSettingUpdater.kt index f9f38e6c2a..2e397763f8 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeNotificationsSettingUpdater.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeNotificationsSettingUpdater.kt @@ -26,6 +26,6 @@ class FakeNotificationsSettingUpdater { val instance = mockk() fun givenOnSessionsStarted(session: Session) { - justRun { instance.onSessionsStarted(session) } + justRun { instance.onSessionStarted(session) } } } diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt index 58bc1a18b8..94bc0966c5 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt @@ -17,6 +17,7 @@ package im.vector.app.test.fakes import im.vector.app.features.settings.BackgroundSyncMode +import android.content.SharedPreferences.OnSharedPreferenceChangeListener import im.vector.app.features.settings.VectorPreferences import io.mockk.every import io.mockk.justRun @@ -77,4 +78,10 @@ class FakeVectorPreferences { fun givenIsBackgroundSyncEnabled(isEnabled: Boolean) { every { instance.isBackgroundSyncEnabled() } returns isEnabled } + + fun givenChangeOnPreference(key: String) { + every { instance.subscribeToChanges(any()) } answers { + firstArg().onSharedPreferenceChanged(mockk(), key) + } + } } From a2ae3af69d5c187930e0e0f6e4884eb3b4f29de2 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 23 Nov 2022 15:23:31 +0100 Subject: [PATCH 062/108] Removing unused imports --- .../vector/app/features/settings/devices/v2/DevicesViewModel.kt | 1 - .../settings/devices/v2/overview/SessionOverviewViewModel.kt | 1 - .../app/features/settings/devices/v2/DevicesViewModelTest.kt | 1 - .../settings/devices/v2/overview/SessionOverviewViewModelTest.kt | 1 - 4 files changed, 4 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt index bfccd2f9d3..b7a6c5df30 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt @@ -28,7 +28,6 @@ import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.features.auth.PendingAuthHandler import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType -import im.vector.app.features.settings.devices.v2.signout.InterceptSignoutFlowResponseUseCase import im.vector.app.features.settings.devices.v2.signout.SignoutSessionsReAuthNeeded import im.vector.app.features.settings.devices.v2.signout.SignoutSessionsUseCase import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSessionCanBeVerifiedUseCase diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt index 74f962b464..f598c397de 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt @@ -32,7 +32,6 @@ import im.vector.app.features.settings.devices.v2.ToggleIpAddressVisibilityUseCa import im.vector.app.features.settings.devices.v2.VectorSessionsListViewModel import im.vector.app.features.settings.devices.v2.notification.GetNotificationsStatusUseCase import im.vector.app.features.settings.devices.v2.notification.ToggleNotificationUseCase -import im.vector.app.features.settings.devices.v2.signout.InterceptSignoutFlowResponseUseCase import im.vector.app.features.settings.devices.v2.signout.SignoutSessionsReAuthNeeded import im.vector.app.features.settings.devices.v2.signout.SignoutSessionsUseCase import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSessionCanBeVerifiedUseCase diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt index aa5ebd73eb..4bfd5c4496 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt @@ -22,7 +22,6 @@ import com.airbnb.mvrx.test.MavericksTestRule import im.vector.app.core.session.clientinfo.MatrixClientInfoContent import im.vector.app.features.settings.devices.v2.details.extended.DeviceExtendedInfo import im.vector.app.features.settings.devices.v2.list.DeviceType -import im.vector.app.features.settings.devices.v2.signout.InterceptSignoutFlowResponseUseCase import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSessionCanBeVerifiedUseCase import im.vector.app.features.settings.devices.v2.verification.CurrentSessionCrossSigningInfo import im.vector.app.features.settings.devices.v2.verification.GetCurrentSessionCrossSigningInfoUseCase diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt index 2ddc91cdac..6018152176 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt @@ -24,7 +24,6 @@ import im.vector.app.features.settings.devices.v2.DeviceFullInfo import im.vector.app.features.settings.devices.v2.RefreshDevicesUseCase import im.vector.app.features.settings.devices.v2.ToggleIpAddressVisibilityUseCase import im.vector.app.features.settings.devices.v2.notification.NotificationsStatus -import im.vector.app.features.settings.devices.v2.signout.InterceptSignoutFlowResponseUseCase import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSessionCanBeVerifiedUseCase import im.vector.app.test.fakes.FakeActiveSessionHolder import im.vector.app.test.fakes.FakeGetNotificationsStatusUseCase From 68d00e00d16b7082f6f86e2c73120387b0eb762e Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 24 Nov 2022 18:01:34 +0100 Subject: [PATCH 063/108] Fix method used to check if background sync is enabled --- ...pdateNotificationSettingsAccountDataUseCase.kt | 4 +++- ...eNotificationSettingsAccountDataUseCaseTest.kt | 15 +++++++++------ .../app/test/fakes/FakeUnifiedPushHelper.kt | 4 ++++ 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/UpdateNotificationSettingsAccountDataUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/UpdateNotificationSettingsAccountDataUseCase.kt index 7791c1dd4b..9296bcd912 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/UpdateNotificationSettingsAccountDataUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/UpdateNotificationSettingsAccountDataUseCase.kt @@ -16,6 +16,7 @@ package im.vector.app.features.settings.devices.v2.notification +import im.vector.app.core.pushers.UnifiedPushHelper import im.vector.app.features.settings.VectorPreferences import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent import org.matrix.android.sdk.api.session.Session @@ -27,13 +28,14 @@ import javax.inject.Inject */ class UpdateNotificationSettingsAccountDataUseCase @Inject constructor( private val vectorPreferences: VectorPreferences, + private val unifiedPushHelper: UnifiedPushHelper, private val getNotificationSettingsAccountDataUseCase: GetNotificationSettingsAccountDataUseCase, private val setNotificationSettingsAccountDataUseCase: SetNotificationSettingsAccountDataUseCase, private val deleteNotificationSettingsAccountDataUseCase: DeleteNotificationSettingsAccountDataUseCase, ) { suspend fun execute(session: Session) { - if (vectorPreferences.isBackgroundSyncEnabled()) { + if (unifiedPushHelper.isBackgroundSync()) { setCurrentNotificationStatus(session) } else { deleteCurrentNotificationStatus(session) diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/UpdateNotificationSettingsAccountDataUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/UpdateNotificationSettingsAccountDataUseCaseTest.kt index 3bca0da84e..f82663f6e4 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/UpdateNotificationSettingsAccountDataUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/UpdateNotificationSettingsAccountDataUseCaseTest.kt @@ -17,6 +17,7 @@ package im.vector.app.features.settings.devices.v2.notification import im.vector.app.test.fakes.FakeSession +import im.vector.app.test.fakes.FakeUnifiedPushHelper import im.vector.app.test.fakes.FakeVectorPreferences import io.mockk.coJustRun import io.mockk.coVerify @@ -30,12 +31,14 @@ import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent class UpdateNotificationSettingsAccountDataUseCaseTest { private val fakeVectorPreferences = FakeVectorPreferences() + private val fakeUnifiedPushHelper = FakeUnifiedPushHelper() private val fakeGetNotificationSettingsAccountDataUseCase = mockk() private val fakeSetNotificationSettingsAccountDataUseCase = mockk() private val fakeDeleteNotificationSettingsAccountDataUseCase = mockk() private val updateNotificationSettingsAccountDataUseCase = UpdateNotificationSettingsAccountDataUseCase( vectorPreferences = fakeVectorPreferences.instance, + unifiedPushHelper = fakeUnifiedPushHelper.instance, getNotificationSettingsAccountDataUseCase = fakeGetNotificationSettingsAccountDataUseCase, setNotificationSettingsAccountDataUseCase = fakeSetNotificationSettingsAccountDataUseCase, deleteNotificationSettingsAccountDataUseCase = fakeDeleteNotificationSettingsAccountDataUseCase, @@ -50,7 +53,7 @@ class UpdateNotificationSettingsAccountDataUseCaseTest { coJustRun { fakeSetNotificationSettingsAccountDataUseCase.execute(any(), any(), any()) } val areNotificationsEnabled = true fakeVectorPreferences.givenAreNotificationEnabled(areNotificationsEnabled) - fakeVectorPreferences.givenIsBackgroundSyncEnabled(true) + fakeUnifiedPushHelper.givenIsBackgroundSyncReturns(true) every { fakeGetNotificationSettingsAccountDataUseCase.execute(any(), any()) } returns LocalNotificationSettingsContent( isSilenced = null @@ -64,7 +67,7 @@ class UpdateNotificationSettingsAccountDataUseCaseTest { // Then verify { - fakeVectorPreferences.instance.isBackgroundSyncEnabled() + fakeUnifiedPushHelper.instance.isBackgroundSync() fakeVectorPreferences.instance.areNotificationEnabledForDevice() fakeGetNotificationSettingsAccountDataUseCase.execute(aSession, aDeviceId) } @@ -81,7 +84,7 @@ class UpdateNotificationSettingsAccountDataUseCaseTest { coJustRun { fakeSetNotificationSettingsAccountDataUseCase.execute(any(), any(), any()) } val areNotificationsEnabled = true fakeVectorPreferences.givenAreNotificationEnabled(areNotificationsEnabled) - fakeVectorPreferences.givenIsBackgroundSyncEnabled(true) + fakeUnifiedPushHelper.givenIsBackgroundSyncReturns(true) every { fakeGetNotificationSettingsAccountDataUseCase.execute(any(), any()) } returns LocalNotificationSettingsContent( isSilenced = false @@ -95,7 +98,7 @@ class UpdateNotificationSettingsAccountDataUseCaseTest { // Then verify { - fakeVectorPreferences.instance.isBackgroundSyncEnabled() + fakeUnifiedPushHelper.instance.isBackgroundSync() fakeVectorPreferences.instance.areNotificationEnabledForDevice() fakeGetNotificationSettingsAccountDataUseCase.execute(aSession, aDeviceId) } @@ -110,14 +113,14 @@ class UpdateNotificationSettingsAccountDataUseCaseTest { val aSession = FakeSession() aSession.givenSessionId(aDeviceId) coJustRun { fakeDeleteNotificationSettingsAccountDataUseCase.execute(any()) } - fakeVectorPreferences.givenIsBackgroundSyncEnabled(false) + fakeUnifiedPushHelper.givenIsBackgroundSyncReturns(false) // When updateNotificationSettingsAccountDataUseCase.execute(aSession) // Then verify { - fakeVectorPreferences.instance.isBackgroundSyncEnabled() + fakeUnifiedPushHelper.instance.isBackgroundSync() } coVerify { fakeDeleteNotificationSettingsAccountDataUseCase.execute(aSession) } coVerify(inverse = true) { fakeSetNotificationSettingsAccountDataUseCase.execute(aSession, aDeviceId, any()) } diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeUnifiedPushHelper.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeUnifiedPushHelper.kt index 99b5b75874..1a09783fad 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeUnifiedPushHelper.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeUnifiedPushHelper.kt @@ -31,4 +31,8 @@ class FakeUnifiedPushHelper { fun givenGetEndpointOrTokenReturns(endpoint: String?) { every { instance.getEndpointOrToken() } returns endpoint } + + fun givenIsBackgroundSyncReturns(enabled: Boolean) { + every { instance.isBackgroundSync() } returns enabled + } } From 9dff4ff949672207112d29eab807e8f1c0a8d0d6 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 1 Dec 2022 10:30:09 +0100 Subject: [PATCH 064/108] Fixing import order after rebase --- .../vector/app/core/session/ConfigureAndStartSessionUseCase.kt | 2 +- .../app/core/session/ConfigureAndStartSessionUseCaseTest.kt | 2 +- .../test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/session/ConfigureAndStartSessionUseCase.kt b/vector/src/main/java/im/vector/app/core/session/ConfigureAndStartSessionUseCase.kt index d167b02d05..fbf89b76a4 100644 --- a/vector/src/main/java/im/vector/app/core/session/ConfigureAndStartSessionUseCase.kt +++ b/vector/src/main/java/im/vector/app/core/session/ConfigureAndStartSessionUseCase.kt @@ -24,8 +24,8 @@ import im.vector.app.core.session.clientinfo.UpdateMatrixClientInfoUseCase import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.session.coroutineScope import im.vector.app.features.settings.VectorPreferences -import im.vector.app.features.sync.SyncUtils import im.vector.app.features.settings.devices.v2.notification.UpdateNotificationSettingsAccountDataUseCase +import im.vector.app.features.sync.SyncUtils import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import timber.log.Timber diff --git a/vector/src/test/java/im/vector/app/core/session/ConfigureAndStartSessionUseCaseTest.kt b/vector/src/test/java/im/vector/app/core/session/ConfigureAndStartSessionUseCaseTest.kt index 4071afaf3f..3fb128c759 100644 --- a/vector/src/test/java/im/vector/app/core/session/ConfigureAndStartSessionUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/core/session/ConfigureAndStartSessionUseCaseTest.kt @@ -19,8 +19,8 @@ package im.vector.app.core.session import im.vector.app.core.extensions.startSyncing import im.vector.app.core.session.clientinfo.UpdateMatrixClientInfoUseCase import im.vector.app.features.session.coroutineScope -import im.vector.app.features.sync.SyncUtils import im.vector.app.features.settings.devices.v2.notification.UpdateNotificationSettingsAccountDataUseCase +import im.vector.app.features.sync.SyncUtils import im.vector.app.test.fakes.FakeContext import im.vector.app.test.fakes.FakeNotificationsSettingUpdater import im.vector.app.test.fakes.FakeSession diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt index 94bc0966c5..3d7de662bd 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt @@ -16,8 +16,8 @@ package im.vector.app.test.fakes -import im.vector.app.features.settings.BackgroundSyncMode import android.content.SharedPreferences.OnSharedPreferenceChangeListener +import im.vector.app.features.settings.BackgroundSyncMode import im.vector.app.features.settings.VectorPreferences import io.mockk.every import io.mockk.justRun From 8973f3892a3fe10df8a784f6d3042e06453430e9 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 1 Dec 2022 11:34:30 +0100 Subject: [PATCH 065/108] Fixing unit tests after rebase --- .../UpdateNotificationSettingsAccountDataUseCaseTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/UpdateNotificationSettingsAccountDataUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/UpdateNotificationSettingsAccountDataUseCaseTest.kt index f82663f6e4..0075be02d2 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/UpdateNotificationSettingsAccountDataUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/UpdateNotificationSettingsAccountDataUseCaseTest.kt @@ -52,7 +52,7 @@ class UpdateNotificationSettingsAccountDataUseCaseTest { aSession.givenSessionId(aDeviceId) coJustRun { fakeSetNotificationSettingsAccountDataUseCase.execute(any(), any(), any()) } val areNotificationsEnabled = true - fakeVectorPreferences.givenAreNotificationEnabled(areNotificationsEnabled) + fakeVectorPreferences.givenAreNotificationsEnabledForDevice(areNotificationsEnabled) fakeUnifiedPushHelper.givenIsBackgroundSyncReturns(true) every { fakeGetNotificationSettingsAccountDataUseCase.execute(any(), any()) } returns LocalNotificationSettingsContent( @@ -83,7 +83,7 @@ class UpdateNotificationSettingsAccountDataUseCaseTest { aSession.givenSessionId(aDeviceId) coJustRun { fakeSetNotificationSettingsAccountDataUseCase.execute(any(), any(), any()) } val areNotificationsEnabled = true - fakeVectorPreferences.givenAreNotificationEnabled(areNotificationsEnabled) + fakeVectorPreferences.givenAreNotificationsEnabledForDevice(areNotificationsEnabled) fakeUnifiedPushHelper.givenIsBackgroundSyncReturns(true) every { fakeGetNotificationSettingsAccountDataUseCase.execute(any(), any()) } returns LocalNotificationSettingsContent( From 3f5147ddcef841caa40c56cd092916020bb06f21 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 1 Dec 2022 16:47:10 +0100 Subject: [PATCH 066/108] Fixing the toggle notifications use case for current session --- ...eCase.kt => ToggleNotificationsUseCase.kt} | 2 +- .../v2/overview/SessionOverviewViewModel.kt | 6 +- ...leNotificationsForCurrentSessionUseCase.kt | 9 +-- ...leNotificationsForCurrentSessionUseCase.kt | 10 +--- ...leNotificationsForCurrentSessionUseCase.kt | 57 +++++++++++++++++++ ...SettingsNotificationPreferenceViewModel.kt | 6 +- ...t.kt => ToggleNotificationsUseCaseTest.kt} | 10 ++-- ...tificationsForCurrentSessionUseCaseTest.kt | 14 ++--- ...tificationsForCurrentSessionUseCaseTest.kt | 8 +-- .../fakes/FakeToggleNotificationUseCase.kt | 4 +- 10 files changed, 92 insertions(+), 34 deletions(-) rename vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/{ToggleNotificationUseCase.kt => ToggleNotificationsUseCase.kt} (97%) create mode 100644 vector/src/main/java/im/vector/app/features/settings/notifications/ToggleNotificationsForCurrentSessionUseCase.kt rename vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/{ToggleNotificationUseCaseTest.kt => ToggleNotificationsUseCaseTest.kt} (93%) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/ToggleNotificationUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/ToggleNotificationsUseCase.kt similarity index 97% rename from vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/ToggleNotificationUseCase.kt rename to vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/ToggleNotificationsUseCase.kt index 73a81a6de1..77195ea950 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/ToggleNotificationUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/ToggleNotificationsUseCase.kt @@ -20,7 +20,7 @@ import im.vector.app.core.di.ActiveSessionHolder import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent import javax.inject.Inject -class ToggleNotificationUseCase @Inject constructor( +class ToggleNotificationsUseCase @Inject constructor( private val activeSessionHolder: ActiveSessionHolder, private val checkIfCanToggleNotificationsViaPusherUseCase: CheckIfCanToggleNotificationsViaPusherUseCase, private val checkIfCanToggleNotificationsViaAccountDataUseCase: CheckIfCanToggleNotificationsViaAccountDataUseCase, diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt index f598c397de..55866cb8c4 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt @@ -31,7 +31,7 @@ import im.vector.app.features.settings.devices.v2.RefreshDevicesUseCase import im.vector.app.features.settings.devices.v2.ToggleIpAddressVisibilityUseCase import im.vector.app.features.settings.devices.v2.VectorSessionsListViewModel import im.vector.app.features.settings.devices.v2.notification.GetNotificationsStatusUseCase -import im.vector.app.features.settings.devices.v2.notification.ToggleNotificationUseCase +import im.vector.app.features.settings.devices.v2.notification.ToggleNotificationsUseCase import im.vector.app.features.settings.devices.v2.signout.SignoutSessionsReAuthNeeded import im.vector.app.features.settings.devices.v2.signout.SignoutSessionsUseCase import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSessionCanBeVerifiedUseCase @@ -52,7 +52,7 @@ class SessionOverviewViewModel @AssistedInject constructor( private val signoutSessionsUseCase: SignoutSessionsUseCase, private val pendingAuthHandler: PendingAuthHandler, private val activeSessionHolder: ActiveSessionHolder, - private val toggleNotificationUseCase: ToggleNotificationUseCase, + private val toggleNotificationsUseCase: ToggleNotificationsUseCase, private val getNotificationsStatusUseCase: GetNotificationsStatusUseCase, refreshDevicesUseCase: RefreshDevicesUseCase, private val vectorPreferences: VectorPreferences, @@ -226,7 +226,7 @@ class SessionOverviewViewModel @AssistedInject constructor( private fun handleTogglePusherAction(action: SessionOverviewAction.TogglePushNotifications) { viewModelScope.launch { - toggleNotificationUseCase.execute(action.deviceId, action.enabled) + toggleNotificationsUseCase.execute(action.deviceId, action.enabled) } } } diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCase.kt index 380c3b5e4e..9173107018 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCase.kt @@ -20,21 +20,22 @@ import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.pushers.PushersManager import im.vector.app.core.pushers.UnregisterUnifiedPushUseCase import im.vector.app.features.settings.devices.v2.notification.CheckIfCanToggleNotificationsViaPusherUseCase -import im.vector.app.features.settings.devices.v2.notification.ToggleNotificationUseCase import javax.inject.Inject class DisableNotificationsForCurrentSessionUseCase @Inject constructor( private val activeSessionHolder: ActiveSessionHolder, private val pushersManager: PushersManager, private val checkIfCanToggleNotificationsViaPusherUseCase: CheckIfCanToggleNotificationsViaPusherUseCase, - private val toggleNotificationUseCase: ToggleNotificationUseCase, + private val toggleNotificationsForCurrentSessionUseCase: ToggleNotificationsForCurrentSessionUseCase, private val unregisterUnifiedPushUseCase: UnregisterUnifiedPushUseCase, ) { + // TODO update unit tests suspend fun execute() { val session = activeSessionHolder.getSafeActiveSession() ?: return - val deviceId = session.sessionParams.deviceId ?: return - toggleNotificationUseCase.execute(deviceId, enabled = false) + toggleNotificationsForCurrentSessionUseCase.execute(enabled = false) + + // handle case when server does not support toggle of pusher if (!checkIfCanToggleNotificationsViaPusherUseCase.execute(session)) { unregisterUnifiedPushUseCase.execute(pushersManager) } diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt index 89633a10c2..663c5004e8 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt @@ -16,17 +16,14 @@ package im.vector.app.features.settings.notifications -import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.pushers.EnsureFcmTokenIsRetrievedUseCase import im.vector.app.core.pushers.PushersManager import im.vector.app.core.pushers.RegisterUnifiedPushUseCase -import im.vector.app.features.settings.devices.v2.notification.ToggleNotificationUseCase import javax.inject.Inject class EnableNotificationsForCurrentSessionUseCase @Inject constructor( - private val activeSessionHolder: ActiveSessionHolder, private val pushersManager: PushersManager, - private val toggleNotificationUseCase: ToggleNotificationUseCase, + private val toggleNotificationsForCurrentSessionUseCase: ToggleNotificationsForCurrentSessionUseCase, private val registerUnifiedPushUseCase: RegisterUnifiedPushUseCase, private val ensureFcmTokenIsRetrievedUseCase: EnsureFcmTokenIsRetrievedUseCase, ) { @@ -37,6 +34,7 @@ class EnableNotificationsForCurrentSessionUseCase @Inject constructor( object NeedToAskUserForDistributor : EnableNotificationsResult } + // TODO update unit tests suspend fun execute(distributor: String = ""): EnableNotificationsResult { val pusherForCurrentSession = pushersManager.getPusherForCurrentSession() if (pusherForCurrentSession == null) { @@ -50,9 +48,7 @@ class EnableNotificationsForCurrentSessionUseCase @Inject constructor( } } - val session = activeSessionHolder.getSafeActiveSession() ?: return EnableNotificationsResult.Failure - val deviceId = session.sessionParams.deviceId ?: return EnableNotificationsResult.Failure - toggleNotificationUseCase.execute(deviceId, enabled = true) + toggleNotificationsForCurrentSessionUseCase.execute(enabled = true) return EnableNotificationsResult.Success } diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/ToggleNotificationsForCurrentSessionUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/ToggleNotificationsForCurrentSessionUseCase.kt new file mode 100644 index 0000000000..64b4b1bb89 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/ToggleNotificationsForCurrentSessionUseCase.kt @@ -0,0 +1,57 @@ +/* + * 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.settings.notifications + +import im.vector.app.core.di.ActiveSessionHolder +import im.vector.app.core.pushers.UnifiedPushHelper +import im.vector.app.features.settings.devices.v2.notification.CheckIfCanToggleNotificationsViaPusherUseCase +import im.vector.app.features.settings.devices.v2.notification.DeleteNotificationSettingsAccountDataUseCase +import im.vector.app.features.settings.devices.v2.notification.SetNotificationSettingsAccountDataUseCase +import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent +import timber.log.Timber +import javax.inject.Inject + +class ToggleNotificationsForCurrentSessionUseCase @Inject constructor( + private val activeSessionHolder: ActiveSessionHolder, + private val unifiedPushHelper: UnifiedPushHelper, + private val checkIfCanToggleNotificationsViaPusherUseCase: CheckIfCanToggleNotificationsViaPusherUseCase, + private val setNotificationSettingsAccountDataUseCase: SetNotificationSettingsAccountDataUseCase, + private val deleteNotificationSettingsAccountDataUseCase: DeleteNotificationSettingsAccountDataUseCase, +) { + + // TODO add unit tests + suspend fun execute(enabled: Boolean) { + val session = activeSessionHolder.getSafeActiveSession() ?: return + val deviceId = session.sessionParams.deviceId ?: return + + if (unifiedPushHelper.isBackgroundSync()) { + Timber.d("background sync is enabled, setting account data event") + val newNotificationSettingsContent = LocalNotificationSettingsContent(isSilenced = !enabled) + setNotificationSettingsAccountDataUseCase.execute(session, deviceId, newNotificationSettingsContent) + } else { + Timber.d("push notif is enabled, deleting any account data and updating pusher") + deleteNotificationSettingsAccountDataUseCase.execute(session) + + if (checkIfCanToggleNotificationsViaPusherUseCase.execute(session)) { + val devicePusher = session.pushersService().getPushers().firstOrNull { it.deviceId == deviceId } + devicePusher?.let { pusher -> + session.pushersService().togglePusher(pusher, enabled) + } + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModel.kt index d6a9c621f2..357f6458f2 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModel.kt @@ -40,6 +40,7 @@ class VectorSettingsNotificationPreferenceViewModel @AssistedInject constructor( private val unregisterUnifiedPushUseCase: UnregisterUnifiedPushUseCase, private val registerUnifiedPushUseCase: RegisterUnifiedPushUseCase, private val ensureFcmTokenIsRetrievedUseCase: EnsureFcmTokenIsRetrievedUseCase, + private val toggleNotificationsForCurrentSessionUseCase: ToggleNotificationsForCurrentSessionUseCase, ) : VectorViewModel(initialState) { @AssistedFactory @@ -80,6 +81,7 @@ class VectorSettingsNotificationPreferenceViewModel @AssistedInject constructor( } } + // TODO update unit tests private fun handleRegisterPushDistributor(distributor: String) { viewModelScope.launch { unregisterUnifiedPushUseCase.execute(pushersManager) @@ -88,7 +90,9 @@ class VectorSettingsNotificationPreferenceViewModel @AssistedInject constructor( _viewEvents.post(VectorSettingsNotificationPreferenceViewEvent.AskUserForPushDistributor) } RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.Success -> { - ensureFcmTokenIsRetrievedUseCase.execute(pushersManager, registerPusher = vectorPreferences.areNotificationEnabledForDevice()) + val areNotificationsEnabled = vectorPreferences.areNotificationEnabledForDevice() + ensureFcmTokenIsRetrievedUseCase.execute(pushersManager, registerPusher = areNotificationsEnabled) + toggleNotificationsForCurrentSessionUseCase.execute(enabled = areNotificationsEnabled) _viewEvents.post(VectorSettingsNotificationPreferenceViewEvent.NotificationMethodChanged) } } diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/ToggleNotificationUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/ToggleNotificationsUseCaseTest.kt similarity index 93% rename from vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/ToggleNotificationUseCaseTest.kt rename to vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/ToggleNotificationsUseCaseTest.kt index 1e3517c776..90afbe9045 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/ToggleNotificationUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/ToggleNotificationsUseCaseTest.kt @@ -26,7 +26,7 @@ import kotlinx.coroutines.test.runTest import org.junit.Test import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent -class ToggleNotificationUseCaseTest { +class ToggleNotificationsUseCaseTest { private val activeSessionHolder = FakeActiveSessionHolder() private val fakeCheckIfCanToggleNotificationsViaPusherUseCase = @@ -36,8 +36,8 @@ class ToggleNotificationUseCaseTest { private val fakeSetNotificationSettingsAccountDataUseCase = mockk() - private val toggleNotificationUseCase = - ToggleNotificationUseCase( + private val toggleNotificationsUseCase = + ToggleNotificationsUseCase( activeSessionHolder = activeSessionHolder.instance, checkIfCanToggleNotificationsViaPusherUseCase = fakeCheckIfCanToggleNotificationsViaPusherUseCase, checkIfCanToggleNotificationsViaAccountDataUseCase = fakeCheckIfCanToggleNotificationsViaAccountDataUseCase, @@ -59,7 +59,7 @@ class ToggleNotificationUseCaseTest { every { fakeCheckIfCanToggleNotificationsViaAccountDataUseCase.execute(fakeSession, sessionId) } returns false // When - toggleNotificationUseCase.execute(sessionId, true) + toggleNotificationsUseCase.execute(sessionId, true) // Then activeSessionHolder.fakeSession.pushersService().verifyTogglePusherCalled(pushers.first(), true) @@ -78,7 +78,7 @@ class ToggleNotificationUseCaseTest { ) // When - toggleNotificationUseCase.execute(sessionId, true) + toggleNotificationsUseCase.execute(sessionId, true) // Then coVerify { diff --git a/vector/src/test/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCaseTest.kt index e53874858a..7e2a118e0e 100644 --- a/vector/src/test/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCaseTest.kt @@ -18,7 +18,7 @@ package im.vector.app.features.settings.notifications import im.vector.app.core.pushers.UnregisterUnifiedPushUseCase import im.vector.app.features.settings.devices.v2.notification.CheckIfCanToggleNotificationsViaPusherUseCase -import im.vector.app.features.settings.devices.v2.notification.ToggleNotificationUseCase +import im.vector.app.features.settings.devices.v2.notification.ToggleNotificationsUseCase import im.vector.app.test.fakes.FakeActiveSessionHolder import im.vector.app.test.fakes.FakePushersManager import io.mockk.coJustRun @@ -35,14 +35,14 @@ class DisableNotificationsForCurrentSessionUseCaseTest { private val fakeActiveSessionHolder = FakeActiveSessionHolder() private val fakePushersManager = FakePushersManager() private val fakeCheckIfCanToggleNotificationsViaPusherUseCase = mockk() - private val fakeToggleNotificationUseCase = mockk() + private val fakeToggleNotificationsUseCase = mockk() private val fakeUnregisterUnifiedPushUseCase = mockk() private val disableNotificationsForCurrentSessionUseCase = DisableNotificationsForCurrentSessionUseCase( activeSessionHolder = fakeActiveSessionHolder.instance, pushersManager = fakePushersManager.instance, checkIfCanToggleNotificationsViaPusherUseCase = fakeCheckIfCanToggleNotificationsViaPusherUseCase, - toggleNotificationUseCase = fakeToggleNotificationUseCase, + toggleNotificationUseCase = fakeToggleNotificationsUseCase, unregisterUnifiedPushUseCase = fakeUnregisterUnifiedPushUseCase, ) @@ -52,13 +52,13 @@ class DisableNotificationsForCurrentSessionUseCaseTest { val fakeSession = fakeActiveSessionHolder.fakeSession fakeSession.givenSessionId(A_SESSION_ID) every { fakeCheckIfCanToggleNotificationsViaPusherUseCase.execute(fakeSession) } returns true - coJustRun { fakeToggleNotificationUseCase.execute(A_SESSION_ID, any()) } + coJustRun { fakeToggleNotificationsUseCase.execute(A_SESSION_ID, any()) } // When disableNotificationsForCurrentSessionUseCase.execute() // Then - coVerify { fakeToggleNotificationUseCase.execute(A_SESSION_ID, false) } + coVerify { fakeToggleNotificationsUseCase.execute(A_SESSION_ID, false) } } @Test @@ -67,7 +67,7 @@ class DisableNotificationsForCurrentSessionUseCaseTest { val fakeSession = fakeActiveSessionHolder.fakeSession fakeSession.givenSessionId(A_SESSION_ID) every { fakeCheckIfCanToggleNotificationsViaPusherUseCase.execute(fakeSession) } returns false - coJustRun { fakeToggleNotificationUseCase.execute(A_SESSION_ID, any()) } + coJustRun { fakeToggleNotificationsUseCase.execute(A_SESSION_ID, any()) } coJustRun { fakeUnregisterUnifiedPushUseCase.execute(any()) } // When @@ -75,7 +75,7 @@ class DisableNotificationsForCurrentSessionUseCaseTest { // Then coVerify { - fakeToggleNotificationUseCase.execute(A_SESSION_ID, false) + fakeToggleNotificationsUseCase.execute(A_SESSION_ID, false) fakeUnregisterUnifiedPushUseCase.execute(fakePushersManager.instance) } } diff --git a/vector/src/test/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCaseTest.kt index f10b0777cb..7beab170f2 100644 --- a/vector/src/test/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCaseTest.kt @@ -18,7 +18,7 @@ package im.vector.app.features.settings.notifications import im.vector.app.core.pushers.EnsureFcmTokenIsRetrievedUseCase import im.vector.app.core.pushers.RegisterUnifiedPushUseCase -import im.vector.app.features.settings.devices.v2.notification.ToggleNotificationUseCase +import im.vector.app.features.settings.devices.v2.notification.ToggleNotificationsUseCase import im.vector.app.test.fakes.FakeActiveSessionHolder import im.vector.app.test.fakes.FakePushersManager import io.mockk.coJustRun @@ -36,14 +36,14 @@ class EnableNotificationsForCurrentSessionUseCaseTest { private val fakeActiveSessionHolder = FakeActiveSessionHolder() private val fakePushersManager = FakePushersManager() - private val fakeToggleNotificationUseCase = mockk() + private val fakeToggleNotificationsUseCase = mockk() private val fakeRegisterUnifiedPushUseCase = mockk() private val fakeEnsureFcmTokenIsRetrievedUseCase = mockk() private val enableNotificationsForCurrentSessionUseCase = EnableNotificationsForCurrentSessionUseCase( activeSessionHolder = fakeActiveSessionHolder.instance, pushersManager = fakePushersManager.instance, - toggleNotificationUseCase = fakeToggleNotificationUseCase, + toggleNotificationUseCase = fakeToggleNotificationsUseCase, registerUnifiedPushUseCase = fakeRegisterUnifiedPushUseCase, ensureFcmTokenIsRetrievedUseCase = fakeEnsureFcmTokenIsRetrievedUseCase, ) @@ -57,7 +57,7 @@ class EnableNotificationsForCurrentSessionUseCaseTest { fakePushersManager.givenGetPusherForCurrentSessionReturns(null) every { fakeRegisterUnifiedPushUseCase.execute(any()) } returns RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.Success justRun { fakeEnsureFcmTokenIsRetrievedUseCase.execute(any(), any()) } - coJustRun { fakeToggleNotificationUseCase.execute(A_SESSION_ID, any()) } + coJustRun { fakeToggleNotificationsUseCase.execute(A_SESSION_ID, any()) } // When val result = enableNotificationsForCurrentSessionUseCase.execute(aDistributor) diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeToggleNotificationUseCase.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeToggleNotificationUseCase.kt index 527625144e..3d2179bc2d 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeToggleNotificationUseCase.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeToggleNotificationUseCase.kt @@ -16,14 +16,14 @@ package im.vector.app.test.fakes -import im.vector.app.features.settings.devices.v2.notification.ToggleNotificationUseCase +import im.vector.app.features.settings.devices.v2.notification.ToggleNotificationsUseCase import io.mockk.coJustRun import io.mockk.coVerify import io.mockk.mockk class FakeToggleNotificationUseCase { - val instance = mockk { + val instance = mockk { coJustRun { execute(any(), any()) } } From 06681fd115596ee9b14a30aef94f73d3a62de79b Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 1 Dec 2022 17:12:09 +0100 Subject: [PATCH 067/108] Removing listening on background sync preference --- .../NotificationsSettingUpdater.kt | 27 ------------ .../NotificationsSettingUpdaterTest.kt | 41 ------------------- 2 files changed, 68 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/notification/NotificationsSettingUpdater.kt b/vector/src/main/java/im/vector/app/core/notification/NotificationsSettingUpdater.kt index 4a16f37cfe..a4d18baa64 100644 --- a/vector/src/main/java/im/vector/app/core/notification/NotificationsSettingUpdater.kt +++ b/vector/src/main/java/im/vector/app/core/notification/NotificationsSettingUpdater.kt @@ -16,11 +16,7 @@ package im.vector.app.core.notification -import android.content.SharedPreferences.OnSharedPreferenceChangeListener import im.vector.app.features.session.coroutineScope -import im.vector.app.features.settings.VectorPreferences -import im.vector.app.features.settings.VectorPreferences.Companion.SETTINGS_FDROID_BACKGROUND_SYNC_MODE -import im.vector.app.features.settings.devices.v2.notification.UpdateNotificationSettingsAccountDataUseCase import kotlinx.coroutines.Job import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session @@ -29,21 +25,16 @@ import javax.inject.Singleton /** * Listen changes in Pusher or Account Data to update the local setting for notification toggle. - * Listen changes on background sync mode preference to update the corresponding Account Data event. */ @Singleton class NotificationsSettingUpdater @Inject constructor( private val updateEnableNotificationsSettingOnChangeUseCase: UpdateEnableNotificationsSettingOnChangeUseCase, - private val vectorPreferences: VectorPreferences, - private val updateNotificationSettingsAccountDataUseCase: UpdateNotificationSettingsAccountDataUseCase, ) { private var job: Job? = null - private var prefChangeListener: OnSharedPreferenceChangeListener? = null fun onSessionStarted(session: Session) { updateEnableNotificationsSettingOnChange(session) - updateAccountDataOnBackgroundSyncChange(session) } private fun updateEnableNotificationsSettingOnChange(session: Session) { @@ -52,22 +43,4 @@ class NotificationsSettingUpdater @Inject constructor( updateEnableNotificationsSettingOnChangeUseCase.execute(session) } } - - private fun updateAccountDataOnBackgroundSyncChange(session: Session) { - prefChangeListener?.let { vectorPreferences.unsubscribeToChanges(it) } - prefChangeListener = null - prefChangeListener = createPrefListener(session).also { - vectorPreferences.subscribeToChanges(it) - } - } - - private fun createPrefListener(session: Session): OnSharedPreferenceChangeListener { - return OnSharedPreferenceChangeListener { _, key -> - session.coroutineScope.launch { - if (key == SETTINGS_FDROID_BACKGROUND_SYNC_MODE) { - updateNotificationSettingsAccountDataUseCase.execute(session) - } - } - } - } } diff --git a/vector/src/test/java/im/vector/app/core/notification/NotificationsSettingUpdaterTest.kt b/vector/src/test/java/im/vector/app/core/notification/NotificationsSettingUpdaterTest.kt index 386b52e61e..0920ee4716 100644 --- a/vector/src/test/java/im/vector/app/core/notification/NotificationsSettingUpdaterTest.kt +++ b/vector/src/test/java/im/vector/app/core/notification/NotificationsSettingUpdaterTest.kt @@ -17,10 +17,7 @@ package im.vector.app.core.notification import im.vector.app.features.session.coroutineScope -import im.vector.app.features.settings.VectorPreferences -import im.vector.app.features.settings.devices.v2.notification.UpdateNotificationSettingsAccountDataUseCase import im.vector.app.test.fakes.FakeSession -import im.vector.app.test.fakes.FakeVectorPreferences import io.mockk.coJustRun import io.mockk.coVerify import io.mockk.every @@ -36,13 +33,9 @@ import org.junit.Test class NotificationsSettingUpdaterTest { private val fakeUpdateEnableNotificationsSettingOnChangeUseCase = mockk() - private val fakeVectorPreferences = FakeVectorPreferences() - private val fakeUpdateNotificationSettingsAccountDataUseCase = mockk() private val notificationsSettingUpdater = NotificationsSettingUpdater( updateEnableNotificationsSettingOnChangeUseCase = fakeUpdateEnableNotificationsSettingOnChangeUseCase, - vectorPreferences = fakeVectorPreferences.instance, - updateNotificationSettingsAccountDataUseCase = fakeUpdateNotificationSettingsAccountDataUseCase, ) @Before @@ -69,38 +62,4 @@ class NotificationsSettingUpdaterTest { // Then coVerify { fakeUpdateEnableNotificationsSettingOnChangeUseCase.execute(aSession) } } - - @Test - fun `given a session when calling onSessionStarted then update account data on background sync preference change`() = runTest { - // Given - val aSession = FakeSession() - every { aSession.coroutineScope } returns this - coJustRun { fakeUpdateEnableNotificationsSettingOnChangeUseCase.execute(any()) } - coJustRun { fakeUpdateNotificationSettingsAccountDataUseCase.execute(any()) } - fakeVectorPreferences.givenChangeOnPreference(VectorPreferences.SETTINGS_FDROID_BACKGROUND_SYNC_MODE) - - // When - notificationsSettingUpdater.onSessionStarted(aSession) - advanceUntilIdle() - - // Then - coVerify { fakeUpdateNotificationSettingsAccountDataUseCase.execute(aSession) } - } - - @Test - fun `given a session when calling onSessionStarted then account data is not updated on other preference change`() = runTest { - // Given - val aSession = FakeSession() - every { aSession.coroutineScope } returns this - coJustRun { fakeUpdateEnableNotificationsSettingOnChangeUseCase.execute(any()) } - coJustRun { fakeUpdateNotificationSettingsAccountDataUseCase.execute(any()) } - fakeVectorPreferences.givenChangeOnPreference("key") - - // When - notificationsSettingUpdater.onSessionStarted(aSession) - advanceUntilIdle() - - // Then - coVerify(inverse = true) { fakeUpdateNotificationSettingsAccountDataUseCase.execute(aSession) } - } } From 5248a69fe2bd498a892f5484fff96b60ccec0154 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 1 Dec 2022 17:33:27 +0100 Subject: [PATCH 068/108] Updating existing unit tests --- ...leNotificationsForCurrentSessionUseCase.kt | 1 - ...leNotificationsForCurrentSessionUseCase.kt | 2 -- ...rSettingsNotificationPreferenceFragment.kt | 1 - ...SettingsNotificationPreferenceViewEvent.kt | 1 - ...SettingsNotificationPreferenceViewModel.kt | 4 --- .../overview/SessionOverviewViewModelTest.kt | 2 +- ...tificationsForCurrentSessionUseCaseTest.kt | 20 +++++------ ...tificationsForCurrentSessionUseCaseTest.kt | 34 ++++--------------- ...ingsNotificationPreferenceViewModelTest.kt | 27 +++------------ 9 files changed, 21 insertions(+), 71 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCase.kt index 9173107018..daa58578d6 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCase.kt @@ -30,7 +30,6 @@ class DisableNotificationsForCurrentSessionUseCase @Inject constructor( private val unregisterUnifiedPushUseCase: UnregisterUnifiedPushUseCase, ) { - // TODO update unit tests suspend fun execute() { val session = activeSessionHolder.getSafeActiveSession() ?: return toggleNotificationsForCurrentSessionUseCase.execute(enabled = false) diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt index 663c5004e8..daf3890e33 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCase.kt @@ -30,11 +30,9 @@ class EnableNotificationsForCurrentSessionUseCase @Inject constructor( sealed interface EnableNotificationsResult { object Success : EnableNotificationsResult - object Failure : EnableNotificationsResult object NeedToAskUserForDistributor : EnableNotificationsResult } - // TODO update unit tests suspend fun execute(distributor: String = ""): EnableNotificationsResult { val pusherForCurrentSession = pushersManager.getPusherForCurrentSession() if (pusherForCurrentSession == null) { diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt index 238ed4218c..490a47ef61 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt @@ -119,7 +119,6 @@ class VectorSettingsNotificationPreferenceFragment : VectorSettingsNotificationPreferenceViewEvent.NotificationsForDeviceEnabled -> onNotificationsForDeviceEnabled() VectorSettingsNotificationPreferenceViewEvent.NotificationsForDeviceDisabled -> onNotificationsForDeviceDisabled() is VectorSettingsNotificationPreferenceViewEvent.AskUserForPushDistributor -> askUserToSelectPushDistributor() - VectorSettingsNotificationPreferenceViewEvent.EnableNotificationForDeviceFailure -> displayErrorDialog(throwable = null) VectorSettingsNotificationPreferenceViewEvent.NotificationMethodChanged -> onNotificationMethodChanged() } } diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewEvent.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewEvent.kt index e4cf8e1973..b0ee107769 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewEvent.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewEvent.kt @@ -20,7 +20,6 @@ import im.vector.app.core.platform.VectorViewEvents sealed interface VectorSettingsNotificationPreferenceViewEvent : VectorViewEvents { object NotificationsForDeviceEnabled : VectorSettingsNotificationPreferenceViewEvent - object EnableNotificationForDeviceFailure : VectorSettingsNotificationPreferenceViewEvent object NotificationsForDeviceDisabled : VectorSettingsNotificationPreferenceViewEvent object AskUserForPushDistributor : VectorSettingsNotificationPreferenceViewEvent object NotificationMethodChanged : VectorSettingsNotificationPreferenceViewEvent diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModel.kt index 357f6458f2..48e82b35e8 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModel.kt @@ -68,9 +68,6 @@ class VectorSettingsNotificationPreferenceViewModel @AssistedInject constructor( private fun handleEnableNotificationsForDevice(distributor: String) { viewModelScope.launch { when (enableNotificationsForCurrentSessionUseCase.execute(distributor)) { - EnableNotificationsForCurrentSessionUseCase.EnableNotificationsResult.Failure -> { - _viewEvents.post(VectorSettingsNotificationPreferenceViewEvent.EnableNotificationForDeviceFailure) - } is EnableNotificationsForCurrentSessionUseCase.EnableNotificationsResult.NeedToAskUserForDistributor -> { _viewEvents.post(VectorSettingsNotificationPreferenceViewEvent.AskUserForPushDistributor) } @@ -81,7 +78,6 @@ class VectorSettingsNotificationPreferenceViewModel @AssistedInject constructor( } } - // TODO update unit tests private fun handleRegisterPushDistributor(distributor: String) { viewModelScope.launch { unregisterUnifiedPushUseCase.execute(pushersManager) diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt index 6018152176..b0f7a774f2 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt @@ -88,7 +88,7 @@ class SessionOverviewViewModelTest { pendingAuthHandler = fakePendingAuthHandler.instance, activeSessionHolder = fakeActiveSessionHolder.instance, refreshDevicesUseCase = refreshDevicesUseCase, - toggleNotificationUseCase = toggleNotificationUseCase.instance, + toggleNotificationsUseCase = toggleNotificationUseCase.instance, getNotificationsStatusUseCase = fakeGetNotificationsStatusUseCase.instance, vectorPreferences = fakeVectorPreferences.instance, toggleIpAddressVisibilityUseCase = toggleIpAddressVisibilityUseCase, diff --git a/vector/src/test/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCaseTest.kt index 7e2a118e0e..b7749d0252 100644 --- a/vector/src/test/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCaseTest.kt @@ -18,7 +18,6 @@ package im.vector.app.features.settings.notifications import im.vector.app.core.pushers.UnregisterUnifiedPushUseCase import im.vector.app.features.settings.devices.v2.notification.CheckIfCanToggleNotificationsViaPusherUseCase -import im.vector.app.features.settings.devices.v2.notification.ToggleNotificationsUseCase import im.vector.app.test.fakes.FakeActiveSessionHolder import im.vector.app.test.fakes.FakePushersManager import io.mockk.coJustRun @@ -28,21 +27,19 @@ import io.mockk.mockk import kotlinx.coroutines.test.runTest import org.junit.Test -private const val A_SESSION_ID = "session-id" - class DisableNotificationsForCurrentSessionUseCaseTest { private val fakeActiveSessionHolder = FakeActiveSessionHolder() private val fakePushersManager = FakePushersManager() private val fakeCheckIfCanToggleNotificationsViaPusherUseCase = mockk() - private val fakeToggleNotificationsUseCase = mockk() + private val fakeToggleNotificationsForCurrentSessionUseCase = mockk() private val fakeUnregisterUnifiedPushUseCase = mockk() private val disableNotificationsForCurrentSessionUseCase = DisableNotificationsForCurrentSessionUseCase( activeSessionHolder = fakeActiveSessionHolder.instance, pushersManager = fakePushersManager.instance, checkIfCanToggleNotificationsViaPusherUseCase = fakeCheckIfCanToggleNotificationsViaPusherUseCase, - toggleNotificationUseCase = fakeToggleNotificationsUseCase, + toggleNotificationsForCurrentSessionUseCase = fakeToggleNotificationsForCurrentSessionUseCase, unregisterUnifiedPushUseCase = fakeUnregisterUnifiedPushUseCase, ) @@ -50,24 +47,25 @@ class DisableNotificationsForCurrentSessionUseCaseTest { fun `given toggle via pusher is possible when execute then disable notification via toggle of existing pusher`() = runTest { // Given val fakeSession = fakeActiveSessionHolder.fakeSession - fakeSession.givenSessionId(A_SESSION_ID) every { fakeCheckIfCanToggleNotificationsViaPusherUseCase.execute(fakeSession) } returns true - coJustRun { fakeToggleNotificationsUseCase.execute(A_SESSION_ID, any()) } + coJustRun { fakeToggleNotificationsForCurrentSessionUseCase.execute(any()) } // When disableNotificationsForCurrentSessionUseCase.execute() // Then - coVerify { fakeToggleNotificationsUseCase.execute(A_SESSION_ID, false) } + coVerify { fakeToggleNotificationsForCurrentSessionUseCase.execute(false) } + coVerify(inverse = true) { + fakeUnregisterUnifiedPushUseCase.execute(any()) + } } @Test fun `given toggle via pusher is NOT possible when execute then disable notification by unregistering the pusher`() = runTest { // Given val fakeSession = fakeActiveSessionHolder.fakeSession - fakeSession.givenSessionId(A_SESSION_ID) every { fakeCheckIfCanToggleNotificationsViaPusherUseCase.execute(fakeSession) } returns false - coJustRun { fakeToggleNotificationsUseCase.execute(A_SESSION_ID, any()) } + coJustRun { fakeToggleNotificationsForCurrentSessionUseCase.execute(any()) } coJustRun { fakeUnregisterUnifiedPushUseCase.execute(any()) } // When @@ -75,7 +73,7 @@ class DisableNotificationsForCurrentSessionUseCaseTest { // Then coVerify { - fakeToggleNotificationsUseCase.execute(A_SESSION_ID, false) + fakeToggleNotificationsForCurrentSessionUseCase.execute(false) fakeUnregisterUnifiedPushUseCase.execute(fakePushersManager.instance) } } diff --git a/vector/src/test/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCaseTest.kt index 7beab170f2..d58ba7645c 100644 --- a/vector/src/test/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/notifications/EnableNotificationsForCurrentSessionUseCaseTest.kt @@ -18,10 +18,9 @@ package im.vector.app.features.settings.notifications import im.vector.app.core.pushers.EnsureFcmTokenIsRetrievedUseCase import im.vector.app.core.pushers.RegisterUnifiedPushUseCase -import im.vector.app.features.settings.devices.v2.notification.ToggleNotificationsUseCase -import im.vector.app.test.fakes.FakeActiveSessionHolder import im.vector.app.test.fakes.FakePushersManager import io.mockk.coJustRun +import io.mockk.coVerify import io.mockk.every import io.mockk.justRun import io.mockk.mockk @@ -30,20 +29,16 @@ import kotlinx.coroutines.test.runTest import org.amshove.kluent.shouldBe import org.junit.Test -private const val A_SESSION_ID = "session-id" - class EnableNotificationsForCurrentSessionUseCaseTest { - private val fakeActiveSessionHolder = FakeActiveSessionHolder() private val fakePushersManager = FakePushersManager() - private val fakeToggleNotificationsUseCase = mockk() + private val fakeToggleNotificationsForCurrentSessionUseCase = mockk() private val fakeRegisterUnifiedPushUseCase = mockk() private val fakeEnsureFcmTokenIsRetrievedUseCase = mockk() private val enableNotificationsForCurrentSessionUseCase = EnableNotificationsForCurrentSessionUseCase( - activeSessionHolder = fakeActiveSessionHolder.instance, pushersManager = fakePushersManager.instance, - toggleNotificationUseCase = fakeToggleNotificationsUseCase, + toggleNotificationsForCurrentSessionUseCase = fakeToggleNotificationsForCurrentSessionUseCase, registerUnifiedPushUseCase = fakeRegisterUnifiedPushUseCase, ensureFcmTokenIsRetrievedUseCase = fakeEnsureFcmTokenIsRetrievedUseCase, ) @@ -52,12 +47,10 @@ class EnableNotificationsForCurrentSessionUseCaseTest { fun `given no existing pusher and a registered distributor when execute then a new pusher is registered and result is success`() = runTest { // Given val aDistributor = "distributor" - val fakeSession = fakeActiveSessionHolder.fakeSession - fakeSession.givenSessionId(A_SESSION_ID) fakePushersManager.givenGetPusherForCurrentSessionReturns(null) every { fakeRegisterUnifiedPushUseCase.execute(any()) } returns RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.Success justRun { fakeEnsureFcmTokenIsRetrievedUseCase.execute(any(), any()) } - coJustRun { fakeToggleNotificationsUseCase.execute(A_SESSION_ID, any()) } + coJustRun { fakeToggleNotificationsForCurrentSessionUseCase.execute(any()) } // When val result = enableNotificationsForCurrentSessionUseCase.execute(aDistributor) @@ -68,6 +61,9 @@ class EnableNotificationsForCurrentSessionUseCaseTest { fakeRegisterUnifiedPushUseCase.execute(aDistributor) fakeEnsureFcmTokenIsRetrievedUseCase.execute(fakePushersManager.instance, registerPusher = true) } + coVerify { + fakeToggleNotificationsForCurrentSessionUseCase.execute(enabled = true) + } } @Test @@ -86,20 +82,4 @@ class EnableNotificationsForCurrentSessionUseCaseTest { fakeRegisterUnifiedPushUseCase.execute(aDistributor) } } - - @Test - fun `given no deviceId for current session when execute then result is failure`() = runTest { - // Given - val aDistributor = "distributor" - val fakeSession = fakeActiveSessionHolder.fakeSession - fakeSession.givenSessionId(null) - fakePushersManager.givenGetPusherForCurrentSessionReturns(mockk()) - every { fakeRegisterUnifiedPushUseCase.execute(any()) } returns RegisterUnifiedPushUseCase.RegisterUnifiedPushResult.NeedToAskUserForDistributor - - // When - val result = enableNotificationsForCurrentSessionUseCase.execute(aDistributor) - - // Then - result shouldBe EnableNotificationsForCurrentSessionUseCase.EnableNotificationsResult.Failure - } } diff --git a/vector/src/test/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModelTest.kt b/vector/src/test/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModelTest.kt index f9d7527316..270447c461 100644 --- a/vector/src/test/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModelTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModelTest.kt @@ -46,6 +46,7 @@ class VectorSettingsNotificationPreferenceViewModelTest { private val fakeUnregisterUnifiedPushUseCase = mockk() private val fakeRegisterUnifiedPushUseCase = mockk() private val fakeEnsureFcmTokenIsRetrievedUseCase = mockk() + private val fakeToggleNotificationsForCurrentSessionUseCase = mockk() private fun createViewModel() = VectorSettingsNotificationPreferenceViewModel( initialState = VectorDummyViewState(), @@ -56,6 +57,7 @@ class VectorSettingsNotificationPreferenceViewModelTest { unregisterUnifiedPushUseCase = fakeUnregisterUnifiedPushUseCase, registerUnifiedPushUseCase = fakeRegisterUnifiedPushUseCase, ensureFcmTokenIsRetrievedUseCase = fakeEnsureFcmTokenIsRetrievedUseCase, + toggleNotificationsForCurrentSessionUseCase = fakeToggleNotificationsForCurrentSessionUseCase, ) @Test @@ -125,29 +127,6 @@ class VectorSettingsNotificationPreferenceViewModelTest { } } - @Test - fun `given EnableNotificationsForDevice action and enable failure when handling action then enable use case is called`() { - // Given - val viewModel = createViewModel() - val aDistributor = "aDistributor" - val action = VectorSettingsNotificationPreferenceViewAction.EnableNotificationsForDevice(aDistributor) - coEvery { fakeEnableNotificationsForCurrentSessionUseCase.execute(any()) } returns - EnableNotificationsForCurrentSessionUseCase.EnableNotificationsResult.Failure - val expectedEvent = VectorSettingsNotificationPreferenceViewEvent.EnableNotificationForDeviceFailure - - // When - val viewModelTest = viewModel.test() - viewModel.handle(action) - - // Then - viewModelTest - .assertEvent { event -> event == expectedEvent } - .finish() - coVerify { - fakeEnableNotificationsForCurrentSessionUseCase.execute(aDistributor) - } - } - @Test fun `given RegisterPushDistributor action and register success when handling action then register use case is called`() { // Given @@ -158,6 +137,7 @@ class VectorSettingsNotificationPreferenceViewModelTest { coJustRun { fakeUnregisterUnifiedPushUseCase.execute(any()) } val areNotificationsEnabled = true fakeVectorPreferences.givenAreNotificationsEnabledForDevice(areNotificationsEnabled) + coJustRun { fakeToggleNotificationsForCurrentSessionUseCase.execute(any()) } justRun { fakeEnsureFcmTokenIsRetrievedUseCase.execute(any(), any()) } val expectedEvent = VectorSettingsNotificationPreferenceViewEvent.NotificationMethodChanged @@ -173,6 +153,7 @@ class VectorSettingsNotificationPreferenceViewModelTest { fakeUnregisterUnifiedPushUseCase.execute(fakePushersManager.instance) fakeRegisterUnifiedPushUseCase.execute(aDistributor) fakeEnsureFcmTokenIsRetrievedUseCase.execute(fakePushersManager.instance, registerPusher = areNotificationsEnabled) + fakeToggleNotificationsForCurrentSessionUseCase.execute(enabled = areNotificationsEnabled) } } From b78de152286cc75f350a444863e6625d79144752 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 1 Dec 2022 17:55:30 +0100 Subject: [PATCH 069/108] Adding unit tests for new toggle notification for current session use case --- ...leNotificationsForCurrentSessionUseCase.kt | 1 - ...tificationsForCurrentSessionUseCaseTest.kt | 117 ++++++++++++++++++ 2 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 vector/src/test/java/im/vector/app/features/settings/notifications/ToggleNotificationsForCurrentSessionUseCaseTest.kt diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/ToggleNotificationsForCurrentSessionUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/ToggleNotificationsForCurrentSessionUseCase.kt index 64b4b1bb89..3dc73f0a31 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/ToggleNotificationsForCurrentSessionUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/ToggleNotificationsForCurrentSessionUseCase.kt @@ -33,7 +33,6 @@ class ToggleNotificationsForCurrentSessionUseCase @Inject constructor( private val deleteNotificationSettingsAccountDataUseCase: DeleteNotificationSettingsAccountDataUseCase, ) { - // TODO add unit tests suspend fun execute(enabled: Boolean) { val session = activeSessionHolder.getSafeActiveSession() ?: return val deviceId = session.sessionParams.deviceId ?: return diff --git a/vector/src/test/java/im/vector/app/features/settings/notifications/ToggleNotificationsForCurrentSessionUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/notifications/ToggleNotificationsForCurrentSessionUseCaseTest.kt new file mode 100644 index 0000000000..f49aafab8a --- /dev/null +++ b/vector/src/test/java/im/vector/app/features/settings/notifications/ToggleNotificationsForCurrentSessionUseCaseTest.kt @@ -0,0 +1,117 @@ +/* + * 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.settings.notifications + +import im.vector.app.features.settings.devices.v2.notification.CheckIfCanToggleNotificationsViaPusherUseCase +import im.vector.app.features.settings.devices.v2.notification.DeleteNotificationSettingsAccountDataUseCase +import im.vector.app.features.settings.devices.v2.notification.SetNotificationSettingsAccountDataUseCase +import im.vector.app.test.fakes.FakeActiveSessionHolder +import im.vector.app.test.fakes.FakeUnifiedPushHelper +import im.vector.app.test.fixtures.PusherFixture +import io.mockk.coJustRun +import io.mockk.coVerify +import io.mockk.every +import io.mockk.mockk +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent + +class ToggleNotificationsForCurrentSessionUseCaseTest { + + private val fakeActiveSessionHolder = FakeActiveSessionHolder() + private val fakeUnifiedPushHelper = FakeUnifiedPushHelper() + private val fakeCheckIfCanToggleNotificationsViaPusherUseCase = mockk() + private val fakeSetNotificationSettingsAccountDataUseCase = mockk() + private val fakeDeleteNotificationSettingsAccountDataUseCase = mockk() + + private val toggleNotificationsForCurrentSessionUseCase = ToggleNotificationsForCurrentSessionUseCase( + activeSessionHolder = fakeActiveSessionHolder.instance, + unifiedPushHelper = fakeUnifiedPushHelper.instance, + checkIfCanToggleNotificationsViaPusherUseCase = fakeCheckIfCanToggleNotificationsViaPusherUseCase, + setNotificationSettingsAccountDataUseCase = fakeSetNotificationSettingsAccountDataUseCase, + deleteNotificationSettingsAccountDataUseCase = fakeDeleteNotificationSettingsAccountDataUseCase, + ) + + @Test + fun `given background sync is enabled when execute then set the related account data with correct value`() = runTest { + // Given + val enabled = true + val aDeviceId = "deviceId" + fakeUnifiedPushHelper.givenIsBackgroundSyncReturns(true) + fakeActiveSessionHolder.fakeSession.givenSessionId(aDeviceId) + coJustRun { fakeSetNotificationSettingsAccountDataUseCase.execute(any(), any(), any()) } + val expectedNotificationContent = LocalNotificationSettingsContent(isSilenced = !enabled) + + // When + toggleNotificationsForCurrentSessionUseCase.execute(enabled) + + // Then + coVerify { + fakeSetNotificationSettingsAccountDataUseCase.execute( + fakeActiveSessionHolder.fakeSession, + aDeviceId, + expectedNotificationContent + ) + } + } + + @Test + fun `given background sync is not enabled and toggle pusher is possible when execute then delete any related account data and toggle pusher`() = runTest { + // Given + val enabled = true + val aDeviceId = "deviceId" + fakeUnifiedPushHelper.givenIsBackgroundSyncReturns(false) + fakeActiveSessionHolder.fakeSession.givenSessionId(aDeviceId) + coJustRun { fakeDeleteNotificationSettingsAccountDataUseCase.execute(any()) } + every { fakeCheckIfCanToggleNotificationsViaPusherUseCase.execute(any()) } returns true + val aPusher = PusherFixture.aPusher(deviceId = aDeviceId) + fakeActiveSessionHolder.fakeSession.fakePushersService.givenGetPushers(listOf(aPusher)) + + // When + toggleNotificationsForCurrentSessionUseCase.execute(enabled) + + // Then + coVerify { + fakeDeleteNotificationSettingsAccountDataUseCase.execute(fakeActiveSessionHolder.fakeSession) + fakeCheckIfCanToggleNotificationsViaPusherUseCase.execute(fakeActiveSessionHolder.fakeSession) + } + fakeActiveSessionHolder.fakeSession.fakePushersService.verifyTogglePusherCalled(aPusher, enabled) + } + + @Test + fun `given background sync is not enabled and toggle pusher is not possible when execute then only delete any related account data`() = runTest { + // Given + val enabled = true + val aDeviceId = "deviceId" + fakeUnifiedPushHelper.givenIsBackgroundSyncReturns(false) + fakeActiveSessionHolder.fakeSession.givenSessionId(aDeviceId) + coJustRun { fakeDeleteNotificationSettingsAccountDataUseCase.execute(any()) } + every { fakeCheckIfCanToggleNotificationsViaPusherUseCase.execute(any()) } returns false + + // When + toggleNotificationsForCurrentSessionUseCase.execute(enabled) + + // Then + coVerify { + fakeDeleteNotificationSettingsAccountDataUseCase.execute(fakeActiveSessionHolder.fakeSession) + fakeCheckIfCanToggleNotificationsViaPusherUseCase.execute(fakeActiveSessionHolder.fakeSession) + } + coVerify(inverse = true) { + fakeActiveSessionHolder.fakeSession.fakePushersService.togglePusher(any(), any()) + } + } +} From e09b9a2ce0e245e1311374135807c835bf14e419 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Fri, 2 Dec 2022 10:03:37 +0100 Subject: [PATCH 070/108] Fixing wrong notification status when no registered pusher for the session --- .../notification/GetNotificationsStatusUseCase.kt | 8 +++++++- .../GetNotificationsStatusUseCaseTest.kt | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCase.kt index f98fd63efb..ae7e859573 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCase.kt @@ -51,7 +51,13 @@ class GetNotificationsStatusUseCase @Inject constructor( .livePushers() .map { it.filter { pusher -> pusher.deviceId == deviceId } } .map { it.takeIf { it.isNotEmpty() }?.any { pusher -> pusher.enabled } } - .map { if (it == true) NotificationsStatus.ENABLED else NotificationsStatus.DISABLED } + .map { + when (it) { + true -> NotificationsStatus.ENABLED + false -> NotificationsStatus.DISABLED + else -> NotificationsStatus.NOT_SUPPORTED + } + } .distinctUntilChanged() } else { flowOf(NotificationsStatus.NOT_SUPPORTED) diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCaseTest.kt index d4c3aa5788..3c454f7965 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCaseTest.kt @@ -105,6 +105,20 @@ class GetNotificationsStatusUseCaseTest { result.firstOrNull() shouldBeEqualTo NotificationsStatus.ENABLED } + @Test + fun `given toggle via pusher is supported and no registered pusher when execute then resulting flow contains NOT_SUPPORTED value`() = runTest { + // Given + fakeSession.pushersService().givenPushersLive(emptyList()) + every { fakeCheckIfCanToggleNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) } returns false + every { fakeCanToggleNotificationsViaPusherUseCase.execute(fakeSession) } returns flowOf(true) + + // When + val result = getNotificationsStatusUseCase.execute(fakeSession, A_DEVICE_ID) + + // Then + result.firstOrNull() shouldBeEqualTo NotificationsStatus.NOT_SUPPORTED + } + @Test fun `given current session and toggle via account data is supported when execute then resulting flow contains status based on settings value`() = runTest { // Given From 635f975b6cb5bbb8170a982e4b713e432861e9d4 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Fri, 2 Dec 2022 15:13:10 +0100 Subject: [PATCH 071/108] Fix missing unregister of pusher when notifications are disabled --- .../features/home/HomeActivityViewModel.kt | 10 +++++++ ...leNotificationsForCurrentSessionUseCase.kt | 11 +------- ...tificationsForCurrentSessionUseCaseTest.kt | 28 +------------------ 3 files changed, 12 insertions(+), 37 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt index 26034fc09c..a54ce2cff3 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt @@ -29,6 +29,7 @@ import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.pushers.EnsureFcmTokenIsRetrievedUseCase import im.vector.app.core.pushers.PushersManager import im.vector.app.core.pushers.RegisterUnifiedPushUseCase +import im.vector.app.core.pushers.UnregisterUnifiedPushUseCase import im.vector.app.features.analytics.AnalyticsConfig import im.vector.app.features.analytics.AnalyticsTracker import im.vector.app.features.analytics.extensions.toAnalyticsType @@ -92,6 +93,7 @@ class HomeActivityViewModel @AssistedInject constructor( private val stopOngoingVoiceBroadcastUseCase: StopOngoingVoiceBroadcastUseCase, private val pushersManager: PushersManager, private val registerUnifiedPushUseCase: RegisterUnifiedPushUseCase, + private val unregisterUnifiedPushUseCase: UnregisterUnifiedPushUseCase, private val ensureFcmTokenIsRetrievedUseCase: EnsureFcmTokenIsRetrievedUseCase, ) : VectorViewModel(initialState) { @@ -130,6 +132,8 @@ class HomeActivityViewModel @AssistedInject constructor( private fun registerUnifiedPushIfNeeded() { if (vectorPreferences.areNotificationEnabledForDevice()) { registerUnifiedPush(distributor = "") + } else { + unregisterUnifiedPush() } } @@ -146,6 +150,12 @@ class HomeActivityViewModel @AssistedInject constructor( } } + private fun unregisterUnifiedPush() { + viewModelScope.launch { + unregisterUnifiedPushUseCase.execute(pushersManager) + } + } + private fun observeReleaseNotes() = withState { state -> if (vectorPreferences.isNewAppLayoutEnabled()) { // we don't want to show release notes for new users or after relogin diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCase.kt index daa58578d6..0c50a296f3 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCase.kt @@ -16,27 +16,18 @@ package im.vector.app.features.settings.notifications -import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.pushers.PushersManager import im.vector.app.core.pushers.UnregisterUnifiedPushUseCase -import im.vector.app.features.settings.devices.v2.notification.CheckIfCanToggleNotificationsViaPusherUseCase import javax.inject.Inject class DisableNotificationsForCurrentSessionUseCase @Inject constructor( - private val activeSessionHolder: ActiveSessionHolder, private val pushersManager: PushersManager, - private val checkIfCanToggleNotificationsViaPusherUseCase: CheckIfCanToggleNotificationsViaPusherUseCase, private val toggleNotificationsForCurrentSessionUseCase: ToggleNotificationsForCurrentSessionUseCase, private val unregisterUnifiedPushUseCase: UnregisterUnifiedPushUseCase, ) { suspend fun execute() { - val session = activeSessionHolder.getSafeActiveSession() ?: return toggleNotificationsForCurrentSessionUseCase.execute(enabled = false) - - // handle case when server does not support toggle of pusher - if (!checkIfCanToggleNotificationsViaPusherUseCase.execute(session)) { - unregisterUnifiedPushUseCase.execute(pushersManager) - } + unregisterUnifiedPushUseCase.execute(pushersManager) } } diff --git a/vector/src/test/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCaseTest.kt index b7749d0252..669b20fc1a 100644 --- a/vector/src/test/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/notifications/DisableNotificationsForCurrentSessionUseCaseTest.kt @@ -17,54 +17,28 @@ package im.vector.app.features.settings.notifications import im.vector.app.core.pushers.UnregisterUnifiedPushUseCase -import im.vector.app.features.settings.devices.v2.notification.CheckIfCanToggleNotificationsViaPusherUseCase -import im.vector.app.test.fakes.FakeActiveSessionHolder import im.vector.app.test.fakes.FakePushersManager import io.mockk.coJustRun import io.mockk.coVerify -import io.mockk.every import io.mockk.mockk import kotlinx.coroutines.test.runTest import org.junit.Test class DisableNotificationsForCurrentSessionUseCaseTest { - private val fakeActiveSessionHolder = FakeActiveSessionHolder() private val fakePushersManager = FakePushersManager() - private val fakeCheckIfCanToggleNotificationsViaPusherUseCase = mockk() private val fakeToggleNotificationsForCurrentSessionUseCase = mockk() private val fakeUnregisterUnifiedPushUseCase = mockk() private val disableNotificationsForCurrentSessionUseCase = DisableNotificationsForCurrentSessionUseCase( - activeSessionHolder = fakeActiveSessionHolder.instance, pushersManager = fakePushersManager.instance, - checkIfCanToggleNotificationsViaPusherUseCase = fakeCheckIfCanToggleNotificationsViaPusherUseCase, toggleNotificationsForCurrentSessionUseCase = fakeToggleNotificationsForCurrentSessionUseCase, unregisterUnifiedPushUseCase = fakeUnregisterUnifiedPushUseCase, ) @Test - fun `given toggle via pusher is possible when execute then disable notification via toggle of existing pusher`() = runTest { + fun `when execute then disable notifications and unregister the pusher`() = runTest { // Given - val fakeSession = fakeActiveSessionHolder.fakeSession - every { fakeCheckIfCanToggleNotificationsViaPusherUseCase.execute(fakeSession) } returns true - coJustRun { fakeToggleNotificationsForCurrentSessionUseCase.execute(any()) } - - // When - disableNotificationsForCurrentSessionUseCase.execute() - - // Then - coVerify { fakeToggleNotificationsForCurrentSessionUseCase.execute(false) } - coVerify(inverse = true) { - fakeUnregisterUnifiedPushUseCase.execute(any()) - } - } - - @Test - fun `given toggle via pusher is NOT possible when execute then disable notification by unregistering the pusher`() = runTest { - // Given - val fakeSession = fakeActiveSessionHolder.fakeSession - every { fakeCheckIfCanToggleNotificationsViaPusherUseCase.execute(fakeSession) } returns false coJustRun { fakeToggleNotificationsForCurrentSessionUseCase.execute(any()) } coJustRun { fakeUnregisterUnifiedPushUseCase.execute(any()) } From 18ab8a1279c94058b55d127df3e6985c2b5014fa Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 23 Nov 2022 17:22:34 +0100 Subject: [PATCH 072/108] Adding changelog entry --- changelog.d/7632.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7632.feature diff --git a/changelog.d/7632.feature b/changelog.d/7632.feature new file mode 100644 index 0000000000..460f987756 --- /dev/null +++ b/changelog.d/7632.feature @@ -0,0 +1 @@ +Update notifications setting when m.local_notification_settings. event changes for current device From 9fbfe82044ec6792a114856a0bf408d40123f290 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 24 Nov 2022 16:56:06 +0100 Subject: [PATCH 073/108] Fix observation of the notification status for the current session --- ...oggleNotificationsViaAccountDataUseCase.kt | 32 +++++ ...icationSettingsAccountDataAsFlowUseCase.kt | 37 ++++++ .../GetNotificationsStatusUseCase.kt | 34 +++--- ...eNotificationsViaAccountDataUseCaseTest.kt | 88 ++++++++++++++ ...ionSettingsAccountDataAsFlowUseCaseTest.kt | 109 ++++++++++++++++++ .../GetNotificationsStatusUseCaseTest.kt | 25 ++-- .../fakes/FakeSessionAccountDataService.kt | 11 ++ 7 files changed, 314 insertions(+), 22 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CanToggleNotificationsViaAccountDataUseCase.kt create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataAsFlowUseCase.kt create mode 100644 vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CanToggleNotificationsViaAccountDataUseCaseTest.kt create mode 100644 vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataAsFlowUseCaseTest.kt diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CanToggleNotificationsViaAccountDataUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CanToggleNotificationsViaAccountDataUseCase.kt new file mode 100644 index 0000000000..ac466852eb --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CanToggleNotificationsViaAccountDataUseCase.kt @@ -0,0 +1,32 @@ +/* + * 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.settings.devices.v2.notification + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import org.matrix.android.sdk.api.session.Session +import javax.inject.Inject + +class CanToggleNotificationsViaAccountDataUseCase @Inject constructor( + private val getNotificationSettingsAccountDataAsFlowUseCase: GetNotificationSettingsAccountDataAsFlowUseCase, +) { + + fun execute(session: Session, deviceId: String): Flow { + return getNotificationSettingsAccountDataAsFlowUseCase.execute(session, deviceId) + .map { it?.isSilenced != null } + } +} diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataAsFlowUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataAsFlowUseCase.kt new file mode 100644 index 0000000000..ea4bd40f1f --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataAsFlowUseCase.kt @@ -0,0 +1,37 @@ +/* + * 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.settings.devices.v2.notification + +import androidx.lifecycle.asFlow +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes +import org.matrix.android.sdk.api.session.events.model.toModel +import javax.inject.Inject + +class GetNotificationSettingsAccountDataAsFlowUseCase @Inject constructor() { + + fun execute(session: Session, deviceId: String): Flow { + return session + .accountDataService() + .getLiveUserAccountDataEvent(UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId) + .asFlow() + .map { it.getOrNull()?.content?.toModel() } + } +} diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCase.kt index ae7e859573..8cf684975e 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCase.kt @@ -31,20 +31,30 @@ import javax.inject.Inject class GetNotificationsStatusUseCase @Inject constructor( private val canToggleNotificationsViaPusherUseCase: CanToggleNotificationsViaPusherUseCase, - private val checkIfCanToggleNotificationsViaAccountDataUseCase: CheckIfCanToggleNotificationsViaAccountDataUseCase, + private val canToggleNotificationsViaAccountDataUseCase: CanToggleNotificationsViaAccountDataUseCase, ) { fun execute(session: Session, deviceId: String): Flow { - return when { - checkIfCanToggleNotificationsViaAccountDataUseCase.execute(session, deviceId) -> { - session.flow() - .liveUserAccountData(UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId) - .unwrap() - .map { it.content.toModel()?.isSilenced?.not() } - .map { if (it == true) NotificationsStatus.ENABLED else NotificationsStatus.DISABLED } - .distinctUntilChanged() - } - else -> canToggleNotificationsViaPusherUseCase.execute(session) + return canToggleNotificationsViaAccountDataUseCase.execute(session, deviceId) + .flatMapLatest { canToggle -> + if (canToggle) { + notificationStatusFromAccountData(session, deviceId) + } else { + notificationStatusFromPusher(session, deviceId) + } + } + .distinctUntilChanged() + } + + private fun notificationStatusFromAccountData(session: Session, deviceId: String) = + session.flow() + .liveUserAccountData(UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId) + .unwrap() + .map { it.content.toModel()?.isSilenced?.not() } + .map { if (it == true) NotificationsStatus.ENABLED else NotificationsStatus.DISABLED } + + private fun notificationStatusFromPusher(session: Session, deviceId: String) = + canToggleNotificationsViaPusherUseCase.execute(session) .flatMapLatest { canToggle -> if (canToggle) { session.flow() @@ -63,6 +73,4 @@ class GetNotificationsStatusUseCase @Inject constructor( flowOf(NotificationsStatus.NOT_SUPPORTED) } } - } - } } diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CanToggleNotificationsViaAccountDataUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CanToggleNotificationsViaAccountDataUseCaseTest.kt new file mode 100644 index 0000000000..a1dfed6902 --- /dev/null +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CanToggleNotificationsViaAccountDataUseCaseTest.kt @@ -0,0 +1,88 @@ +/* + * 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.settings.devices.v2.notification + +import im.vector.app.test.fakes.FakeSession +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runTest +import org.amshove.kluent.shouldBe +import org.junit.Test +import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent + +class CanToggleNotificationsViaAccountDataUseCaseTest { + + private val fakeGetNotificationSettingsAccountDataAsFlowUseCase = mockk() + + private val canToggleNotificationsViaAccountDataUseCase = CanToggleNotificationsViaAccountDataUseCase( + getNotificationSettingsAccountDataAsFlowUseCase = fakeGetNotificationSettingsAccountDataAsFlowUseCase, + ) + + @Test + fun `given current session and content for account data when execute then true is returned`() = runTest { + // Given + val aSession = FakeSession() + val aDeviceId = "aDeviceId" + val localNotificationSettingsContent = LocalNotificationSettingsContent( + isSilenced = true, + ) + every { fakeGetNotificationSettingsAccountDataAsFlowUseCase.execute(any(), any()) } returns flowOf(localNotificationSettingsContent) + + // When + val result = canToggleNotificationsViaAccountDataUseCase.execute(aSession, aDeviceId).firstOrNull() + + // Then + result shouldBe true + verify { fakeGetNotificationSettingsAccountDataAsFlowUseCase.execute(aSession, aDeviceId) } + } + + @Test + fun `given current session and empty content for account data when execute then false is returned`() = runTest { + // Given + val aSession = FakeSession() + val aDeviceId = "aDeviceId" + val localNotificationSettingsContent = LocalNotificationSettingsContent( + isSilenced = null, + ) + every { fakeGetNotificationSettingsAccountDataAsFlowUseCase.execute(any(), any()) } returns flowOf(localNotificationSettingsContent) + + // When + val result = canToggleNotificationsViaAccountDataUseCase.execute(aSession, aDeviceId).firstOrNull() + + // Then + result shouldBe false + verify { fakeGetNotificationSettingsAccountDataAsFlowUseCase.execute(aSession, aDeviceId) } + } + + @Test + fun `given current session and no related account data when execute then false is returned`() = runTest { + // Given + val aSession = FakeSession() + val aDeviceId = "aDeviceId" + every { fakeGetNotificationSettingsAccountDataAsFlowUseCase.execute(any(), any()) } returns flowOf(null) + + // When + val result = canToggleNotificationsViaAccountDataUseCase.execute(aSession, aDeviceId).firstOrNull() + + // Then + result shouldBe false + verify { fakeGetNotificationSettingsAccountDataAsFlowUseCase.execute(aSession, aDeviceId) } + } +} diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataAsFlowUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataAsFlowUseCaseTest.kt new file mode 100644 index 0000000000..6280d4c48b --- /dev/null +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataAsFlowUseCaseTest.kt @@ -0,0 +1,109 @@ +/* + * 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.settings.devices.v2.notification + +import im.vector.app.test.fakes.FakeFlowLiveDataConversions +import im.vector.app.test.fakes.FakeSession +import im.vector.app.test.fakes.givenAsFlow +import io.mockk.unmockkAll +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.test.runTest +import org.amshove.kluent.shouldBeEqualTo +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent +import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes +import org.matrix.android.sdk.api.session.events.model.toContent + +class GetNotificationSettingsAccountDataAsFlowUseCaseTest { + + private val fakeFlowLiveDataConversions = FakeFlowLiveDataConversions() + private val getNotificationSettingsAccountDataAsFlowUseCase = GetNotificationSettingsAccountDataAsFlowUseCase() + + @Before + fun setUp() { + fakeFlowLiveDataConversions.setup() + } + + @After + fun tearDown() { + unmockkAll() + } + + @Test + fun `given a device id when execute then retrieve the account data event corresponding to this id if any`() = runTest { + // Given + val aDeviceId = "device-id" + val aSession = FakeSession() + val expectedContent = LocalNotificationSettingsContent(isSilenced = true) + aSession + .accountDataService() + .givenGetLiveUserAccountDataEventReturns( + type = UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + aDeviceId, + content = expectedContent.toContent(), + ) + .givenAsFlow() + + // When + val result = getNotificationSettingsAccountDataAsFlowUseCase.execute(aSession, aDeviceId).firstOrNull() + + // Then + result shouldBeEqualTo expectedContent + } + + @Test + fun `given a device id and no content for account data when execute then retrieve the account data event corresponding to this id if any`() = runTest { + // Given + val aDeviceId = "device-id" + val aSession = FakeSession() + aSession + .accountDataService() + .givenGetLiveUserAccountDataEventReturns( + type = UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + aDeviceId, + content = null, + ) + .givenAsFlow() + + // When + val result = getNotificationSettingsAccountDataAsFlowUseCase.execute(aSession, aDeviceId).firstOrNull() + + // Then + result shouldBeEqualTo null + } + + @Test + fun `given a device id and empty content for account data when execute then retrieve the account data event corresponding to this id if any`() = runTest { + // Given + val aDeviceId = "device-id" + val aSession = FakeSession() + val expectedContent = LocalNotificationSettingsContent(isSilenced = null) + aSession + .accountDataService() + .givenGetLiveUserAccountDataEventReturns( + type = UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + aDeviceId, + content = expectedContent.toContent(), + ) + .givenAsFlow() + + // When + val result = getNotificationSettingsAccountDataAsFlowUseCase.execute(aSession, aDeviceId).firstOrNull() + + // Then + result shouldBeEqualTo expectedContent + } +} diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCaseTest.kt index 3c454f7965..e4b681c5ec 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationsStatusUseCaseTest.kt @@ -22,6 +22,7 @@ import im.vector.app.test.fixtures.PusherFixture import im.vector.app.test.testDispatcher import io.mockk.every import io.mockk.mockk +import io.mockk.verify import io.mockk.verifyOrder import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.firstOrNull @@ -46,14 +47,14 @@ class GetNotificationsStatusUseCaseTest { val instantTaskExecutorRule = InstantTaskExecutorRule() private val fakeSession = FakeSession() - private val fakeCheckIfCanToggleNotificationsViaAccountDataUseCase = - mockk() + private val fakeCanToggleNotificationsViaAccountDataUseCase = + mockk() private val fakeCanToggleNotificationsViaPusherUseCase = mockk() private val getNotificationsStatusUseCase = GetNotificationsStatusUseCase( - checkIfCanToggleNotificationsViaAccountDataUseCase = fakeCheckIfCanToggleNotificationsViaAccountDataUseCase, + canToggleNotificationsViaAccountDataUseCase = fakeCanToggleNotificationsViaAccountDataUseCase, canToggleNotificationsViaPusherUseCase = fakeCanToggleNotificationsViaPusherUseCase, ) @@ -70,7 +71,7 @@ class GetNotificationsStatusUseCaseTest { @Test fun `given current session and toggle is not supported when execute then resulting flow contains NOT_SUPPORTED value`() = runTest { // Given - every { fakeCheckIfCanToggleNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) } returns false + every { fakeCanToggleNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) } returns flowOf(false) every { fakeCanToggleNotificationsViaPusherUseCase.execute(fakeSession) } returns flowOf(false) // When @@ -80,7 +81,7 @@ class GetNotificationsStatusUseCaseTest { result.firstOrNull() shouldBeEqualTo NotificationsStatus.NOT_SUPPORTED verifyOrder { // we should first check account data - fakeCheckIfCanToggleNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) + fakeCanToggleNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) fakeCanToggleNotificationsViaPusherUseCase.execute(fakeSession) } } @@ -95,7 +96,7 @@ class GetNotificationsStatusUseCaseTest { ) ) fakeSession.pushersService().givenPushersLive(pushers) - every { fakeCheckIfCanToggleNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) } returns false + every { fakeCanToggleNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) } returns flowOf(false) every { fakeCanToggleNotificationsViaPusherUseCase.execute(fakeSession) } returns flowOf(true) // When @@ -109,7 +110,7 @@ class GetNotificationsStatusUseCaseTest { fun `given toggle via pusher is supported and no registered pusher when execute then resulting flow contains NOT_SUPPORTED value`() = runTest { // Given fakeSession.pushersService().givenPushersLive(emptyList()) - every { fakeCheckIfCanToggleNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) } returns false + every { fakeCanToggleNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) } returns flowOf(false) every { fakeCanToggleNotificationsViaPusherUseCase.execute(fakeSession) } returns flowOf(true) // When @@ -120,7 +121,7 @@ class GetNotificationsStatusUseCaseTest { } @Test - fun `given current session and toggle via account data is supported when execute then resulting flow contains status based on settings value`() = runTest { + fun `given current session and toggle via account data is supported when execute then resulting flow contains status based on account data`() = runTest { // Given fakeSession .accountDataService() @@ -130,7 +131,7 @@ class GetNotificationsStatusUseCaseTest { isSilenced = false ).toContent(), ) - every { fakeCheckIfCanToggleNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) } returns true + every { fakeCanToggleNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) } returns flowOf(true) every { fakeCanToggleNotificationsViaPusherUseCase.execute(fakeSession) } returns flowOf(false) // When @@ -138,5 +139,11 @@ class GetNotificationsStatusUseCaseTest { // Then result.firstOrNull() shouldBeEqualTo NotificationsStatus.ENABLED + verify { + fakeCanToggleNotificationsViaAccountDataUseCase.execute(fakeSession, A_DEVICE_ID) + } + verify(inverse = true) { + fakeCanToggleNotificationsViaPusherUseCase.execute(fakeSession) + } } } diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeSessionAccountDataService.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeSessionAccountDataService.kt index c44fc4a497..f1a0ae7452 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeSessionAccountDataService.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeSessionAccountDataService.kt @@ -16,6 +16,8 @@ package im.vector.app.test.fakes +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import io.mockk.coEvery import io.mockk.coVerify import io.mockk.every @@ -25,6 +27,8 @@ import io.mockk.runs import org.matrix.android.sdk.api.session.accountdata.SessionAccountDataService import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent import org.matrix.android.sdk.api.session.events.model.Content +import org.matrix.android.sdk.api.util.Optional +import org.matrix.android.sdk.api.util.toOptional class FakeSessionAccountDataService : SessionAccountDataService by mockk(relaxed = true) { @@ -32,6 +36,13 @@ class FakeSessionAccountDataService : SessionAccountDataService by mockk(relaxed every { getUserAccountDataEvent(type) } returns content?.let { UserAccountDataEvent(type, it) } } + fun givenGetLiveUserAccountDataEventReturns(type: String, content: Content?): LiveData> { + return MutableLiveData(content?.let { UserAccountDataEvent(type, it) }.toOptional()) + .also { + every { getLiveUserAccountDataEvent(type) } returns it + } + } + fun givenUpdateUserAccountDataEventSucceeds() { coEvery { updateUserAccountData(any(), any()) } just runs } From c12af5a800f5bd436482ec118052ecabb76a3460 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Fri, 2 Dec 2022 14:14:51 +0100 Subject: [PATCH 074/108] Listening changes on notifications enabled preference to update the UI in settings --- ...SettingsNotificationPreferenceViewModel.kt | 27 ++++++++++++++ ...ingsNotificationPreferenceViewModelTest.kt | 35 +++++++++++++++++++ .../app/test/fakes/FakeVectorPreferences.kt | 7 ---- 3 files changed, 62 insertions(+), 7 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModel.kt index 48e82b35e8..9530be599e 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModel.kt @@ -16,6 +16,8 @@ package im.vector.app.features.settings.notifications +import android.content.SharedPreferences +import androidx.annotation.VisibleForTesting import com.airbnb.mvrx.MavericksViewModelFactory import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -50,6 +52,31 @@ class VectorSettingsNotificationPreferenceViewModel @AssistedInject constructor( companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() + @VisibleForTesting + val notificationsPreferenceListener: SharedPreferences.OnSharedPreferenceChangeListener = + SharedPreferences.OnSharedPreferenceChangeListener { _, key -> + if (key == VectorPreferences.SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY) { + if (vectorPreferences.areNotificationEnabledForDevice()) { + _viewEvents.post(VectorSettingsNotificationPreferenceViewEvent.NotificationsForDeviceEnabled) + } else { + _viewEvents.post(VectorSettingsNotificationPreferenceViewEvent.NotificationsForDeviceDisabled) + } + } + } + + init { + observeNotificationsEnabledPreference() + } + + private fun observeNotificationsEnabledPreference() { + vectorPreferences.subscribeToChanges(notificationsPreferenceListener) + } + + override fun onCleared() { + vectorPreferences.unsubscribeToChanges(notificationsPreferenceListener) + super.onCleared() + } + override fun handle(action: VectorSettingsNotificationPreferenceViewAction) { when (action) { VectorSettingsNotificationPreferenceViewAction.DisableNotificationsForDevice -> handleDisableNotificationsForDevice() diff --git a/vector/src/test/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModelTest.kt b/vector/src/test/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModelTest.kt index 270447c461..ae36ee7600 100644 --- a/vector/src/test/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModelTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceViewModelTest.kt @@ -21,6 +21,7 @@ import im.vector.app.core.platform.VectorDummyViewState import im.vector.app.core.pushers.EnsureFcmTokenIsRetrievedUseCase import im.vector.app.core.pushers.RegisterUnifiedPushUseCase import im.vector.app.core.pushers.UnregisterUnifiedPushUseCase +import im.vector.app.features.settings.VectorPreferences.Companion.SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY import im.vector.app.test.fakes.FakePushersManager import im.vector.app.test.fakes.FakeVectorPreferences import im.vector.app.test.test @@ -60,6 +61,40 @@ class VectorSettingsNotificationPreferenceViewModelTest { toggleNotificationsForCurrentSessionUseCase = fakeToggleNotificationsForCurrentSessionUseCase, ) + @Test + fun `given view model init when notifications are enabled in preferences then view event is posted`() { + // Given + fakeVectorPreferences.givenAreNotificationsEnabledForDevice(true) + val expectedEvent = VectorSettingsNotificationPreferenceViewEvent.NotificationsForDeviceEnabled + val viewModel = createViewModel() + + // When + val viewModelTest = viewModel.test() + viewModel.notificationsPreferenceListener.onSharedPreferenceChanged(mockk(), SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY) + + // Then + viewModelTest + .assertEvent { event -> event == expectedEvent } + .finish() + } + + @Test + fun `given view model init when notifications are disabled in preferences then view event is posted`() { + // Given + fakeVectorPreferences.givenAreNotificationsEnabledForDevice(false) + val expectedEvent = VectorSettingsNotificationPreferenceViewEvent.NotificationsForDeviceDisabled + val viewModel = createViewModel() + + // When + val viewModelTest = viewModel.test() + viewModel.notificationsPreferenceListener.onSharedPreferenceChanged(mockk(), SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY) + + // Then + viewModelTest + .assertEvent { event -> event == expectedEvent } + .finish() + } + @Test fun `given DisableNotificationsForDevice action when handling action then disable use case is called`() { // Given diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt index 3d7de662bd..58bc1a18b8 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt @@ -16,7 +16,6 @@ package im.vector.app.test.fakes -import android.content.SharedPreferences.OnSharedPreferenceChangeListener import im.vector.app.features.settings.BackgroundSyncMode import im.vector.app.features.settings.VectorPreferences import io.mockk.every @@ -78,10 +77,4 @@ class FakeVectorPreferences { fun givenIsBackgroundSyncEnabled(isEnabled: Boolean) { every { instance.isBackgroundSyncEnabled() } returns isEnabled } - - fun givenChangeOnPreference(key: String) { - every { instance.subscribeToChanges(any()) } answers { - firstArg().onSharedPreferenceChanged(mockk(), key) - } - } } From b8023d66debbcff06df6f1c6bdd47a8fc41a3f0c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Dec 2022 09:57:02 +0100 Subject: [PATCH 075/108] Fix formatting --- vector/src/test/java/im/vector/app/screenshot/PaparazziRule.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/vector/src/test/java/im/vector/app/screenshot/PaparazziRule.kt b/vector/src/test/java/im/vector/app/screenshot/PaparazziRule.kt index 970bb15a25..a5cba20561 100644 --- a/vector/src/test/java/im/vector/app/screenshot/PaparazziRule.kt +++ b/vector/src/test/java/im/vector/app/screenshot/PaparazziRule.kt @@ -32,4 +32,3 @@ fun createPaparazziRule() = Paparazzi( theme = "Theme.Vector.Light", maxPercentDifference = 0.0, ) - From b4792c8a59e66715ee68f8ac650e712562b2b4a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Dec 2022 10:20:02 +0100 Subject: [PATCH 076/108] Bump leakcanary-android from 2.9.1 to 2.10 (#7570) --- vector-app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector-app/build.gradle b/vector-app/build.gradle index 68e20996ad..fa6aa5f0fd 100644 --- a/vector-app/build.gradle +++ b/vector-app/build.gradle @@ -404,6 +404,6 @@ dependencies { androidTestImplementation libs.androidx.fragmentTesting androidTestImplementation "org.jetbrains.kotlin:kotlin-reflect:1.7.22" debugImplementation libs.androidx.fragmentTesting - debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1' + debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.10' } From bbc756136cfa4b5d8dbc3baea00f88932b818b1f Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 5 Dec 2022 10:26:07 +0100 Subject: [PATCH 077/108] Adding the rename and signout actions in the menu --- .../v2/VectorSettingsDevicesFragment.kt | 54 ++++++++++++------- .../res/menu/menu_current_session_header.xml | 10 ++++ 2 files changed, 44 insertions(+), 20 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt index d748600416..64744f6eeb 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt @@ -101,6 +101,7 @@ class VectorSettingsDevicesFragment : initWaitingView() initCurrentSessionHeaderView() + initCurrentSessionListView() initOtherSessionsHeaderView() initOtherSessionsView() initSecurityRecommendationsView() @@ -153,6 +154,12 @@ class VectorSettingsDevicesFragment : } } + private fun initCurrentSessionListView() { + views.deviceListCurrentSession.viewVerifyButton.debouncedClicks { + viewModel.handle(DevicesAction.VerifyCurrentSession) + } + } + private fun initOtherSessionsHeaderView() { views.deviceListHeaderOtherSessions.setOnMenuItemClickListener { menuItem -> when (menuItem.itemId) { @@ -351,31 +358,38 @@ class VectorSettingsDevicesFragment : private fun renderCurrentSessionView(currentDeviceInfo: DeviceFullInfo?, hasOtherDevices: Boolean) { currentDeviceInfo?.let { - views.deviceListHeaderCurrentSession.isVisible = true - val colorDestructive = colorProvider.getColorFromAttribute(R.attr.colorError) - val signoutOtherSessionsItem = views.deviceListHeaderCurrentSession.menu.findItem(R.id.currentSessionHeaderSignoutOtherSessions) - signoutOtherSessionsItem.setTextColor(colorDestructive) - signoutOtherSessionsItem.isVisible = hasOtherDevices - views.deviceListCurrentSession.isVisible = true - val viewState = SessionInfoViewState( - isCurrentSession = true, - deviceFullInfo = it - ) - views.deviceListCurrentSession.render(viewState, dateFormatter, drawableProvider, colorProvider, stringProvider) - views.deviceListCurrentSession.debouncedClicks { - currentDeviceInfo.deviceInfo.deviceId?.let { deviceId -> navigateToSessionOverview(deviceId) } - } - views.deviceListCurrentSession.viewDetailsButton.debouncedClicks { - currentDeviceInfo.deviceInfo.deviceId?.let { deviceId -> navigateToSessionOverview(deviceId) } - } - views.deviceListCurrentSession.viewVerifyButton.debouncedClicks { - viewModel.handle(DevicesAction.VerifyCurrentSession) - } + renderCurrentSessionHeaderView(hasOtherDevices) + renderCurrentSessionListView(it) } ?: run { hideCurrentSessionView() } } + private fun renderCurrentSessionHeaderView(hasOtherDevices: Boolean) { + views.deviceListHeaderCurrentSession.isVisible = true + val colorDestructive = colorProvider.getColorFromAttribute(R.attr.colorError) + val signoutSessionItem = views.deviceListHeaderCurrentSession.menu.findItem(R.id.currentSessionHeaderSignout) + signoutSessionItem.setTextColor(colorDestructive) + val signoutOtherSessionsItem = views.deviceListHeaderCurrentSession.menu.findItem(R.id.currentSessionHeaderSignoutOtherSessions) + signoutOtherSessionsItem.setTextColor(colorDestructive) + signoutOtherSessionsItem.isVisible = hasOtherDevices + } + + private fun renderCurrentSessionListView(currentDeviceInfo: DeviceFullInfo) { + views.deviceListCurrentSession.isVisible = true + val viewState = SessionInfoViewState( + isCurrentSession = true, + deviceFullInfo = currentDeviceInfo + ) + views.deviceListCurrentSession.render(viewState, dateFormatter, drawableProvider, colorProvider, stringProvider) + views.deviceListCurrentSession.debouncedClicks { + currentDeviceInfo.deviceInfo.deviceId?.let { deviceId -> navigateToSessionOverview(deviceId) } + } + views.deviceListCurrentSession.viewDetailsButton.debouncedClicks { + currentDeviceInfo.deviceInfo.deviceId?.let { deviceId -> navigateToSessionOverview(deviceId) } + } + } + private fun navigateToSessionOverview(deviceId: String) { viewNavigator.navigateToSessionOverview( context = requireActivity(), diff --git a/vector/src/main/res/menu/menu_current_session_header.xml b/vector/src/main/res/menu/menu_current_session_header.xml index 3b00423488..993bee6178 100644 --- a/vector/src/main/res/menu/menu_current_session_header.xml +++ b/vector/src/main/res/menu/menu_current_session_header.xml @@ -4,6 +4,16 @@ xmlns:tools="http://schemas.android.com/tools" tools:ignore="AlwaysShowAction"> + + + + Date: Mon, 5 Dec 2022 09:42:20 +0000 Subject: [PATCH 078/108] Bump wysiwyg from 0.7.0.1 to 0.8.0 (#7666) Bumps [wysiwyg](https://github.com/matrix-org/matrix-wysiwyg) from 0.7.0.1 to 0.8.0. - [Release notes](https://github.com/matrix-org/matrix-wysiwyg/releases) - [Changelog](https://github.com/matrix-org/matrix-rich-text-editor/blob/main/CHANGELOG.md) - [Commits](https://github.com/matrix-org/matrix-wysiwyg/commits/0.8.0) --- updated-dependencies: - dependency-name: io.element.android:wysiwyg dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index 27bca6434f..fd630eba6d 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -98,7 +98,7 @@ ext.libs = [ ], element : [ 'opusencoder' : "io.element.android:opusencoder:1.1.0", - 'wysiwyg' : "io.element.android:wysiwyg:0.7.0.1" + 'wysiwyg' : "io.element.android:wysiwyg:0.8.0" ], squareup : [ 'moshi' : "com.squareup.moshi:moshi:$moshi", From 540758d66b0730d408a9737142ecaa01b7ccfd93 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 5 Dec 2022 10:43:56 +0100 Subject: [PATCH 079/108] Navigate to rename session screen from current session menu --- .../v2/VectorSettingsDevicesFragment.kt | 14 ++++++++ .../v2/VectorSettingsDevicesViewNavigator.kt | 5 +++ .../VectorSettingsDevicesViewNavigatorTest.kt | 35 +++++++++++++++---- .../OtherSessionsViewNavigatorTest.kt | 8 ++--- .../SessionOverviewViewNavigatorTest.kt | 8 ++--- .../im/vector/app/test/fakes/FakeContext.kt | 10 ++++-- 6 files changed, 61 insertions(+), 19 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt index 64744f6eeb..c29655a0c7 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt @@ -145,6 +145,10 @@ class VectorSettingsDevicesFragment : private fun initCurrentSessionHeaderView() { views.deviceListHeaderCurrentSession.setOnMenuItemClickListener { menuItem -> when (menuItem.itemId) { + R.id.currentSessionHeaderRename -> { + navigateToRenameCurrentSession() + true + } R.id.currentSessionHeaderSignoutOtherSessions -> { confirmMultiSignoutOtherSessions() true @@ -154,6 +158,16 @@ class VectorSettingsDevicesFragment : } } + private fun navigateToRenameCurrentSession() = withState(viewModel) { state -> + val currentDeviceId = state.currentSessionCrossSigningInfo.deviceId + if (currentDeviceId.isNotEmpty()) { + viewNavigator.navigateToRenameSession( + context = requireActivity(), + deviceId = currentDeviceId, + ) + } + } + private fun initCurrentSessionListView() { views.deviceListCurrentSession.viewVerifyButton.debouncedClicks { viewModel.handle(DevicesAction.VerifyCurrentSession) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesViewNavigator.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesViewNavigator.kt index 47e697822b..d4b3345fea 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesViewNavigator.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesViewNavigator.kt @@ -20,6 +20,7 @@ import android.content.Context import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType import im.vector.app.features.settings.devices.v2.othersessions.OtherSessionsActivity import im.vector.app.features.settings.devices.v2.overview.SessionOverviewActivity +import im.vector.app.features.settings.devices.v2.rename.RenameSessionActivity import javax.inject.Inject class VectorSettingsDevicesViewNavigator @Inject constructor() { @@ -38,4 +39,8 @@ class VectorSettingsDevicesViewNavigator @Inject constructor() { OtherSessionsActivity.newIntent(context, titleResourceId, defaultFilter, excludeCurrentDevice) ) } + + fun navigateToRenameSession(context: Context, deviceId: String) { + context.startActivity(RenameSessionActivity.newIntent(context, deviceId)) + } } diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesViewNavigatorTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesViewNavigatorTest.kt index ec8019384a..37823f7d53 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesViewNavigatorTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesViewNavigatorTest.kt @@ -20,6 +20,7 @@ import android.content.Intent import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType import im.vector.app.features.settings.devices.v2.othersessions.OtherSessionsActivity import im.vector.app.features.settings.devices.v2.overview.SessionOverviewActivity +import im.vector.app.features.settings.devices.v2.rename.RenameSessionActivity import im.vector.app.test.fakes.FakeContext import io.mockk.every import io.mockk.mockk @@ -43,6 +44,7 @@ class VectorSettingsDevicesViewNavigatorTest { fun setUp() { mockkObject(SessionOverviewActivity.Companion) mockkObject(OtherSessionsActivity.Companion) + mockkObject(RenameSessionActivity.Companion) } @After @@ -52,26 +54,41 @@ class VectorSettingsDevicesViewNavigatorTest { @Test fun `given a session id when navigating to overview then it starts the correct activity`() { + // Given val intent = givenIntentForSessionOverview(A_SESSION_ID) context.givenStartActivity(intent) + // When vectorSettingsDevicesViewNavigator.navigateToSessionOverview(context.instance, A_SESSION_ID) - verify { - context.instance.startActivity(intent) - } + // Then + context.verifyStartActivity(intent) } @Test fun `given an intent when navigating to other sessions list then it starts the correct activity`() { + // Given val intent = givenIntentForOtherSessions(A_TITLE_RESOURCE_ID, A_DEFAULT_FILTER, true) context.givenStartActivity(intent) + // When vectorSettingsDevicesViewNavigator.navigateToOtherSessions(context.instance, A_TITLE_RESOURCE_ID, A_DEFAULT_FILTER, true) - verify { - context.instance.startActivity(intent) - } + // Then + context.verifyStartActivity(intent) + } + + @Test + fun `given an intent when navigating to rename session screen then it starts the correct activity`() { + // Given + val intent = givenIntentForRenameSession(A_SESSION_ID) + context.givenStartActivity(intent) + + // When + vectorSettingsDevicesViewNavigator.navigateToRenameSession(context.instance, A_SESSION_ID) + + // Then + context.verifyStartActivity(intent) } private fun givenIntentForSessionOverview(sessionId: String): Intent { @@ -85,4 +102,10 @@ class VectorSettingsDevicesViewNavigatorTest { every { OtherSessionsActivity.newIntent(context.instance, titleResourceId, defaultFilter, excludeCurrentDevice) } returns intent return intent } + + private fun givenIntentForRenameSession(sessionId: String): Intent { + val intent = mockk() + every { RenameSessionActivity.newIntent(context.instance, sessionId) } returns intent + return intent + } } diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewNavigatorTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewNavigatorTest.kt index 3123572521..23fc1cbdce 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewNavigatorTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewNavigatorTest.kt @@ -23,7 +23,6 @@ import io.mockk.every import io.mockk.mockk import io.mockk.mockkObject import io.mockk.unmockkAll -import io.mockk.verify import org.junit.After import org.junit.Before import org.junit.Test @@ -47,14 +46,15 @@ class OtherSessionsViewNavigatorTest { @Test fun `given a device id when navigating to overview then it starts the correct activity`() { + // Given val intent = givenIntentForDeviceOverview(A_DEVICE_ID) context.givenStartActivity(intent) + // When otherSessionsViewNavigator.navigateToSessionOverview(context.instance, A_DEVICE_ID) - verify { - context.instance.startActivity(intent) - } + // Then + context.verifyStartActivity(intent) } private fun givenIntentForDeviceOverview(deviceId: String): Intent { diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewNavigatorTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewNavigatorTest.kt index e309c05042..99f3013697 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewNavigatorTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewNavigatorTest.kt @@ -60,9 +60,7 @@ class SessionOverviewViewNavigatorTest { sessionOverviewViewNavigator.goToSessionDetails(context.instance, A_SESSION_ID) // Then - verify { - context.instance.startActivity(intent) - } + context.verifyStartActivity(intent) } @Test @@ -75,9 +73,7 @@ class SessionOverviewViewNavigatorTest { sessionOverviewViewNavigator.goToRenameSession(context.instance, A_SESSION_ID) // Then - verify { - context.instance.startActivity(intent) - } + context.verifyStartActivity(intent) } @Test diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeContext.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeContext.kt index 9a94313fec..22e191e29c 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeContext.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeContext.kt @@ -24,9 +24,9 @@ import android.net.ConnectivityManager import android.net.Uri import android.os.ParcelFileDescriptor import io.mockk.every -import io.mockk.just +import io.mockk.justRun import io.mockk.mockk -import io.mockk.runs +import io.mockk.verify import java.io.OutputStream class FakeContext( @@ -73,7 +73,11 @@ class FakeContext( } fun givenStartActivity(intent: Intent) { - every { instance.startActivity(intent) } just runs + justRun { instance.startActivity(intent) } + } + + fun verifyStartActivity(intent: Intent) { + verify { instance.startActivity(intent) } } fun givenClipboardManager(): FakeClipboardManager { From 57554c5d3611642023669279d65d4c8c909b2b51 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 5 Dec 2022 14:10:56 +0100 Subject: [PATCH 080/108] Handling signout current session action --- .../settings/devices/v2/VectorSettingsDevicesFragment.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt index c29655a0c7..a21c7accb7 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt @@ -52,6 +52,7 @@ import im.vector.app.features.settings.devices.v2.list.SecurityRecommendationVie import im.vector.app.features.settings.devices.v2.list.SecurityRecommendationViewState import im.vector.app.features.settings.devices.v2.list.SessionInfoViewState import im.vector.app.features.settings.devices.v2.signout.BuildConfirmSignoutDialogUseCase +import im.vector.app.features.workers.signout.SignOutUiWorker import org.matrix.android.sdk.api.auth.data.LoginFlowTypes import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel @@ -149,6 +150,10 @@ class VectorSettingsDevicesFragment : navigateToRenameCurrentSession() true } + R.id.currentSessionHeaderSignout -> { + confirmSignoutCurrentSession() + true + } R.id.currentSessionHeaderSignoutOtherSessions -> { confirmMultiSignoutOtherSessions() true @@ -168,6 +173,10 @@ class VectorSettingsDevicesFragment : } } + private fun confirmSignoutCurrentSession() { + activity?.let { SignOutUiWorker(it).perform() } + } + private fun initCurrentSessionListView() { views.deviceListCurrentSession.viewVerifyButton.debouncedClicks { viewModel.handle(DevicesAction.VerifyCurrentSession) From a00508e08588568c8a85d14aa0f591ca31393b60 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 5 Dec 2022 14:12:00 +0100 Subject: [PATCH 081/108] Removing unused import --- .../devices/v2/VectorSettingsDevicesViewNavigatorTest.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesViewNavigatorTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesViewNavigatorTest.kt index 37823f7d53..24582c75d8 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesViewNavigatorTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesViewNavigatorTest.kt @@ -26,7 +26,6 @@ import io.mockk.every import io.mockk.mockk import io.mockk.mockkObject import io.mockk.unmockkAll -import io.mockk.verify import org.junit.After import org.junit.Before import org.junit.Test From 516103e51b2aa132f577166e2a004acaed57bbb1 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Mon, 5 Dec 2022 18:10:22 +0300 Subject: [PATCH 082/108] Fix usage of unknown shield in room summary. --- .../app/core/ui/views/ShieldImageView.kt | 25 ++++++++++++++++--- .../features/settings/devices/DeviceItem.kt | 4 +-- .../devices/v2/list/OtherSessionItem.kt | 2 +- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/ui/views/ShieldImageView.kt b/vector/src/main/java/im/vector/app/core/ui/views/ShieldImageView.kt index 6327daec86..0570bbe4d7 100644 --- a/vector/src/main/java/im/vector/app/core/ui/views/ShieldImageView.kt +++ b/vector/src/main/java/im/vector/app/core/ui/views/ShieldImageView.kt @@ -38,6 +38,25 @@ class ShieldImageView @JvmOverloads constructor( } } + /** + * Renders device shield with the support of unknown shields instead of black shields which is used for rooms. + * @param roomEncryptionTrustLevel trust level that is usally calculated with [im.vector.app.features.settings.devices.TrustUtils.shieldForTrust] + * @param borderLess if true then the shield icon with border around is used + */ + fun renderDeviceShield(roomEncryptionTrustLevel: RoomEncryptionTrustLevel?, borderLess: Boolean = false) { + isVisible = roomEncryptionTrustLevel != null + + if (roomEncryptionTrustLevel == RoomEncryptionTrustLevel.Default) { + contentDescription = context.getString(R.string.a11y_trust_level_default) + setImageResource( + if (borderLess) R.drawable.ic_shield_unknown_no_border + else R.drawable.ic_shield_unknown + ) + } else { + render(roomEncryptionTrustLevel, borderLess) + } + } + fun render(roomEncryptionTrustLevel: RoomEncryptionTrustLevel?, borderLess: Boolean = false) { isVisible = roomEncryptionTrustLevel != null @@ -45,8 +64,8 @@ class ShieldImageView @JvmOverloads constructor( RoomEncryptionTrustLevel.Default -> { contentDescription = context.getString(R.string.a11y_trust_level_default) setImageResource( - if (borderLess) R.drawable.ic_shield_unknown_no_border - else R.drawable.ic_shield_unknown + if (borderLess) R.drawable.ic_shield_black_no_border + else R.drawable.ic_shield_black ) } RoomEncryptionTrustLevel.Warning -> { @@ -137,7 +156,7 @@ class ShieldImageView @JvmOverloads constructor( @DrawableRes fun RoomEncryptionTrustLevel.toDrawableRes(): Int { return when (this) { - RoomEncryptionTrustLevel.Default -> R.drawable.ic_shield_unknown + RoomEncryptionTrustLevel.Default -> R.drawable.ic_shield_black RoomEncryptionTrustLevel.Warning -> R.drawable.ic_shield_warning RoomEncryptionTrustLevel.Trusted -> R.drawable.ic_shield_trusted RoomEncryptionTrustLevel.E2EWithUnsupportedAlgorithm -> R.drawable.ic_warning_badge diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceItem.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceItem.kt index 6486b8a3ca..5924742c26 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceItem.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceItem.kt @@ -85,9 +85,9 @@ abstract class DeviceItem : VectorEpoxyModel(R.layout.item_de trusted ) - holder.trustIcon.render(shield) + holder.trustIcon.renderDeviceShield(shield) } else { - holder.trustIcon.render(null) + holder.trustIcon.renderDeviceShield(null) } val detailedModeLabels = listOf( diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionItem.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionItem.kt index 9d9cb15c28..68cae344cd 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionItem.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionItem.kt @@ -97,7 +97,7 @@ abstract class OtherSessionItem : VectorEpoxyModel(R.la } else { setDeviceTypeIconUseCase.execute(deviceType, holder.otherSessionDeviceTypeImageView, stringProvider) } - holder.otherSessionVerificationStatusImageView.render(roomEncryptionTrustLevel) + holder.otherSessionVerificationStatusImageView.renderDeviceShield(roomEncryptionTrustLevel) holder.otherSessionNameTextView.text = sessionName holder.otherSessionDescriptionTextView.text = sessionDescription sessionDescriptionColor?.let { From 32ded289fcf91b46c45df3c6cbfa92dbddb71d29 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Mon, 5 Dec 2022 18:18:09 +0300 Subject: [PATCH 083/108] Add changelog. --- changelog.d/7710.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7710.bugfix diff --git a/changelog.d/7710.bugfix b/changelog.d/7710.bugfix new file mode 100644 index 0000000000..9e75a03e1b --- /dev/null +++ b/changelog.d/7710.bugfix @@ -0,0 +1 @@ +Fix usage of unknown shield in room summary From febf01a2e634c51fc4cdf1c1ff64d26230731369 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Dec 2022 16:36:16 +0100 Subject: [PATCH 084/108] Use the API `startForeground(int id, @NonNull Notification notification, @ForegroundServiceType int foregroundServiceType)` when available. Add missing android:foregroundServiceType in the manifest --- vector/src/main/AndroidManifest.xml | 6 ++- .../im/vector/app/core/extensions/Service.kt | 38 +++++++++++++++++++ .../app/core/services/CallAndroidService.kt | 13 ++++--- .../core/services/VectorSyncAndroidService.kt | 3 +- .../webrtc/ScreenCaptureAndroidService.kt | 3 +- .../tracking/LocationSharingAndroidService.kt | 3 +- .../features/start/StartAppAndroidService.kt | 3 +- 7 files changed, 58 insertions(+), 11 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/core/extensions/Service.kt diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml index a26be23456..9c8186b2d4 100644 --- a/vector/src/main/AndroidManifest.xml +++ b/vector/src/main/AndroidManifest.xml @@ -72,7 +72,9 @@ - + @@ -341,6 +344,7 @@ Int)? = null +) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + startForeground( + id, + notification, + provideForegroundServiceType?.invoke() ?: ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST + ) + } else { + startForeground(id, notification) + } +} diff --git a/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt b/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt index 85ea7f1a1b..a4e3872e0f 100644 --- a/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt +++ b/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt @@ -28,6 +28,7 @@ import androidx.media.session.MediaButtonReceiver import com.airbnb.mvrx.Mavericks import dagger.hilt.android.AndroidEntryPoint import im.vector.app.core.extensions.singletonEntryPoint +import im.vector.app.core.extensions.startForegroundCompat import im.vector.app.features.call.CallArgs import im.vector.app.features.call.VectorCallActivity import im.vector.app.features.call.telecom.CallConnection @@ -181,7 +182,7 @@ class CallAndroidService : VectorAndroidService() { fromBg = fromBg ) if (knownCalls.isEmpty()) { - startForeground(callId.hashCode(), notification) + startForegroundCompat(callId.hashCode(), notification) } else { notificationManager.notify(callId.hashCode(), notification) } @@ -201,7 +202,7 @@ class CallAndroidService : VectorAndroidService() { } val notification = notificationUtils.buildCallEndedNotification(false) val notificationId = callId.hashCode() - startForeground(notificationId, notification) + startForegroundCompat(notificationId, notification) if (knownCalls.isEmpty()) { Timber.tag(loggerTag.value).v("No more call, stop the service") stopForegroundCompat() @@ -236,7 +237,7 @@ class CallAndroidService : VectorAndroidService() { title = callInformation.opponentMatrixItem?.getBestName() ?: callInformation.opponentUserId ) if (knownCalls.isEmpty()) { - startForeground(callId.hashCode(), notification) + startForegroundCompat(callId.hashCode(), notification) } else { notificationManager.notify(callId.hashCode(), notification) } @@ -260,7 +261,7 @@ class CallAndroidService : VectorAndroidService() { title = callInformation.opponentMatrixItem?.getBestName() ?: callInformation.opponentUserId ) if (knownCalls.isEmpty()) { - startForeground(callId.hashCode(), notification) + startForegroundCompat(callId.hashCode(), notification) } else { notificationManager.notify(callId.hashCode(), notification) } @@ -273,9 +274,9 @@ class CallAndroidService : VectorAndroidService() { callRingPlayerOutgoing?.stop() val notification = notificationUtils.buildCallEndedNotification(false) if (callId != null) { - startForeground(callId.hashCode(), notification) + startForegroundCompat(callId.hashCode(), notification) } else { - startForeground(DEFAULT_NOTIFICATION_ID, notification) + startForegroundCompat(DEFAULT_NOTIFICATION_ID, notification) } if (knownCalls.isEmpty()) { mediaSession?.isActive = false diff --git a/vector/src/main/java/im/vector/app/core/services/VectorSyncAndroidService.kt b/vector/src/main/java/im/vector/app/core/services/VectorSyncAndroidService.kt index 864f69a136..f746c0749b 100644 --- a/vector/src/main/java/im/vector/app/core/services/VectorSyncAndroidService.kt +++ b/vector/src/main/java/im/vector/app/core/services/VectorSyncAndroidService.kt @@ -32,6 +32,7 @@ import androidx.work.Worker import androidx.work.WorkerParameters import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R +import im.vector.app.core.extensions.startForegroundCompat import im.vector.app.core.platform.PendingIntentCompat import im.vector.app.core.time.Clock import im.vector.app.core.time.DefaultClock @@ -98,7 +99,7 @@ class VectorSyncAndroidService : SyncAndroidService() { R.string.notification_listening_for_notifications } val notification = notificationUtils.buildForegroundServiceNotification(notificationSubtitleRes, false) - startForeground(NotificationUtils.NOTIFICATION_ID_FOREGROUND_SERVICE, notification) + startForegroundCompat(NotificationUtils.NOTIFICATION_ID_FOREGROUND_SERVICE, notification) } override fun onRescheduleAsked( diff --git a/vector/src/main/java/im/vector/app/features/call/webrtc/ScreenCaptureAndroidService.kt b/vector/src/main/java/im/vector/app/features/call/webrtc/ScreenCaptureAndroidService.kt index e7cebfb9c9..00b6bc40d2 100644 --- a/vector/src/main/java/im/vector/app/features/call/webrtc/ScreenCaptureAndroidService.kt +++ b/vector/src/main/java/im/vector/app/features/call/webrtc/ScreenCaptureAndroidService.kt @@ -20,6 +20,7 @@ import android.content.Intent import android.os.Binder import android.os.IBinder import dagger.hilt.android.AndroidEntryPoint +import im.vector.app.core.extensions.startForegroundCompat import im.vector.app.core.services.VectorAndroidService import im.vector.app.core.time.Clock import im.vector.app.features.notifications.NotificationUtils @@ -41,7 +42,7 @@ class ScreenCaptureAndroidService : VectorAndroidService() { private fun showStickyNotification() { val notificationId = clock.epochMillis().toInt() val notification = notificationUtils.buildScreenSharingNotification() - startForeground(notificationId, notification) + startForegroundCompat(notificationId, notification) } override fun onBind(intent: Intent?): IBinder { diff --git a/vector/src/main/java/im/vector/app/features/location/live/tracking/LocationSharingAndroidService.kt b/vector/src/main/java/im/vector/app/features/location/live/tracking/LocationSharingAndroidService.kt index ccab23a83b..d77a87f756 100644 --- a/vector/src/main/java/im/vector/app/features/location/live/tracking/LocationSharingAndroidService.kt +++ b/vector/src/main/java/im/vector/app/features/location/live/tracking/LocationSharingAndroidService.kt @@ -22,6 +22,7 @@ import android.os.Parcelable import androidx.core.app.NotificationManagerCompat import dagger.hilt.android.AndroidEntryPoint import im.vector.app.core.di.ActiveSessionHolder +import im.vector.app.core.extensions.startForegroundCompat import im.vector.app.core.services.VectorAndroidService import im.vector.app.features.location.LocationData import im.vector.app.features.location.LocationTracker @@ -105,7 +106,7 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca if (foregroundModeStarted) { NotificationManagerCompat.from(this).notify(FOREGROUND_SERVICE_NOTIFICATION_ID, notification) } else { - startForeground(FOREGROUND_SERVICE_NOTIFICATION_ID, notification) + startForegroundCompat(FOREGROUND_SERVICE_NOTIFICATION_ID, notification) foregroundModeStarted = true } diff --git a/vector/src/main/java/im/vector/app/features/start/StartAppAndroidService.kt b/vector/src/main/java/im/vector/app/features/start/StartAppAndroidService.kt index e8e0eac863..a14967a931 100644 --- a/vector/src/main/java/im/vector/app/features/start/StartAppAndroidService.kt +++ b/vector/src/main/java/im/vector/app/features/start/StartAppAndroidService.kt @@ -20,6 +20,7 @@ import android.content.Intent import dagger.hilt.android.AndroidEntryPoint import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.NamedGlobalScope +import im.vector.app.core.extensions.startForegroundCompat import im.vector.app.core.services.VectorAndroidService import im.vector.app.features.notifications.NotificationUtils import kotlinx.coroutines.CoroutineScope @@ -58,6 +59,6 @@ class StartAppAndroidService : VectorAndroidService() { private fun showStickyNotification() { val notificationId = Random.nextInt() val notification = notificationUtils.buildStartAppNotification() - startForeground(notificationId, notification) + startForegroundCompat(notificationId, notification) } } From 7b830d1c1a0679977ba5b2f2155f71aa86a4b04e Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 5 Dec 2022 17:40:38 +0100 Subject: [PATCH 085/108] Renaming a use case --- ...anToggleNotificationsViaAccountDataUseCase.kt | 4 ++-- ...ficationSettingsAccountDataUpdatesUseCase.kt} | 2 +- ...ggleNotificationsViaAccountDataUseCaseTest.kt | 16 ++++++++-------- ...tionSettingsAccountDataUpdatesUseCaseTest.kt} | 10 +++++----- 4 files changed, 16 insertions(+), 16 deletions(-) rename vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/{GetNotificationSettingsAccountDataAsFlowUseCase.kt => GetNotificationSettingsAccountDataUpdatesUseCase.kt} (94%) rename vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/{GetNotificationSettingsAccountDataAsFlowUseCaseTest.kt => GetNotificationSettingsAccountDataUpdatesUseCaseTest.kt} (87%) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CanToggleNotificationsViaAccountDataUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CanToggleNotificationsViaAccountDataUseCase.kt index ac466852eb..18ee9ad937 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CanToggleNotificationsViaAccountDataUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/CanToggleNotificationsViaAccountDataUseCase.kt @@ -22,11 +22,11 @@ import org.matrix.android.sdk.api.session.Session import javax.inject.Inject class CanToggleNotificationsViaAccountDataUseCase @Inject constructor( - private val getNotificationSettingsAccountDataAsFlowUseCase: GetNotificationSettingsAccountDataAsFlowUseCase, + private val getNotificationSettingsAccountDataUpdatesUseCase: GetNotificationSettingsAccountDataUpdatesUseCase, ) { fun execute(session: Session, deviceId: String): Flow { - return getNotificationSettingsAccountDataAsFlowUseCase.execute(session, deviceId) + return getNotificationSettingsAccountDataUpdatesUseCase.execute(session, deviceId) .map { it?.isSilenced != null } } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataAsFlowUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataUpdatesUseCase.kt similarity index 94% rename from vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataAsFlowUseCase.kt rename to vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataUpdatesUseCase.kt index ea4bd40f1f..308aeec5f2 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataAsFlowUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataUpdatesUseCase.kt @@ -25,7 +25,7 @@ import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes import org.matrix.android.sdk.api.session.events.model.toModel import javax.inject.Inject -class GetNotificationSettingsAccountDataAsFlowUseCase @Inject constructor() { +class GetNotificationSettingsAccountDataUpdatesUseCase @Inject constructor() { fun execute(session: Session, deviceId: String): Flow { return session diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CanToggleNotificationsViaAccountDataUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CanToggleNotificationsViaAccountDataUseCaseTest.kt index a1dfed6902..b85acb1e69 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CanToggleNotificationsViaAccountDataUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/CanToggleNotificationsViaAccountDataUseCaseTest.kt @@ -29,10 +29,10 @@ import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent class CanToggleNotificationsViaAccountDataUseCaseTest { - private val fakeGetNotificationSettingsAccountDataAsFlowUseCase = mockk() + private val fakeGetNotificationSettingsAccountDataUpdatesUseCase = mockk() private val canToggleNotificationsViaAccountDataUseCase = CanToggleNotificationsViaAccountDataUseCase( - getNotificationSettingsAccountDataAsFlowUseCase = fakeGetNotificationSettingsAccountDataAsFlowUseCase, + getNotificationSettingsAccountDataUpdatesUseCase = fakeGetNotificationSettingsAccountDataUpdatesUseCase, ) @Test @@ -43,14 +43,14 @@ class CanToggleNotificationsViaAccountDataUseCaseTest { val localNotificationSettingsContent = LocalNotificationSettingsContent( isSilenced = true, ) - every { fakeGetNotificationSettingsAccountDataAsFlowUseCase.execute(any(), any()) } returns flowOf(localNotificationSettingsContent) + every { fakeGetNotificationSettingsAccountDataUpdatesUseCase.execute(any(), any()) } returns flowOf(localNotificationSettingsContent) // When val result = canToggleNotificationsViaAccountDataUseCase.execute(aSession, aDeviceId).firstOrNull() // Then result shouldBe true - verify { fakeGetNotificationSettingsAccountDataAsFlowUseCase.execute(aSession, aDeviceId) } + verify { fakeGetNotificationSettingsAccountDataUpdatesUseCase.execute(aSession, aDeviceId) } } @Test @@ -61,14 +61,14 @@ class CanToggleNotificationsViaAccountDataUseCaseTest { val localNotificationSettingsContent = LocalNotificationSettingsContent( isSilenced = null, ) - every { fakeGetNotificationSettingsAccountDataAsFlowUseCase.execute(any(), any()) } returns flowOf(localNotificationSettingsContent) + every { fakeGetNotificationSettingsAccountDataUpdatesUseCase.execute(any(), any()) } returns flowOf(localNotificationSettingsContent) // When val result = canToggleNotificationsViaAccountDataUseCase.execute(aSession, aDeviceId).firstOrNull() // Then result shouldBe false - verify { fakeGetNotificationSettingsAccountDataAsFlowUseCase.execute(aSession, aDeviceId) } + verify { fakeGetNotificationSettingsAccountDataUpdatesUseCase.execute(aSession, aDeviceId) } } @Test @@ -76,13 +76,13 @@ class CanToggleNotificationsViaAccountDataUseCaseTest { // Given val aSession = FakeSession() val aDeviceId = "aDeviceId" - every { fakeGetNotificationSettingsAccountDataAsFlowUseCase.execute(any(), any()) } returns flowOf(null) + every { fakeGetNotificationSettingsAccountDataUpdatesUseCase.execute(any(), any()) } returns flowOf(null) // When val result = canToggleNotificationsViaAccountDataUseCase.execute(aSession, aDeviceId).firstOrNull() // Then result shouldBe false - verify { fakeGetNotificationSettingsAccountDataAsFlowUseCase.execute(aSession, aDeviceId) } + verify { fakeGetNotificationSettingsAccountDataUpdatesUseCase.execute(aSession, aDeviceId) } } } diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataAsFlowUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataUpdatesUseCaseTest.kt similarity index 87% rename from vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataAsFlowUseCaseTest.kt rename to vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataUpdatesUseCaseTest.kt index 6280d4c48b..50940b9d34 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataAsFlowUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/notification/GetNotificationSettingsAccountDataUpdatesUseCaseTest.kt @@ -30,10 +30,10 @@ import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes import org.matrix.android.sdk.api.session.events.model.toContent -class GetNotificationSettingsAccountDataAsFlowUseCaseTest { +class GetNotificationSettingsAccountDataUpdatesUseCaseTest { private val fakeFlowLiveDataConversions = FakeFlowLiveDataConversions() - private val getNotificationSettingsAccountDataAsFlowUseCase = GetNotificationSettingsAccountDataAsFlowUseCase() + private val getNotificationSettingsAccountDataUpdatesUseCase = GetNotificationSettingsAccountDataUpdatesUseCase() @Before fun setUp() { @@ -60,7 +60,7 @@ class GetNotificationSettingsAccountDataAsFlowUseCaseTest { .givenAsFlow() // When - val result = getNotificationSettingsAccountDataAsFlowUseCase.execute(aSession, aDeviceId).firstOrNull() + val result = getNotificationSettingsAccountDataUpdatesUseCase.execute(aSession, aDeviceId).firstOrNull() // Then result shouldBeEqualTo expectedContent @@ -80,7 +80,7 @@ class GetNotificationSettingsAccountDataAsFlowUseCaseTest { .givenAsFlow() // When - val result = getNotificationSettingsAccountDataAsFlowUseCase.execute(aSession, aDeviceId).firstOrNull() + val result = getNotificationSettingsAccountDataUpdatesUseCase.execute(aSession, aDeviceId).firstOrNull() // Then result shouldBeEqualTo null @@ -101,7 +101,7 @@ class GetNotificationSettingsAccountDataAsFlowUseCaseTest { .givenAsFlow() // When - val result = getNotificationSettingsAccountDataAsFlowUseCase.execute(aSession, aDeviceId).firstOrNull() + val result = getNotificationSettingsAccountDataUpdatesUseCase.execute(aSession, aDeviceId).firstOrNull() // Then result shouldBeEqualTo expectedContent From f2952f2deee61693de49e6b703b913ba0d5d8d6d Mon Sep 17 00:00:00 2001 From: valere Date: Mon, 5 Dec 2022 18:15:30 +0100 Subject: [PATCH 086/108] add to device tracing id --- changelog.d/7708.misc | 1 + .../internal/crypto/tasks/SendToDeviceTask.kt | 44 ++++- .../session/sync/handler/CryptoSyncHandler.kt | 5 +- .../crypto/DefaultSendToDeviceTaskTest.kt | 161 ++++++++++++++++++ 4 files changed, 206 insertions(+), 5 deletions(-) create mode 100644 changelog.d/7708.misc create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/DefaultSendToDeviceTaskTest.kt diff --git a/changelog.d/7708.misc b/changelog.d/7708.misc new file mode 100644 index 0000000000..6273330395 --- /dev/null +++ b/changelog.d/7708.misc @@ -0,0 +1 @@ +Add tracing Id for to device messages diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendToDeviceTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendToDeviceTask.kt index fc4d422360..1e6ceeb138 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendToDeviceTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendToDeviceTask.kt @@ -17,14 +17,21 @@ package org.matrix.android.sdk.internal.crypto.tasks import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap +import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.internal.crypto.api.CryptoApi import org.matrix.android.sdk.internal.crypto.model.rest.SendToDeviceBody import org.matrix.android.sdk.internal.network.GlobalErrorReceiver import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.task.Task +import timber.log.Timber import java.util.UUID import javax.inject.Inject +const val TO_DEVICE_TRACING_ID_KEY = "org.matrix.msgid" + +fun Event.toDeviceTracingId(): String? = content?.get(TO_DEVICE_TRACING_ID_KEY) as? String + internal interface SendToDeviceTask : Task { data class Params( // the type of event to send @@ -42,15 +49,17 @@ internal class DefaultSendToDeviceTask @Inject constructor( ) : SendToDeviceTask { override suspend fun execute(params: SendToDeviceTask.Params) { - val sendToDeviceBody = SendToDeviceBody( - messages = params.contentMap.map - ) - // If params.transactionId is not provided, we create a unique txnId. // It's important to do that outside the requestBlock parameter of executeRequest() // to use the same value if the request is retried val txnId = params.transactionId ?: createUniqueTxnId() + // add id tracing to debug + val decorated = decorateWithToDeviceTracingIds(params) + val sendToDeviceBody = SendToDeviceBody( + messages = decorated.first + ) + return executeRequest( globalErrorReceiver, canRetry = true, @@ -61,8 +70,35 @@ internal class DefaultSendToDeviceTask @Inject constructor( transactionId = txnId, body = sendToDeviceBody ) + Timber.i("Sent to device type=${params.eventType} txnid=$txnId [${decorated.second.joinToString(",")}]") } } + + /** + * To make it easier to track down where to-device messages are getting lost, + * add a custom property to each one, and that will be logged after sent and on reception. Synapse will also log + * this property. + * @return A pair, first is the decorated content, and second info to log out after sending + */ + private fun decorateWithToDeviceTracingIds(params: SendToDeviceTask.Params): Pair>, List> { + val tracingInfo = mutableListOf() + val decoratedContent = params.contentMap.map.map { userToDeviceMap -> + val userId = userToDeviceMap.key + userId to userToDeviceMap.value.map { + val deviceId = it.key + deviceId to it.value.toContent().toMutableMap().apply { + put( + TO_DEVICE_TRACING_ID_KEY, + UUID.randomUUID().toString().also { + tracingInfo.add("$userId/$deviceId (msgid $it)") + } + ) + } + }.toMap() + }.toMap() + + return decoratedContent to tracingInfo + } } internal fun createUniqueTxnId() = UUID.randomUUID().toString() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/CryptoSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/CryptoSyncHandler.kt index b2fe12ebc3..291e785aa5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/CryptoSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/CryptoSyncHandler.kt @@ -29,6 +29,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.session.sync.model.SyncResponse import org.matrix.android.sdk.api.session.sync.model.ToDeviceSyncResponse import org.matrix.android.sdk.internal.crypto.DefaultCryptoService +import org.matrix.android.sdk.internal.crypto.tasks.toDeviceTracingId import org.matrix.android.sdk.internal.crypto.verification.DefaultVerificationService import org.matrix.android.sdk.internal.session.sync.ProgressReporter import timber.log.Timber @@ -48,12 +49,14 @@ internal class CryptoSyncHandler @Inject constructor( ?.forEachIndexed { index, event -> progressReporter?.reportProgress(index * 100F / total) // Decrypt event if necessary - Timber.tag(loggerTag.value).i("To device event from ${event.senderId} of type:${event.type}") + Timber.tag(loggerTag.value).d("To device event tracingId:${event.toDeviceTracingId()}") decryptToDeviceEvent(event, null) + if (event.getClearType() == EventType.MESSAGE && event.getClearContent()?.toModel()?.msgType == "m.bad.encrypted") { Timber.tag(loggerTag.value).e("handleToDeviceEvent() : Warning: Unable to decrypt to-device event : ${event.content}") } else { + Timber.tag(loggerTag.value).d("received to-device ${event.getClearType()} from:${event.senderId} id:${event.toDeviceTracingId()}") verificationService.onToDeviceEvent(event) cryptoService.onToDeviceEvent(event) } diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/DefaultSendToDeviceTaskTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/DefaultSendToDeviceTaskTest.kt new file mode 100644 index 0000000000..72d166c1f4 --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/DefaultSendToDeviceTaskTest.kt @@ -0,0 +1,161 @@ +/* + * 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 org.matrix.android.sdk.internal.crypto + +import io.mockk.mockk +import kotlinx.coroutines.runBlocking +import org.amshove.kluent.internal.assertEquals +import org.junit.Assert +import org.junit.Test +import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo +import org.matrix.android.sdk.api.session.crypto.model.DevicesListResponse +import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap +import org.matrix.android.sdk.api.session.events.model.EventType +import org.matrix.android.sdk.internal.crypto.api.CryptoApi +import org.matrix.android.sdk.internal.crypto.model.rest.DeleteDeviceParams +import org.matrix.android.sdk.internal.crypto.model.rest.DeleteDevicesParams +import org.matrix.android.sdk.internal.crypto.model.rest.KeyChangesResponse +import org.matrix.android.sdk.internal.crypto.model.rest.KeysClaimBody +import org.matrix.android.sdk.internal.crypto.model.rest.KeysClaimResponse +import org.matrix.android.sdk.internal.crypto.model.rest.KeysQueryBody +import org.matrix.android.sdk.internal.crypto.model.rest.KeysQueryResponse +import org.matrix.android.sdk.internal.crypto.model.rest.KeysUploadBody +import org.matrix.android.sdk.internal.crypto.model.rest.KeysUploadResponse +import org.matrix.android.sdk.internal.crypto.model.rest.SendToDeviceBody +import org.matrix.android.sdk.internal.crypto.model.rest.SignatureUploadResponse +import org.matrix.android.sdk.internal.crypto.model.rest.UpdateDeviceInfoBody +import org.matrix.android.sdk.internal.crypto.model.rest.UploadSigningKeysBody +import org.matrix.android.sdk.internal.crypto.tasks.DefaultSendToDeviceTask +import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask +import org.matrix.android.sdk.internal.network.GlobalErrorReceiver + +class DefaultSendToDeviceTaskTest { + + val users = listOf( + "@alice:example.com" to listOf("D0", "D1"), + "bob@example.com" to listOf("D2", "D3") + ) + + val fakeEncryptedContent = mapOf( + "algorithm" to "m.olm.v1.curve25519-aes-sha2", + "sender_key" to "gMObR+/4dqL5T4DisRRRYBJpn+OjzFnkyCFOktP6Eyw", + "ciphertext" to mapOf( + "tdwXf7006FDgzmufMCVI4rDdVPO51ecRTTT6HkRxUwE" to mapOf( + "type" to 0, + "body" to "AwogCA1ULEc0abGIFxMDIC9iv7ul3jqJSnapTHQ+8JJx" + ) + ) + ) + + @Test + fun `tracing id should be added to all to_device contents`() { + val fakeCryptoAPi = FakeCryptoApi() + + val sendToDeviceTask = DefaultSendToDeviceTask( + cryptoApi = fakeCryptoAPi, + globalErrorReceiver = mockk(relaxed = true) + ) + + val contentMap = MXUsersDevicesMap() + + users.forEach { + val userId = it.first + it.second.forEach { + contentMap.setObject(userId, it, fakeEncryptedContent) + } + } + + val params = SendToDeviceTask.Params( + eventType = EventType.ENCRYPTED, + contentMap = contentMap + ) + + runBlocking { + sendToDeviceTask.execute(params) + } + + val generatedIds = mutableListOf() + users.forEach { + val userId = it.first + it.second.forEach { + val modifiedContent = fakeCryptoAPi.body!!.messages!![userId]!![it] as Map + Assert.assertNotNull("Tracing id should have been added", modifiedContent["org.matrix.msgid"]) + generatedIds.add(modifiedContent["org.matrix.msgid"] as String) + + assertEquals( + "The rest of the content should be the same", + fakeEncryptedContent.keys, + modifiedContent.toMutableMap().apply { remove("org.matrix.msgid") }.keys + ) + } + } + + assertEquals("Id should be unique per content", generatedIds.size, generatedIds.toSet().size) + println("modified content ${fakeCryptoAPi.body}") + } + + internal class FakeCryptoApi : CryptoApi { + override suspend fun getDevices(): DevicesListResponse { + throw java.lang.AssertionError("Should not be called") + } + + override suspend fun getDeviceInfo(deviceId: String): DeviceInfo { + throw java.lang.AssertionError("Should not be called") + } + + override suspend fun uploadKeys(body: KeysUploadBody): KeysUploadResponse { + throw java.lang.AssertionError("Should not be called") + } + + override suspend fun downloadKeysForUsers(params: KeysQueryBody): KeysQueryResponse { + throw java.lang.AssertionError("Should not be called") + } + + override suspend fun uploadSigningKeys(params: UploadSigningKeysBody): KeysQueryResponse { + throw java.lang.AssertionError("Should not be called") + } + + override suspend fun uploadSignatures(params: Map?): SignatureUploadResponse { + throw java.lang.AssertionError("Should not be called") + } + + override suspend fun claimOneTimeKeysForUsersDevices(body: KeysClaimBody): KeysClaimResponse { + throw java.lang.AssertionError("Should not be called") + } + + var body: SendToDeviceBody? = null + override suspend fun sendToDevice(eventType: String, transactionId: String, body: SendToDeviceBody) { + this.body = body + } + + override suspend fun deleteDevice(deviceId: String, params: DeleteDeviceParams) { + throw java.lang.AssertionError("Should not be called") + } + + override suspend fun deleteDevices(params: DeleteDevicesParams) { + throw java.lang.AssertionError("Should not be called") + } + + override suspend fun updateDeviceInfo(deviceId: String, params: UpdateDeviceInfoBody) { + throw java.lang.AssertionError("Should not be called") + } + + override suspend fun getKeyChanges(oldToken: String, newToken: String): KeyChangesResponse { + throw java.lang.AssertionError("Should not be called") + } + } +} From 2ed212aa11a36aed1df67722df77f8736f96ad4f Mon Sep 17 00:00:00 2001 From: valere Date: Mon, 5 Dec 2022 18:30:38 +0100 Subject: [PATCH 087/108] Fix copyright --- .../android/sdk/internal/crypto/DefaultSendToDeviceTaskTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/DefaultSendToDeviceTaskTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/DefaultSendToDeviceTaskTest.kt index 72d166c1f4..c813991d3f 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/DefaultSendToDeviceTaskTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/DefaultSendToDeviceTaskTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright 2022 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 139eb1708c3b5e7f9fb15d87c6fb2dd3346f8639 Mon Sep 17 00:00:00 2001 From: valere Date: Tue, 6 Dec 2022 08:17:31 +0100 Subject: [PATCH 088/108] fix uncheck cast warning --- .../crypto/DefaultSendToDeviceTaskTest.kt | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/DefaultSendToDeviceTaskTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/DefaultSendToDeviceTaskTest.kt index c813991d3f..b8e870bd06 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/DefaultSendToDeviceTaskTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/DefaultSendToDeviceTaskTest.kt @@ -41,16 +41,15 @@ import org.matrix.android.sdk.internal.crypto.model.rest.UpdateDeviceInfoBody import org.matrix.android.sdk.internal.crypto.model.rest.UploadSigningKeysBody import org.matrix.android.sdk.internal.crypto.tasks.DefaultSendToDeviceTask import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask -import org.matrix.android.sdk.internal.network.GlobalErrorReceiver class DefaultSendToDeviceTaskTest { - val users = listOf( + private val users = listOf( "@alice:example.com" to listOf("D0", "D1"), "bob@example.com" to listOf("D2", "D3") ) - val fakeEncryptedContent = mapOf( + private val fakeEncryptedContent = mapOf( "algorithm" to "m.olm.v1.curve25519-aes-sha2", "sender_key" to "gMObR+/4dqL5T4DisRRRYBJpn+OjzFnkyCFOktP6Eyw", "ciphertext" to mapOf( @@ -67,14 +66,14 @@ class DefaultSendToDeviceTaskTest { val sendToDeviceTask = DefaultSendToDeviceTask( cryptoApi = fakeCryptoAPi, - globalErrorReceiver = mockk(relaxed = true) + globalErrorReceiver = mockk(relaxed = true) ) val contentMap = MXUsersDevicesMap() - users.forEach { - val userId = it.first - it.second.forEach { + users.forEach { pairOfUserDevices -> + val userId = pairOfUserDevices.first + pairOfUserDevices.second.forEach { contentMap.setObject(userId, it, fakeEncryptedContent) } } @@ -89,10 +88,10 @@ class DefaultSendToDeviceTaskTest { } val generatedIds = mutableListOf() - users.forEach { - val userId = it.first - it.second.forEach { - val modifiedContent = fakeCryptoAPi.body!!.messages!![userId]!![it] as Map + users.forEach { pairOfUserDevices -> + val userId = pairOfUserDevices.first + pairOfUserDevices.second.forEach { + val modifiedContent = fakeCryptoAPi.body!!.messages!![userId]!![it] as Map<*, *> Assert.assertNotNull("Tracing id should have been added", modifiedContent["org.matrix.msgid"]) generatedIds.add(modifiedContent["org.matrix.msgid"] as String) From 4cd4cf1c51930954e22398f21dcf0312e14e0c05 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Tue, 6 Dec 2022 14:06:14 +0300 Subject: [PATCH 089/108] Code review fix. --- .../app/features/settings/devices/v2/list/SessionInfoView.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt index c6044d04a4..7727cee4fa 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt @@ -90,7 +90,7 @@ class SessionInfoView @JvmOverloads constructor( hasLearnMoreLink: Boolean, isVerifyButtonVisible: Boolean, ) { - views.sessionInfoVerificationStatusImageView.render(encryptionTrustLevel) + views.sessionInfoVerificationStatusImageView.renderDeviceShield(encryptionTrustLevel) when { encryptionTrustLevel == RoomEncryptionTrustLevel.Trusted -> renderCrossSigningVerified(isCurrentSession) encryptionTrustLevel == RoomEncryptionTrustLevel.Default && !isCurrentSession -> renderCrossSigningUnknown() From a65e13970d9ca9a92feb913ec0e5ab71ddaac3d2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 6 Dec 2022 11:52:13 +0100 Subject: [PATCH 090/108] `appdistribution` is only for nightly builds, not necessary for gplay (prod) builds. --- vector-app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector-app/build.gradle b/vector-app/build.gradle index fa6aa5f0fd..6ebb08600b 100644 --- a/vector-app/build.gradle +++ b/vector-app/build.gradle @@ -374,7 +374,7 @@ dependencies { // API-only library gplayImplementation libs.google.appdistributionApi // Full SDK implementation - gplayImplementation libs.google.appdistribution + nightlyImplementation libs.google.appdistribution // OSS License, gplay flavor only gplayImplementation 'com.google.android.gms:play-services-oss-licenses:17.0.0' From 0d12dbbe7e8031aba0ca64e4da514c9e79636e01 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 6 Dec 2022 12:21:07 +0100 Subject: [PATCH 091/108] Disable the Nightly popup, user registration (with `updateIfNewReleaseAvailable()`) to get upgrade does not work. Add a nightly build section in the preferences to manually try to upgrade. --- .../src/main/res/values/strings.xml | 3 +++ .../app/nightly/FirebaseNightlyProxy.kt | 21 +++++++++++++++---- .../vector/app/features/home/HomeActivity.kt | 4 +++- .../vector/app/features/home/NightlyProxy.kt | 15 ++++++++++++- .../VectorSettingsAdvancedSettingsFragment.kt | 18 ++++++++++++++++ .../xml/vector_settings_advanced_settings.xml | 12 +++++++++++ 6 files changed, 67 insertions(+), 6 deletions(-) diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml index 609cdac233..7f11e63469 100644 --- a/library/ui-strings/src/main/res/values/strings.xml +++ b/library/ui-strings/src/main/res/values/strings.xml @@ -2487,6 +2487,9 @@ Key Requests Export Audit + Nightly build + Get the latest build (note: you may have trouble to sign in) + Unlock encrypted messages history Refresh diff --git a/vector-app/src/gplay/java/im/vector/app/nightly/FirebaseNightlyProxy.kt b/vector-app/src/gplay/java/im/vector/app/nightly/FirebaseNightlyProxy.kt index 94a36036b6..71ffda7c36 100644 --- a/vector-app/src/gplay/java/im/vector/app/nightly/FirebaseNightlyProxy.kt +++ b/vector-app/src/gplay/java/im/vector/app/nightly/FirebaseNightlyProxy.kt @@ -34,8 +34,11 @@ class FirebaseNightlyProxy @Inject constructor( private val buildMeta: BuildMeta, ) : NightlyProxy { - override fun onHomeResumed() { - if (!canDisplayPopup()) return + override fun isNightlyBuild(): Boolean { + return buildMeta.applicationId in nightlyPackages + } + + override fun updateApplication() { val firebaseAppDistribution = FirebaseAppDistribution.getInstance() firebaseAppDistribution.updateIfNewReleaseAvailable() .addOnProgressListener { up -> @@ -46,6 +49,7 @@ class FirebaseNightlyProxy @Inject constructor( when (e.errorCode) { FirebaseAppDistributionException.Status.NOT_IMPLEMENTED -> { // SDK did nothing. This is expected when building for Play. + Timber.d("FirebaseAppDistribution NOT_IMPLEMENTED error") } else -> { // Handle other errors. @@ -56,10 +60,14 @@ class FirebaseNightlyProxy @Inject constructor( Timber.e(e, "FirebaseAppDistribution - other error") } } + .addOnSuccessListener { + Timber.d("FirebaseAppDistribution Success!") + } } - private fun canDisplayPopup(): Boolean { - if (buildMeta.applicationId != "im.vector.app.nightly") return false + override fun canDisplayPopup(): Boolean { + if (!POPUP_IS_ENABLED) return false + if (!isNightlyBuild()) return false val today = clock.epochMillis() / A_DAY_IN_MILLIS val lastDisplayPopupDay = sharedPreferences.getLong(SHARED_PREF_KEY, 0) return (today > lastDisplayPopupDay) @@ -73,7 +81,12 @@ class FirebaseNightlyProxy @Inject constructor( } companion object { + private const val POPUP_IS_ENABLED = false private const val A_DAY_IN_MILLIS = 8_600_000L private const val SHARED_PREF_KEY = "LAST_NIGHTLY_POPUP_DAY" + + private val nightlyPackages = listOf( + "im.vector.app.nightly" + ) } } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt index 8c6daae95a..2a3d8d094c 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt @@ -580,7 +580,9 @@ class HomeActivity : serverBackupStatusViewModel.refreshRemoteStateIfNeeded() // Check nightly - nightlyProxy.onHomeResumed() + if (nightlyProxy.canDisplayPopup()) { + nightlyProxy.updateApplication() + } checkNewAppLayoutFlagChange() } diff --git a/vector/src/main/java/im/vector/app/features/home/NightlyProxy.kt b/vector/src/main/java/im/vector/app/features/home/NightlyProxy.kt index b25add2ac9..42b93bf1a5 100644 --- a/vector/src/main/java/im/vector/app/features/home/NightlyProxy.kt +++ b/vector/src/main/java/im/vector/app/features/home/NightlyProxy.kt @@ -17,5 +17,18 @@ package im.vector.app.features.home interface NightlyProxy { - fun onHomeResumed() + /** + * Return true if this is a nightly build (checking the package of the app), and only once a day. + */ + fun canDisplayPopup(): Boolean + + /** + * Return true if this is a nightly build (checking the package of the app). + */ + fun isNightlyBuild(): Boolean + + /** + * Try to update the application, if update is available. Will also take care of the user sign in. + */ + fun updateApplication() } diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsAdvancedSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsAdvancedSettingsFragment.kt index 9c08d446f4..b6fa997f41 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsAdvancedSettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsAdvancedSettingsFragment.kt @@ -22,10 +22,13 @@ import androidx.preference.SeekBarPreference import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R import im.vector.app.core.platform.VectorBaseActivity +import im.vector.app.core.preference.VectorPreference import im.vector.app.core.preference.VectorPreferenceCategory import im.vector.app.core.preference.VectorSwitchPreference import im.vector.app.features.analytics.plan.MobileScreen +import im.vector.app.features.home.NightlyProxy import im.vector.app.features.rageshake.RageShake +import javax.inject.Inject @AndroidEntryPoint class VectorSettingsAdvancedSettingsFragment : @@ -34,6 +37,8 @@ class VectorSettingsAdvancedSettingsFragment : override var titleRes = R.string.settings_advanced_settings override val preferenceXmlRes = R.xml.vector_settings_advanced_settings + @Inject lateinit var nightlyProxy: NightlyProxy + private var rageshake: RageShake? = null override fun onCreate(savedInstanceState: Bundle?) { @@ -57,6 +62,11 @@ class VectorSettingsAdvancedSettingsFragment : } override fun bindPref() { + setupRageShakeSection() + setupNightlySection() + } + + private fun setupRageShakeSection() { val isRageShakeAvailable = RageShake.isAvailable(requireContext()) if (isRageShakeAvailable) { @@ -86,4 +96,12 @@ class VectorSettingsAdvancedSettingsFragment : findPreference("SETTINGS_RAGE_SHAKE_CATEGORY_KEY")!!.isVisible = false } } + + private fun setupNightlySection() { + findPreference("SETTINGS_NIGHTLY_BUILD_PREFERENCE_KEY")?.isVisible = nightlyProxy.isNightlyBuild() + findPreference("SETTINGS_NIGHTLY_BUILD_UPDATE_PREFERENCE_KEY")?.setOnPreferenceClickListener { + nightlyProxy.updateApplication() + true + } + } } diff --git a/vector/src/main/res/xml/vector_settings_advanced_settings.xml b/vector/src/main/res/xml/vector_settings_advanced_settings.xml index 29d8051583..9260b33162 100644 --- a/vector/src/main/res/xml/vector_settings_advanced_settings.xml +++ b/vector/src/main/res/xml/vector_settings_advanced_settings.xml @@ -95,4 +95,16 @@ + + + + + + From ae93c075973b61d5a03b9842fa4bd1e0698bfb6f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 6 Dec 2022 15:01:47 +0100 Subject: [PATCH 092/108] Do not propagate failure if saving the filter server side fails. This will be retried later. --- .../session/filter/GetCurrentFilterTask.kt | 2 +- .../internal/session/filter/SaveFilterTask.kt | 18 +++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/GetCurrentFilterTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/GetCurrentFilterTask.kt index e88f286e27..76805c5c51 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/GetCurrentFilterTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/GetCurrentFilterTask.kt @@ -42,7 +42,7 @@ internal class DefaultGetCurrentFilterTask @Inject constructor( return when (storedFilterBody) { currentFilterBody -> storedFilterId ?: storedFilterBody - else -> saveFilter(currentFilter) + else -> saveFilter(currentFilter) ?: currentFilterBody } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/SaveFilterTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/SaveFilterTask.kt index 82d5ff4d2f..0223cd3ee7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/SaveFilterTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/SaveFilterTask.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.internal.session.filter +import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.network.GlobalErrorReceiver import org.matrix.android.sdk.internal.network.executeRequest @@ -24,8 +25,9 @@ import javax.inject.Inject /** * Save a filter, in db and if any changes, upload to the server. + * Return the filterId if uploading to the server is successful, else return null. */ -internal interface SaveFilterTask : Task { +internal interface SaveFilterTask : Task { data class Params( val filter: Filter @@ -39,18 +41,20 @@ internal class DefaultSaveFilterTask @Inject constructor( private val globalErrorReceiver: GlobalErrorReceiver, ) : SaveFilterTask { - override suspend fun execute(params: SaveFilterTask.Params): String { + override suspend fun execute(params: SaveFilterTask.Params): String? { val filter = params.filter - val filterResponse = executeRequest(globalErrorReceiver) { - // TODO auto retry - filterAPI.uploadFilter(userId, filter) + val filterResponse = tryOrNull { + executeRequest(globalErrorReceiver) { + filterAPI.uploadFilter(userId, filter) + } } + val filterId = filterResponse?.filterId filterRepository.storeSyncFilter( filter = filter, - filterId = filterResponse.filterId, + filterId = filterId.orEmpty(), roomEventFilter = FilterFactory.createDefaultRoomFilter() ) - return filterResponse.filterId + return filterId } } From 8646cc441d2c4c2d7e36c95f6dd3de71f5bd3dc3 Mon Sep 17 00:00:00 2001 From: valere Date: Tue, 6 Dec 2022 15:30:06 +0100 Subject: [PATCH 093/108] do not add tracing ids to verification events --- .../sdk/api/session/events/model/EventType.kt | 3 + .../internal/crypto/tasks/SendToDeviceTask.kt | 12 ++- .../crypto/DefaultSendToDeviceTaskTest.kt | 97 ++++++++++++++++++- 3 files changed, 109 insertions(+), 3 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt index e5c14afa90..013b452ced 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt @@ -16,6 +16,8 @@ package org.matrix.android.sdk.api.session.events.model +import org.matrix.android.sdk.api.session.room.model.message.MessageType.MSGTYPE_VERIFICATION_REQUEST + /** * Constants defining known event types from Matrix specifications. */ @@ -126,6 +128,7 @@ object EventType { fun isVerificationEvent(type: String): Boolean { return when (type) { + MSGTYPE_VERIFICATION_REQUEST, KEY_VERIFICATION_START, KEY_VERIFICATION_ACCEPT, KEY_VERIFICATION_KEY, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendToDeviceTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendToDeviceTask.kt index 1e6ceeb138..a7e93202ef 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendToDeviceTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendToDeviceTask.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.crypto.tasks import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.internal.crypto.api.CryptoApi import org.matrix.android.sdk.internal.crypto.model.rest.SendToDeviceBody @@ -39,7 +40,9 @@ internal interface SendToDeviceTask : Task { // the content to send. Map from user_id to device_id to content dictionary. val contentMap: MXUsersDevicesMap, // the transactionId. If not provided, a transactionId will be created by the task - val transactionId: String? = null + val transactionId: String? = null, + // add tracing id, notice that to device events that do signature on content might be broken by it + val addTracingIds: Boolean = !EventType.isVerificationEvent(eventType), ) } @@ -55,7 +58,12 @@ internal class DefaultSendToDeviceTask @Inject constructor( val txnId = params.transactionId ?: createUniqueTxnId() // add id tracing to debug - val decorated = decorateWithToDeviceTracingIds(params) + val decorated = if (params.addTracingIds) { + decorateWithToDeviceTracingIds(params) + } else { + params.contentMap.map to emptyList() + } + val sendToDeviceBody = SendToDeviceBody( messages = decorated.first ) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/DefaultSendToDeviceTaskTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/DefaultSendToDeviceTaskTest.kt index b8e870bd06..df6fc5f165 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/DefaultSendToDeviceTaskTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/DefaultSendToDeviceTaskTest.kt @@ -25,6 +25,7 @@ import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo import org.matrix.android.sdk.api.session.crypto.model.DevicesListResponse import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.api.session.events.model.EventType +import org.matrix.android.sdk.api.session.room.model.message.MessageType import org.matrix.android.sdk.internal.crypto.api.CryptoApi import org.matrix.android.sdk.internal.crypto.model.rest.DeleteDeviceParams import org.matrix.android.sdk.internal.crypto.model.rest.DeleteDevicesParams @@ -60,8 +61,28 @@ class DefaultSendToDeviceTaskTest { ) ) + private val fakeStartVerificationContent = mapOf( + "method" to "m.sas.v1", + "from_device" to "MNQHVEISFQ", + "key_agreement_protocols" to listOf( + "curve25519-hkdf-sha256", + "curve25519" + ), + "hashes" to listOf("sha256"), + "message_authentication_codes" to listOf( + "org.matrix.msc3783.hkdf-hmac-sha256", + "hkdf-hmac-sha256", + "hmac-sha256" + ), + "short_authentication_string" to listOf( + "decimal", + "emoji" + ), + "transaction_id" to "4wNOpkHGwGZPXjkZToooCDWfb8hsf7vW" + ) + @Test - fun `tracing id should be added to all to_device contents`() { + fun `tracing id should be added to to_device contents`() { val fakeCryptoAPi = FakeCryptoApi() val sendToDeviceTask = DefaultSendToDeviceTask( @@ -107,6 +128,80 @@ class DefaultSendToDeviceTaskTest { println("modified content ${fakeCryptoAPi.body}") } + @Test + fun `tracing id should not be added to verification start to_device contents`() { + val fakeCryptoAPi = FakeCryptoApi() + + val sendToDeviceTask = DefaultSendToDeviceTask( + cryptoApi = fakeCryptoAPi, + globalErrorReceiver = mockk(relaxed = true) + ) + val contentMap = MXUsersDevicesMap() + contentMap.setObject("@alice:example.com", "MNQHVEISFQ", fakeStartVerificationContent) + + val params = SendToDeviceTask.Params( + eventType = EventType.KEY_VERIFICATION_START, + contentMap = contentMap + ) + + runBlocking { + sendToDeviceTask.execute(params) + } + + val modifiedContent = fakeCryptoAPi.body!!.messages!!["@alice:example.com"]!!["MNQHVEISFQ"] as Map<*, *> + Assert.assertNull("Tracing id should not have been added", modifiedContent["org.matrix.msgid"]) + + // try to force + runBlocking { + sendToDeviceTask.execute( + SendToDeviceTask.Params( + eventType = EventType.KEY_VERIFICATION_START, + contentMap = contentMap, + addTracingIds = true + ) + ) + } + + val modifiedContentForced = fakeCryptoAPi.body!!.messages!!["@alice:example.com"]!!["MNQHVEISFQ"] as Map<*, *> + Assert.assertNotNull("Tracing id should have been added", modifiedContentForced["org.matrix.msgid"]) + } + + @Test + fun `tracing id should not be added to all verification to_device contents`() { + val fakeCryptoAPi = FakeCryptoApi() + + val sendToDeviceTask = DefaultSendToDeviceTask( + cryptoApi = fakeCryptoAPi, + globalErrorReceiver = mockk(relaxed = true) + ) + val contentMap = MXUsersDevicesMap() + contentMap.setObject("@alice:example.com", "MNQHVEISFQ", emptyMap()) + + val verificationEvents = listOf( + MessageType.MSGTYPE_VERIFICATION_REQUEST, + EventType.KEY_VERIFICATION_START, + EventType.KEY_VERIFICATION_ACCEPT, + EventType.KEY_VERIFICATION_KEY, + EventType.KEY_VERIFICATION_MAC, + EventType.KEY_VERIFICATION_CANCEL, + EventType.KEY_VERIFICATION_DONE, + EventType.KEY_VERIFICATION_READY + ) + + for (type in verificationEvents) { + val params = SendToDeviceTask.Params( + eventType = type, + contentMap = contentMap + ) + runBlocking { + sendToDeviceTask.execute(params) + } + + val modifiedContent = fakeCryptoAPi.body!!.messages!!["@alice:example.com"]!!["MNQHVEISFQ"] as Map<*, *> + Assert.assertNull("Tracing id should not have been added", modifiedContent["org.matrix.msgid"]) + } + } + internal class FakeCryptoApi : CryptoApi { override suspend fun getDevices(): DevicesListResponse { throw java.lang.AssertionError("Should not be called") From 63d2886415820d979c1e6592dd582123b1bb09ec Mon Sep 17 00:00:00 2001 From: valere Date: Tue, 6 Dec 2022 16:07:24 +0100 Subject: [PATCH 094/108] use msgid in logs for consistency --- .../sdk/internal/session/sync/handler/CryptoSyncHandler.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/CryptoSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/CryptoSyncHandler.kt index 291e785aa5..551db52dbd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/CryptoSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/CryptoSyncHandler.kt @@ -49,14 +49,14 @@ internal class CryptoSyncHandler @Inject constructor( ?.forEachIndexed { index, event -> progressReporter?.reportProgress(index * 100F / total) // Decrypt event if necessary - Timber.tag(loggerTag.value).d("To device event tracingId:${event.toDeviceTracingId()}") + Timber.tag(loggerTag.value).d("To device event msgid:${event.toDeviceTracingId()}") decryptToDeviceEvent(event, null) if (event.getClearType() == EventType.MESSAGE && event.getClearContent()?.toModel()?.msgType == "m.bad.encrypted") { Timber.tag(loggerTag.value).e("handleToDeviceEvent() : Warning: Unable to decrypt to-device event : ${event.content}") } else { - Timber.tag(loggerTag.value).d("received to-device ${event.getClearType()} from:${event.senderId} id:${event.toDeviceTracingId()}") + Timber.tag(loggerTag.value).d("received to-device ${event.getClearType()} from:${event.senderId} msgid:${event.toDeviceTracingId()}") verificationService.onToDeviceEvent(event) cryptoService.onToDeviceEvent(event) } From a6752a0cf18fa974dd7ec5d7ba90954f21f1f17c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Dec 2022 16:10:25 +0000 Subject: [PATCH 095/108] Bump com.google.devtools.ksp from 1.7.21-1.0.8 to 1.7.22-1.0.8 Bumps [com.google.devtools.ksp](https://github.com/google/ksp) from 1.7.21-1.0.8 to 1.7.22-1.0.8. - [Release notes](https://github.com/google/ksp/releases) - [Commits](https://github.com/google/ksp/compare/1.7.21-1.0.8...1.7.22-1.0.8) --- updated-dependencies: - dependency-name: com.google.devtools.ksp dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 58084ab64d..2abb2a9072 100644 --- a/build.gradle +++ b/build.gradle @@ -45,7 +45,7 @@ plugins { // Detekt id "io.gitlab.arturbosch.detekt" version "1.22.0" // Ksp - id "com.google.devtools.ksp" version "1.7.21-1.0.8" + id "com.google.devtools.ksp" version "1.7.22-1.0.8" // Dependency Analysis id 'com.autonomousapps.dependency-analysis' version "1.17.0" From 988afa4ebe5354849972c5a8faa2a4a4a331c846 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 6 Dec 2022 18:21:07 +0100 Subject: [PATCH 096/108] Fix FDroid build --- vector-app/src/fdroid/java/im/vector/app/di/FlavorModule.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vector-app/src/fdroid/java/im/vector/app/di/FlavorModule.kt b/vector-app/src/fdroid/java/im/vector/app/di/FlavorModule.kt index 63b4c2a3cd..e7d9598b65 100644 --- a/vector-app/src/fdroid/java/im/vector/app/di/FlavorModule.kt +++ b/vector-app/src/fdroid/java/im/vector/app/di/FlavorModule.kt @@ -46,9 +46,9 @@ abstract class FlavorModule { @Provides fun provideNightlyProxy() = object : NightlyProxy { - override fun onHomeResumed() { - // no op - } + override fun canDisplayPopup() = false + override fun isNightlyBuild() = false + override fun updateApplication() = Unit } @Provides From 9bbecbeed33eba37af120ea004cd7c49fd539f21 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Dec 2022 23:02:30 +0000 Subject: [PATCH 097/108] Bump wysiwyg from 0.8.0 to 0.9.0 Bumps [wysiwyg](https://github.com/matrix-org/matrix-wysiwyg) from 0.8.0 to 0.9.0. - [Release notes](https://github.com/matrix-org/matrix-wysiwyg/releases) - [Changelog](https://github.com/matrix-org/matrix-rich-text-editor/blob/main/CHANGELOG.md) - [Commits](https://github.com/matrix-org/matrix-wysiwyg/compare/0.8.0...0.9.0) --- updated-dependencies: - dependency-name: io.element.android:wysiwyg dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index fd630eba6d..b408ee01eb 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -98,7 +98,7 @@ ext.libs = [ ], element : [ 'opusencoder' : "io.element.android:opusencoder:1.1.0", - 'wysiwyg' : "io.element.android:wysiwyg:0.8.0" + 'wysiwyg' : "io.element.android:wysiwyg:0.9.0" ], squareup : [ 'moshi' : "com.squareup.moshi:moshi:$moshi", From 11dded71ec0c642039214c6deec0d73f8c1ca039 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 7 Dec 2022 13:54:20 +0100 Subject: [PATCH 098/108] Changelog --- changelog.d/7725.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7725.bugfix diff --git a/changelog.d/7725.bugfix b/changelog.d/7725.bugfix new file mode 100644 index 0000000000..b701451505 --- /dev/null +++ b/changelog.d/7725.bugfix @@ -0,0 +1 @@ +Fix crash when the network is not available. From 1acd8e10499c6553996142591b17d44b9d375273 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 6 Dec 2022 15:01:47 +0100 Subject: [PATCH 099/108] Do not propagate failure if saving the filter server side fails. This will be retried later. --- .../session/filter/GetCurrentFilterTask.kt | 2 +- .../internal/session/filter/SaveFilterTask.kt | 18 +++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/GetCurrentFilterTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/GetCurrentFilterTask.kt index e88f286e27..76805c5c51 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/GetCurrentFilterTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/GetCurrentFilterTask.kt @@ -42,7 +42,7 @@ internal class DefaultGetCurrentFilterTask @Inject constructor( return when (storedFilterBody) { currentFilterBody -> storedFilterId ?: storedFilterBody - else -> saveFilter(currentFilter) + else -> saveFilter(currentFilter) ?: currentFilterBody } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/SaveFilterTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/SaveFilterTask.kt index 82d5ff4d2f..0223cd3ee7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/SaveFilterTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/SaveFilterTask.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.internal.session.filter +import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.network.GlobalErrorReceiver import org.matrix.android.sdk.internal.network.executeRequest @@ -24,8 +25,9 @@ import javax.inject.Inject /** * Save a filter, in db and if any changes, upload to the server. + * Return the filterId if uploading to the server is successful, else return null. */ -internal interface SaveFilterTask : Task { +internal interface SaveFilterTask : Task { data class Params( val filter: Filter @@ -39,18 +41,20 @@ internal class DefaultSaveFilterTask @Inject constructor( private val globalErrorReceiver: GlobalErrorReceiver, ) : SaveFilterTask { - override suspend fun execute(params: SaveFilterTask.Params): String { + override suspend fun execute(params: SaveFilterTask.Params): String? { val filter = params.filter - val filterResponse = executeRequest(globalErrorReceiver) { - // TODO auto retry - filterAPI.uploadFilter(userId, filter) + val filterResponse = tryOrNull { + executeRequest(globalErrorReceiver) { + filterAPI.uploadFilter(userId, filter) + } } + val filterId = filterResponse?.filterId filterRepository.storeSyncFilter( filter = filter, - filterId = filterResponse.filterId, + filterId = filterId.orEmpty(), roomEventFilter = FilterFactory.createDefaultRoomFilter() ) - return filterResponse.filterId + return filterId } } From 53b703dcafe14b7153840bc72d52d789f60233cb Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 7 Dec 2022 13:54:20 +0100 Subject: [PATCH 100/108] Changelog --- changelog.d/7725.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7725.bugfix diff --git a/changelog.d/7725.bugfix b/changelog.d/7725.bugfix new file mode 100644 index 0000000000..b701451505 --- /dev/null +++ b/changelog.d/7725.bugfix @@ -0,0 +1 @@ +Fix crash when the network is not available. From 6c84668611d2dd63a365caa27b9144646a6e582b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 7 Dec 2022 13:58:02 +0100 Subject: [PATCH 101/108] Hotfix 1.5.11 --- matrix-sdk-android/build.gradle | 2 +- vector-app/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index 60b0329fbc..0b5dc1aacf 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -62,7 +62,7 @@ android { // that the app's state is completely cleared between tests. testInstrumentationRunnerArguments clearPackageData: 'true' - buildConfigField "String", "SDK_VERSION", "\"1.5.10\"" + buildConfigField "String", "SDK_VERSION", "\"1.5.11\"" buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\"" buildConfigField "String", "GIT_SDK_REVISION_UNIX_DATE", "\"${gitRevisionUnixDate()}\"" diff --git a/vector-app/build.gradle b/vector-app/build.gradle index 86b94a8497..0796afe38b 100644 --- a/vector-app/build.gradle +++ b/vector-app/build.gradle @@ -37,7 +37,7 @@ ext.versionMinor = 5 // Note: even values are reserved for regular release, odd values for hotfix release. // When creating a hotfix, you should decrease the value, since the current value // is the value for the next regular release. -ext.versionPatch = 10 +ext.versionPatch = 11 static def getGitTimestamp() { def cmd = 'git show -s --format=%ct' From a0bba91d67f8832812e766baf31b527a5acd6e42 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 7 Dec 2022 13:58:51 +0100 Subject: [PATCH 102/108] Fastlane file for hotfix 1.5.11 --- fastlane/metadata/android/en-US/changelogs/40105110.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/en-US/changelogs/40105110.txt diff --git a/fastlane/metadata/android/en-US/changelogs/40105110.txt b/fastlane/metadata/android/en-US/changelogs/40105110.txt new file mode 100644 index 0000000000..c9e5ba5fa9 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/40105110.txt @@ -0,0 +1,2 @@ +Main changes in this version: New implementation of the full screen mode for the Rich Text Editor and bugfixes. +Full changelog: https://github.com/vector-im/element-android/releases From 3132a7d463d7ce4443a2d1496c3452a85706e95c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 7 Dec 2022 13:59:42 +0100 Subject: [PATCH 103/108] Towncrier 1.5.11 --- CHANGES.md | 8 ++++++++ changelog.d/7725.bugfix | 1 - 2 files changed, 8 insertions(+), 1 deletion(-) delete mode 100644 changelog.d/7725.bugfix diff --git a/CHANGES.md b/CHANGES.md index 022591f5a1..c170c3b92b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,11 @@ +Changes in Element 1.5.11 (2022-12-07) +====================================== + +Bugfixes 🐛 +---------- + - Fix crash when the network is not available. ([#7725](https://github.com/vector-im/element-android/issues/7725)) + + Changes in Element v1.5.10 (2022-11-30) ======================================= diff --git a/changelog.d/7725.bugfix b/changelog.d/7725.bugfix deleted file mode 100644 index b701451505..0000000000 --- a/changelog.d/7725.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fix crash when the network is not available. From c9c5483d227e80d3761596fecba6502ea000321c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 7 Dec 2022 14:09:59 +0100 Subject: [PATCH 104/108] Changelog --- changelog.d/7723.misc | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7723.misc diff --git a/changelog.d/7723.misc b/changelog.d/7723.misc new file mode 100644 index 0000000000..36869d1efb --- /dev/null +++ b/changelog.d/7723.misc @@ -0,0 +1 @@ +Disable nightly popup and add an entry point in the advanced settings instead. From a44c8dfca302bab2e50f349acdbf0ed4a419a325 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 7 Dec 2022 15:10:21 +0100 Subject: [PATCH 105/108] Renaming a method to avoid confusion --- .../settings/devices/v2/VectorSettingsDevicesFragment.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt index a21c7accb7..c21b044f1f 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt @@ -102,7 +102,7 @@ class VectorSettingsDevicesFragment : initWaitingView() initCurrentSessionHeaderView() - initCurrentSessionListView() + initCurrentSessionView() initOtherSessionsHeaderView() initOtherSessionsView() initSecurityRecommendationsView() @@ -177,7 +177,7 @@ class VectorSettingsDevicesFragment : activity?.let { SignOutUiWorker(it).perform() } } - private fun initCurrentSessionListView() { + private fun initCurrentSessionView() { views.deviceListCurrentSession.viewVerifyButton.debouncedClicks { viewModel.handle(DevicesAction.VerifyCurrentSession) } From 6c94f1cd52a6e871281a299ef92614dfeddb5cf2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 7 Dec 2022 15:50:26 +0100 Subject: [PATCH 106/108] Quick tweak on the release script. --- tools/release/releaseScript.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/release/releaseScript.sh b/tools/release/releaseScript.sh index d76cd98061..f91e11584c 100755 --- a/tools/release/releaseScript.sh +++ b/tools/release/releaseScript.sh @@ -345,7 +345,8 @@ ${buildToolsPath}/aapt dump badging ${targetPath}/vector-gplay-x86-release-signe printf "File vector-gplay-x86_64-release-signed.apk:\n" ${buildToolsPath}/aapt dump badging ${targetPath}/vector-gplay-x86_64-release-signed.apk | grep package -read -p "\nDoes it look correct? Press enter when it's done." +printf "\n" +read -p "Does it look correct? Press enter when it's done." printf "\n================================================================================\n" read -p "Installing apk on a real device, press enter when a real device is connected. " @@ -356,7 +357,7 @@ read -p "Please run the APK on your phone to check that the upgrade went well (n # TODO Get the block to copy from towncrier earlier (be may be edited by the release manager)? read -p "Create the release on gitHub from the tag https://github.com/vector-im/element-android/tags, copy paste the block from the file CHANGES.md. Press enter when it's done." -read -p "Add the 4 signed APKs to the GitHub release. Press enter when it's done." +read -p "Add the 4 signed APKs to the GitHub release. They are located at ${targetPath}. Press enter when it's done." printf "\n================================================================================\n" printf "Message for the Android internal room:\n\n" From 72ecd1bbc9adcf9fa01f2ed3cfa8e7b86af349a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Dec 2022 10:51:20 +0100 Subject: [PATCH 107/108] Bump kotlin-gradle-plugin from 1.7.21 to 1.7.22 (#7664) Bumps [kotlin-gradle-plugin](https://github.com/JetBrains/kotlin) from 1.7.21 to 1.7.22. - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.7.21...v1.7.22) --- updated-dependencies: - dependency-name: org.jetbrains.kotlin:kotlin-gradle-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index b408ee01eb..a9aee3b681 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -8,7 +8,7 @@ ext.versions = [ def gradle = "7.3.1" // Ref: https://kotlinlang.org/releases.html -def kotlin = "1.7.21" +def kotlin = "1.7.22" def kotlinCoroutines = "1.6.4" def dagger = "2.44.2" def appDistribution = "16.0.0-beta05" From de18f37849fa599eb174ea9f5394867b86378670 Mon Sep 17 00:00:00 2001 From: jonnyandrew Date: Thu, 8 Dec 2022 11:43:19 +0000 Subject: [PATCH 108/108] [Rich text editor] Add error tracking for rich text editor (#7695) --- changelog.d/7695.bugfix | 1 + .../im/vector/app/core/di/SingletonModule.kt | 4 +++ .../app/features/analytics/VectorAnalytics.kt | 3 +- .../features/analytics/errors/ErrorTracker.kt | 21 +++++++++++++ .../analytics/impl/DefaultVectorAnalytics.kt | 14 ++++++--- .../{SentryFactory.kt => SentryAnalytics.kt} | 9 ++++-- .../composer/MessageComposerFragment.kt | 3 ++ .../detail/composer/RichTextComposerLayout.kt | 10 ++++-- .../composer/RichTextEditorException.kt | 21 +++++++++++++ .../impl/DefaultVectorAnalyticsTest.kt | 31 +++++++++++++++---- ...entryFactory.kt => FakeSentryAnalytics.kt} | 15 +++++++-- 11 files changed, 114 insertions(+), 18 deletions(-) create mode 100644 changelog.d/7695.bugfix create mode 100644 vector/src/main/java/im/vector/app/features/analytics/errors/ErrorTracker.kt rename vector/src/main/java/im/vector/app/features/analytics/impl/{SentryFactory.kt => SentryAnalytics.kt} (88%) create mode 100644 vector/src/main/java/im/vector/app/features/home/room/detail/composer/RichTextEditorException.kt rename vector/src/test/java/im/vector/app/test/fakes/{FakeSentryFactory.kt => FakeSentryAnalytics.kt} (74%) diff --git a/changelog.d/7695.bugfix b/changelog.d/7695.bugfix new file mode 100644 index 0000000000..7ec0805bce --- /dev/null +++ b/changelog.d/7695.bugfix @@ -0,0 +1 @@ +[Rich text editor] Add error tracking for rich text editor diff --git a/vector-app/src/main/java/im/vector/app/core/di/SingletonModule.kt b/vector-app/src/main/java/im/vector/app/core/di/SingletonModule.kt index 28ca761ace..21a46b0757 100644 --- a/vector-app/src/main/java/im/vector/app/core/di/SingletonModule.kt +++ b/vector-app/src/main/java/im/vector/app/core/di/SingletonModule.kt @@ -46,6 +46,7 @@ import im.vector.app.core.utils.AndroidSystemSettingsProvider import im.vector.app.core.utils.SystemSettingsProvider import im.vector.app.features.analytics.AnalyticsTracker import im.vector.app.features.analytics.VectorAnalytics +import im.vector.app.features.analytics.errors.ErrorTracker import im.vector.app.features.analytics.impl.DefaultVectorAnalytics import im.vector.app.features.analytics.metrics.VectorPlugins import im.vector.app.features.invite.AutoAcceptInvites @@ -84,6 +85,9 @@ import javax.inject.Singleton @Binds abstract fun bindVectorAnalytics(analytics: DefaultVectorAnalytics): VectorAnalytics + @Binds + abstract fun bindErrorTracker(analytics: DefaultVectorAnalytics): ErrorTracker + @Binds abstract fun bindAnalyticsTracker(analytics: DefaultVectorAnalytics): AnalyticsTracker diff --git a/vector/src/main/java/im/vector/app/features/analytics/VectorAnalytics.kt b/vector/src/main/java/im/vector/app/features/analytics/VectorAnalytics.kt index 7d11f93883..802ba08092 100644 --- a/vector/src/main/java/im/vector/app/features/analytics/VectorAnalytics.kt +++ b/vector/src/main/java/im/vector/app/features/analytics/VectorAnalytics.kt @@ -16,9 +16,10 @@ package im.vector.app.features.analytics +import im.vector.app.features.analytics.errors.ErrorTracker import kotlinx.coroutines.flow.Flow -interface VectorAnalytics : AnalyticsTracker { +interface VectorAnalytics : AnalyticsTracker, ErrorTracker { /** * Return a Flow of Boolean, true if the user has given their consent. */ diff --git a/vector/src/main/java/im/vector/app/features/analytics/errors/ErrorTracker.kt b/vector/src/main/java/im/vector/app/features/analytics/errors/ErrorTracker.kt new file mode 100644 index 0000000000..8ad6bfffc0 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/analytics/errors/ErrorTracker.kt @@ -0,0 +1,21 @@ +/* + * 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.errors + +interface ErrorTracker { + fun trackError(throwable: Throwable) +} 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 553d699d86..ca7608166c 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 @@ -41,7 +41,7 @@ private val IGNORED_OPTIONS: Options? = null @Singleton class DefaultVectorAnalytics @Inject constructor( postHogFactory: PostHogFactory, - private val sentryFactory: SentryFactory, + private val sentryAnalytics: SentryAnalytics, analyticsConfig: AnalyticsConfig, private val analyticsStore: AnalyticsStore, private val lateInitUserPropertiesFactory: LateInitUserPropertiesFactory, @@ -97,7 +97,7 @@ class DefaultVectorAnalytics @Inject constructor( setAnalyticsId("") // Close Sentry SDK. - sentryFactory.stopSentry() + sentryAnalytics.stopSentry() } private fun observeAnalyticsId() { @@ -135,8 +135,8 @@ class DefaultVectorAnalytics @Inject constructor( private fun initOrStopSentry() { userConsent?.let { when (it) { - true -> sentryFactory.initSentry() - false -> sentryFactory.stopSentry() + true -> sentryAnalytics.initSentry() + false -> sentryAnalytics.stopSentry() } } } @@ -180,4 +180,10 @@ class DefaultVectorAnalytics @Inject constructor( putAll(this@toPostHogUserProperties.filter { it.value != null }) } } + + override fun trackError(throwable: Throwable) { + sentryAnalytics + .takeIf { userConsent == true } + ?.trackError(throwable) + } } diff --git a/vector/src/main/java/im/vector/app/features/analytics/impl/SentryFactory.kt b/vector/src/main/java/im/vector/app/features/analytics/impl/SentryAnalytics.kt similarity index 88% rename from vector/src/main/java/im/vector/app/features/analytics/impl/SentryFactory.kt rename to vector/src/main/java/im/vector/app/features/analytics/impl/SentryAnalytics.kt index a000f2a77a..21721a31ae 100644 --- a/vector/src/main/java/im/vector/app/features/analytics/impl/SentryFactory.kt +++ b/vector/src/main/java/im/vector/app/features/analytics/impl/SentryAnalytics.kt @@ -18,6 +18,7 @@ package im.vector.app.features.analytics.impl import android.content.Context import im.vector.app.features.analytics.AnalyticsConfig +import im.vector.app.features.analytics.errors.ErrorTracker import im.vector.app.features.analytics.log.analyticsTag import io.sentry.Sentry import io.sentry.SentryOptions @@ -25,10 +26,10 @@ import io.sentry.android.core.SentryAndroid import timber.log.Timber import javax.inject.Inject -class SentryFactory @Inject constructor( +class SentryAnalytics @Inject constructor( private val context: Context, private val analyticsConfig: AnalyticsConfig, -) { +) : ErrorTracker { fun initSentry() { Timber.tag(analyticsTag.value).d("Initializing Sentry") @@ -47,4 +48,8 @@ class SentryFactory @Inject constructor( Timber.tag(analyticsTag.value).d("Stopping Sentry") Sentry.close() } + + override fun trackError(throwable: Throwable) { + Sentry.captureException(throwable) + } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt index 97e74785ec..bf9e0ae726 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt @@ -60,6 +60,7 @@ import im.vector.app.core.utils.onPermissionDeniedDialog import im.vector.app.core.utils.registerForPermissionsResult import im.vector.app.databinding.FragmentComposerBinding import im.vector.app.features.VectorFeatures +import im.vector.app.features.analytics.errors.ErrorTracker import im.vector.app.features.attachments.AttachmentType import im.vector.app.features.attachments.AttachmentTypeSelectorBottomSheet import im.vector.app.features.attachments.AttachmentTypeSelectorSharedAction @@ -116,6 +117,7 @@ class MessageComposerFragment : VectorBaseFragment(), A @Inject lateinit var vectorFeatures: VectorFeatures @Inject lateinit var buildMeta: BuildMeta @Inject lateinit var session: Session + @Inject lateinit var errorTracker: ErrorTracker private val roomId: String get() = withState(timelineViewModel) { it.roomId } @@ -171,6 +173,7 @@ class MessageComposerFragment : VectorBaseFragment(), A views.composerLayout.isGone = vectorPreferences.isRichTextEditorEnabled() views.richTextComposerLayout.isVisible = vectorPreferences.isRichTextEditorEnabled() + views.richTextComposerLayout.setOnErrorListener(errorTracker::trackError) messageComposerViewModel.observeViewEvents { when (it) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/RichTextComposerLayout.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/RichTextComposerLayout.kt index 48459b5c06..16234c3766 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/RichTextComposerLayout.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/RichTextComposerLayout.kt @@ -49,10 +49,11 @@ import im.vector.app.databinding.ComposerRichTextLayoutBinding import im.vector.app.databinding.ViewRichTextMenuButtonBinding import io.element.android.wysiwyg.EditorEditText import io.element.android.wysiwyg.inputhandlers.models.InlineFormat +import io.element.android.wysiwyg.utils.RustErrorCollector import uniffi.wysiwyg_composer.ActionState import uniffi.wysiwyg_composer.ComposerAction -class RichTextComposerLayout @JvmOverloads constructor( +internal class RichTextComposerLayout @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 @@ -248,10 +249,15 @@ class RichTextComposerLayout @JvmOverloads constructor( updateMenuStateFor(action, state) } } - updateEditTextVisibility() } + fun setOnErrorListener(onError: (e: RichTextEditorException) -> Unit) { + views.richTextComposerEditText.rustErrorCollector = RustErrorCollector { + onError(RichTextEditorException(it)) + } + } + private fun updateEditTextVisibility() { views.richTextComposerEditText.isVisible = isTextFormattingEnabled views.richTextMenu.isVisible = isTextFormattingEnabled diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/RichTextEditorException.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/RichTextEditorException.kt new file mode 100644 index 0000000000..9bdef59ae3 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/RichTextEditorException.kt @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.detail.composer + +internal class RichTextEditorException( + cause: Throwable, +) : Exception(cause) diff --git a/vector/src/test/java/im/vector/app/features/analytics/impl/DefaultVectorAnalyticsTest.kt b/vector/src/test/java/im/vector/app/features/analytics/impl/DefaultVectorAnalyticsTest.kt index be53f1b908..3fd0528a19 100644 --- a/vector/src/test/java/im/vector/app/features/analytics/impl/DefaultVectorAnalyticsTest.kt +++ b/vector/src/test/java/im/vector/app/features/analytics/impl/DefaultVectorAnalyticsTest.kt @@ -23,7 +23,7 @@ import im.vector.app.test.fakes.FakeAnalyticsStore import im.vector.app.test.fakes.FakeLateInitUserPropertiesFactory import im.vector.app.test.fakes.FakePostHog import im.vector.app.test.fakes.FakePostHogFactory -import im.vector.app.test.fakes.FakeSentryFactory +import im.vector.app.test.fakes.FakeSentryAnalytics import im.vector.app.test.fixtures.AnalyticsConfigFixture.anAnalyticsConfig import im.vector.app.test.fixtures.aUserProperties import im.vector.app.test.fixtures.aVectorAnalyticsEvent @@ -46,11 +46,11 @@ class DefaultVectorAnalyticsTest { private val fakePostHog = FakePostHog() private val fakeAnalyticsStore = FakeAnalyticsStore() private val fakeLateInitUserPropertiesFactory = FakeLateInitUserPropertiesFactory() - private val fakeSentryFactory = FakeSentryFactory() + private val fakeSentryAnalytics = FakeSentryAnalytics() private val defaultVectorAnalytics = DefaultVectorAnalytics( postHogFactory = FakePostHogFactory(fakePostHog.instance).instance, - sentryFactory = fakeSentryFactory.instance, + sentryAnalytics = fakeSentryAnalytics.instance, analyticsStore = fakeAnalyticsStore.instance, globalScope = CoroutineScope(Dispatchers.Unconfined), analyticsConfig = anAnalyticsConfig(isEnabled = true), @@ -75,7 +75,7 @@ class DefaultVectorAnalyticsTest { fakePostHog.verifyOptOutStatus(optedOut = false) - fakeSentryFactory.verifySentryInit() + fakeSentryAnalytics.verifySentryInit() } @Test @@ -84,7 +84,7 @@ class DefaultVectorAnalyticsTest { fakePostHog.verifyOptOutStatus(optedOut = true) - fakeSentryFactory.verifySentryClose() + fakeSentryAnalytics.verifySentryClose() } @Test @@ -111,7 +111,7 @@ class DefaultVectorAnalyticsTest { fakePostHog.verifyReset() - fakeSentryFactory.verifySentryClose() + fakeSentryAnalytics.verifySentryClose() } @Test @@ -149,6 +149,25 @@ class DefaultVectorAnalyticsTest { fakePostHog.verifyNoEventTracking() } + + @Test + fun `given user has consented, when tracking exception, then submits to sentry`() = runTest { + fakeAnalyticsStore.givenUserContent(consent = true) + val exception = Exception("test") + + defaultVectorAnalytics.trackError(exception) + + fakeSentryAnalytics.verifySentryTrackError(exception) + } + + @Test + fun `given user has not consented, when tracking exception, then does not track to sentry`() = runTest { + fakeAnalyticsStore.givenUserContent(consent = false) + + defaultVectorAnalytics.trackError(Exception("test")) + + fakeSentryAnalytics.verifyNoErrorTracking() + } } private fun VectorAnalyticsScreen.toPostHogProperties(): Properties? { diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeSentryFactory.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeSentryAnalytics.kt similarity index 74% rename from vector/src/test/java/im/vector/app/test/fakes/FakeSentryFactory.kt rename to vector/src/test/java/im/vector/app/test/fakes/FakeSentryAnalytics.kt index 2628f80435..59f41543b0 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeSentryFactory.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeSentryAnalytics.kt @@ -16,15 +16,15 @@ package im.vector.app.test.fakes -import im.vector.app.features.analytics.impl.SentryFactory +import im.vector.app.features.analytics.impl.SentryAnalytics import io.mockk.every import io.mockk.mockk import io.mockk.verify -class FakeSentryFactory { +class FakeSentryAnalytics { private var isSentryEnabled = false - val instance = mockk().also { + val instance = mockk(relaxUnitFun = true).also { every { it.initSentry() } answers { isSentryEnabled = true } @@ -41,4 +41,13 @@ class FakeSentryFactory { fun verifySentryClose() { verify { instance.stopSentry() } } + + fun verifySentryTrackError(error: Throwable) { + verify { instance.trackError(error) } + } + + fun verifyNoErrorTracking() = + verify(inverse = true) { + instance.trackError(any()) + } }