diff --git a/.github/dependabot.yml b/.github/dependabot.yml index e552f5fd43..8c2f1041e0 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -18,6 +18,5 @@ updates: open-pull-requests-limit: 200 reviewers: - "bmarty" -### ignore: -### - dependency-name: com.squareup.okhttp3:logging-interceptor -### versions: "> 3.12.10" + ignore: + - dependency-name: com.google.zxing:core diff --git a/CHANGES.md b/CHANGES.md index 3824fead36..9590adc060 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,39 @@ +Changes in Element v1.3.7 (2021-11-04) +====================================== + +Features ✨ +---------- + - Adding the room name to the invitation notification (if the room summary is available) ([#582](https://github.com/vector-im/element-android/issues/582)) + - Updating single sign on providers ordering to match priority/popularity ([#4277](https://github.com/vector-im/element-android/issues/4277)) + +Bugfixes 🐛 +---------- + - Stops showing a dedicated redacted event notification, the message notifications will update accordingly ([#1491](https://github.com/vector-im/element-android/issues/1491)) + - Fixes marking individual notifications as read causing other notifications to be dismissed ([#3395](https://github.com/vector-im/element-android/issues/3395)) + - Fixing missing send button in light mode dev tools - send * event ([#3674](https://github.com/vector-im/element-android/issues/3674)) + - Fixing room search needing exact casing for non latin-1 character named rooms ([#3968](https://github.com/vector-im/element-android/issues/3968)) + - Fixing call ringtones only playing once when the ringtone doesn't contain looping metadata (android 9.0 and above) ([#4047](https://github.com/vector-im/element-android/issues/4047)) + - Tentatively fixing the doubled notifications by updating the group summary at specific points in the notification rendering cycle ([#4152](https://github.com/vector-im/element-android/issues/4152)) + - Do not show shortcuts if a PIN code is set ([#4170](https://github.com/vector-im/element-android/issues/4170)) + - Fixes being unable to join rooms by name ([#4255](https://github.com/vector-im/element-android/issues/4255)) + - Fixing missing F-Droid notifications when in background due to background syncs not triggering ([#4298](https://github.com/vector-im/element-android/issues/4298)) + - Fix video compression before upload ([#4353](https://github.com/vector-im/element-android/issues/4353)) + - Fixing QR code crashes caused by a known issue in the zxing library for older versions of android by downgrading to 3.3.3 ([#4361](https://github.com/vector-im/element-android/issues/4361)) + - Fixing timeline crash when rotating with the emoji window open ([#4365](https://github.com/vector-im/element-android/issues/4365)) + - Fix handling of links coming from web instance reported as malformed by mistake ([#4369](https://github.com/vector-im/element-android/issues/4369)) + +SDK API changes ⚠️ +------------------ + - Add API `LoginWizard.loginCustom(data: JsonDict): Session` to be able to login to a homeserver using arbitrary request content ([#4266](https://github.com/vector-im/element-android/issues/4266)) + - Add optional deviceId to the login API ([#4334](https://github.com/vector-im/element-android/issues/4334)) + +Other changes +------------- + - Migrate app DI framework to Hilt ([#3888](https://github.com/vector-im/element-android/issues/3888)) + - Limit supported TLS versions and cipher suites ([#4192](https://github.com/vector-im/element-android/issues/4192)) + - Fixed capitalisation of text on initial sync screen ([#4292](https://github.com/vector-im/element-android/issues/4292)) + + Changes in Element v1.3.6 (2021-10-26) ====================================== diff --git a/build.gradle b/build.gradle index 93f3e17f34..e9045b99c7 100644 --- a/build.gradle +++ b/build.gradle @@ -17,6 +17,7 @@ buildscript { // https://developer.android.com/studio/releases/gradle-plugin classpath libs.gradle.gradlePlugin classpath libs.gradle.kotlinPlugin + classpath libs.gradle.hiltPlugin classpath 'com.google.gms:google-services:4.3.10' classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.3' classpath 'com.google.android.gms:oss-licenses-plugin:0.10.4' diff --git a/dependencies.gradle b/dependencies.gradle index 7fa42a666f..47090d4732 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -11,7 +11,7 @@ def gradle = "7.0.3" // Ref: https://kotlinlang.org/releases.html def kotlin = "1.5.31" def kotlinCoroutines = "1.5.2" -def dagger = "2.39.1" +def dagger = "2.40" def retrofit = "2.9.0" def arrow = "0.8.2" def markwon = "4.6.2" @@ -34,7 +34,9 @@ def androidxTest = "1.4.0" ext.libs = [ gradle : [ 'gradlePlugin' : "com.android.tools.build:gradle:$gradle", - 'kotlinPlugin' : "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin" + 'kotlinPlugin' : "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin", + 'hiltPlugin' : "com.google.dagger:hilt-android-gradle-plugin:$dagger" + ], jetbrains : [ 'coroutinesCore' : "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinCoroutines", @@ -72,7 +74,9 @@ ext.libs = [ ], dagger : [ 'dagger' : "com.google.dagger:dagger:$dagger", - 'daggerCompiler' : "com.google.dagger:dagger-compiler:$dagger" + 'daggerCompiler' : "com.google.dagger:dagger-compiler:$dagger", + 'hilt' : "com.google.dagger:hilt-android:$dagger", + 'hiltCompiler' : "com.google.dagger:hilt-compiler:$dagger" ], squareup : [ 'moshi' : "com.squareup.moshi:moshi-adapters:$moshi", diff --git a/docs/hilt_migration.md b/docs/hilt_migration.md new file mode 100644 index 0000000000..50021e9792 --- /dev/null +++ b/docs/hilt_migration.md @@ -0,0 +1,33 @@ +Useful links: +- https://dagger.dev/hilt/migration-guide +- https://dagger.dev/hilt/quick-start + +Hilt is built on top of Dagger 2 and simplify usage by removing needs to create components manually. + +When you create a new feature, you should have the following: + +Annotate your Activity with @AndroidEntryPoint +If you have a BottomSheetFragment => Annotate it with @AndroidEntryPoint +Otherwise => Add your Fragment to the FragmentModule +Add your ViewModel.Factory to the MavericksViewModelModule +Makes sure your ViewModel as the following code: + +``` + @AssistedFactory + interface Factory: MavericksAssistedViewModelFactory { + override fun create(initialState: MyViewState): MyViewModel + } + + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() +``` + +## Some remarks + +@MavericksViewModelScope dependencies can't be injected inside Fragments/Activities +You can only inject @Singleton, @MavericksViewModelScope or unscoped dependencies inside Maverick ViewModels +You can access some specific dependencies from Singleton component by using +``` +context.singletonEntryPoint() +``` +Be aware that only the app has been migrated to Hilt and not the SDK. + diff --git a/fastlane/metadata/android/cs-CZ/changelogs/40103040.txt b/fastlane/metadata/android/cs-CZ/changelogs/40103040.txt new file mode 100644 index 0000000000..ac909485d7 --- /dev/null +++ b/fastlane/metadata/android/cs-CZ/changelogs/40103040.txt @@ -0,0 +1,2 @@ +Hlavní změny v této verzi: Přidání podpory přítomnosti pro místnost s přímými zprávami (poznámka: přítomnost je na matrix.org zakázána). Opět přidána podpora Android Auto. +Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.3.4 diff --git a/fastlane/metadata/android/de-DE/changelogs/40103000.txt b/fastlane/metadata/android/de-DE/changelogs/40103000.txt new file mode 100644 index 0000000000..85386226db --- /dev/null +++ b/fastlane/metadata/android/de-DE/changelogs/40103000.txt @@ -0,0 +1,2 @@ +Hauptänderungen: Organisiere deine Räume mit Spaces! +Alle Änderungen: https://github.com/vector-im/element-android/releases/tag/v1.3.0 diff --git a/fastlane/metadata/android/de-DE/changelogs/40103010.txt b/fastlane/metadata/android/de-DE/changelogs/40103010.txt new file mode 100644 index 0000000000..3323a37a59 --- /dev/null +++ b/fastlane/metadata/android/de-DE/changelogs/40103010.txt @@ -0,0 +1,2 @@ +Hauptänderungen: Organisiere deine Räume mit Spaces, Crash aus 1.3.0 gefixt. +Alle Änderungen: https://github.com/vector-im/element-android/releases/tag/v1.3.1 diff --git a/fastlane/metadata/android/de-DE/changelogs/40103020.txt b/fastlane/metadata/android/de-DE/changelogs/40103020.txt new file mode 100644 index 0000000000..880ec71c4d --- /dev/null +++ b/fastlane/metadata/android/de-DE/changelogs/40103020.txt @@ -0,0 +1,2 @@ +Hauptänderungen: Unterstützung für Android Auto, Viele Fehlerbehebungen +Alle Änderungen: https://github.com/vector-im/element-android/releases/tag/v1.3.2 diff --git a/fastlane/metadata/android/de-DE/changelogs/40103030.txt b/fastlane/metadata/android/de-DE/changelogs/40103030.txt new file mode 100644 index 0000000000..da3451fb0d --- /dev/null +++ b/fastlane/metadata/android/de-DE/changelogs/40103030.txt @@ -0,0 +1,2 @@ +Hauptänderungen: Bedingungen des Identitätsservers in Einstellungen anzeigen. Unterstützung für Android Auto temporär entfernt. +Alle Änderungen: https://github.com/vector-im/element-android/releases/tag/v1.3.3 diff --git a/fastlane/metadata/android/de-DE/changelogs/40103040.txt b/fastlane/metadata/android/de-DE/changelogs/40103040.txt new file mode 100644 index 0000000000..37ab9ccd8f --- /dev/null +++ b/fastlane/metadata/android/de-DE/changelogs/40103040.txt @@ -0,0 +1,4 @@ +Hauptänderungen: +- Anwesenheiten in Direktnachrichten anzeigen (Momentan auf Matrix.org deaktiviert) +- Android Auto wird wieder unterstützt +Alle Änderungen: https://github.com/vector-im/element-android/releases/tag/v1.3.4 diff --git a/fastlane/metadata/android/de-DE/full_description.txt b/fastlane/metadata/android/de-DE/full_description.txt index 30eb153ee9..c4fecf7674 100644 --- a/fastlane/metadata/android/de-DE/full_description.txt +++ b/fastlane/metadata/android/de-DE/full_description.txt @@ -37,3 +37,6 @@ Kurznachrichten, Sprach- und Videoanrufe, Dateifreigaben, Bildschirmübertragung Da Weitermachen, wo Sie aufgehört haben Bleiben Sie in Kontakt, egal wo Sie sind, mit vollständig synchronisiertem Nachrichtenverlauf quer über all Ihre Geräte und im Netz auf https://app.element.io + +Open source +Element ist ein Open-Source-Projekt und wird auf GitHub gehostet. Solltest du Fehler in Element finden, melde diese bitte hier: https://github.com/vector-im/element-android diff --git a/fastlane/metadata/android/en-US/changelogs/40103070.txt b/fastlane/metadata/android/en-US/changelogs/40103070.txt new file mode 100644 index 0000000000..ad225133c2 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/40103070.txt @@ -0,0 +1,2 @@ +Main changes in this version: Bug fixes mainly regarding the notifications. +Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.3.7 \ No newline at end of file diff --git a/fastlane/metadata/android/et/changelogs/40103040.txt b/fastlane/metadata/android/et/changelogs/40103040.txt new file mode 100644 index 0000000000..2ede9de81e --- /dev/null +++ b/fastlane/metadata/android/et/changelogs/40103040.txt @@ -0,0 +1,2 @@ +Põhilised muutused selles versioonis: Lisasime otsevestlustele kasutaja võrguolekute toe (matrix.org puhul on välja lülitatud) ja uuesti lisasime Android Auto toe. +Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.3.4 diff --git a/fastlane/metadata/android/fa/changelogs/40103040.txt b/fastlane/metadata/android/fa/changelogs/40103040.txt new file mode 100644 index 0000000000..5da14baf6e --- /dev/null +++ b/fastlane/metadata/android/fa/changelogs/40103040.txt @@ -0,0 +1,2 @@ +تغییرات اصلی در این نگارش: افزودن پشتیبانی حضور برای اتاق پیام خصوصی (توجه: حضور روی matrix.org از کار افتاده است). افزودن دوبارهٔ پشتیبانی اندروید خودرو. +گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.3.4 diff --git a/fastlane/metadata/android/fr-FR/changelogs/40103040.txt b/fastlane/metadata/android/fr-FR/changelogs/40103040.txt new file mode 100644 index 0000000000..7017fa243f --- /dev/null +++ b/fastlane/metadata/android/fr-FR/changelogs/40103040.txt @@ -0,0 +1,2 @@ +Principaux changements pour cette version : ajout du support pour les indicateurs de présence, dans les conversations privées (attention : les indicateurs de présence sont désactivés sur matrix.org). Réactivation de la prise en charge de Android Auto. +Intégralité des changements : https://github.com/vector-im/element-android/releases/tag/v1.3.4 diff --git a/fastlane/metadata/android/hu-HU/changelogs/40103040.txt b/fastlane/metadata/android/hu-HU/changelogs/40103040.txt new file mode 100644 index 0000000000..de2e859028 --- /dev/null +++ b/fastlane/metadata/android/hu-HU/changelogs/40103040.txt @@ -0,0 +1,2 @@ +Fő változás ebben a verzióban: Állapot állítási lehetőség közvetlen beszélgetéseknél (megj.: a matrix.org-on az állapot jelzés ki van kapcsolva). Újra elérhető az Android Auto. +Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.3.4 diff --git a/fastlane/metadata/android/id/changelogs/40103040.txt b/fastlane/metadata/android/id/changelogs/40103040.txt new file mode 100644 index 0000000000..60e2e3a4de --- /dev/null +++ b/fastlane/metadata/android/id/changelogs/40103040.txt @@ -0,0 +1,2 @@ +Perubahan utama di versi ini: Menambahkan dukungan presensi, untuk ruangan Pesan Langsung (diingat bahwa presensi dinonaktifkan di matrix.org). Menambahkan lagi dukungan Android Auto. +Changelog lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.3.4 diff --git a/fastlane/metadata/android/it-IT/changelogs/40103040.txt b/fastlane/metadata/android/it-IT/changelogs/40103040.txt new file mode 100644 index 0000000000..e28ce08e03 --- /dev/null +++ b/fastlane/metadata/android/it-IT/changelogs/40103040.txt @@ -0,0 +1,2 @@ +Modifiche principali in questa versione: aggiunto supporto alla presenza per messaggi diretti (nota: la presenza è disattivata su matrix.org). Aggiunto di nuovo il supporto ad Android Auto. +Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.3.4 diff --git a/fastlane/metadata/android/pt-BR/changelogs/40103040.txt b/fastlane/metadata/android/pt-BR/changelogs/40103040.txt new file mode 100644 index 0000000000..b713e0418f --- /dev/null +++ b/fastlane/metadata/android/pt-BR/changelogs/40103040.txt @@ -0,0 +1,2 @@ +Principais mudanças nesta versão: Adicionar suporte a Presença, para sala de Mensagem Direta (nota: presença está desabilitada em matrix.org). Adicionar de novo suporte a Android Auto. +Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.3.4 diff --git a/fastlane/metadata/android/ru-RU/changelogs/40103040.txt b/fastlane/metadata/android/ru-RU/changelogs/40103040.txt new file mode 100644 index 0000000000..e4e5edc5b1 --- /dev/null +++ b/fastlane/metadata/android/ru-RU/changelogs/40103040.txt @@ -0,0 +1,2 @@ +Основные изменения в этой версии: Добавлена поддержка присутствия, для комнат личных сообщений (примечание: присутствие отключено на matrix.org). Снова добавлена поддержка Android Auto. +Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.3.4 diff --git a/fastlane/metadata/android/sq/changelogs/40103040.txt b/fastlane/metadata/android/sq/changelogs/40103040.txt new file mode 100644 index 0000000000..7f37e82801 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40103040.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: Shtim mbulimi për Prani, për dhomë Mesazh i Drejtpërdrejtë (shënim: në matrix.org prania është e çaktivizuar. Shtim sërish i mbulimit për Android Auto. +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.3.4 diff --git a/fastlane/metadata/android/sv-SE/changelogs/40103040.txt b/fastlane/metadata/android/sv-SE/changelogs/40103040.txt new file mode 100644 index 0000000000..faec3bef4d --- /dev/null +++ b/fastlane/metadata/android/sv-SE/changelogs/40103040.txt @@ -0,0 +1,2 @@ +Huvudsakliga ändringar i den här versionen: Lägg till närvarostöd för direktmeddelanden (obs: närvaro är inaktiverat på matrix.org). Lägg till stöd för Android Auto igen. +Full ändringslogg: https://github.com/vector-im/element-android/releases/tag/v1.3.4 diff --git a/fastlane/metadata/android/uk/changelogs/40103040.txt b/fastlane/metadata/android/uk/changelogs/40103040.txt new file mode 100644 index 0000000000..3e65e0bc07 --- /dev/null +++ b/fastlane/metadata/android/uk/changelogs/40103040.txt @@ -0,0 +1,2 @@ +Основні зміни в цій версії: Додано підтримку присутності для кімнати особистих повідомлень (примітка: присутність вимкнено на matrix.org. Знову додано підтримку Android Auto. +Повний журнал змін: https://github.com/vector-im/element-android/releases/tag/v1.3.4 diff --git a/fastlane/metadata/android/vi/changelogs/40100120.txt b/fastlane/metadata/android/vi/changelogs/40100120.txt new file mode 100644 index 0000000000..28c983ef2c --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40100120.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: xem trước URL, bàn phím Emoji mới, các khả năng cài đặt phòng mới và tuyết cho Giáng Sinh! +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.0.12 diff --git a/fastlane/metadata/android/vi/changelogs/40100130.txt b/fastlane/metadata/android/vi/changelogs/40100130.txt new file mode 100644 index 0000000000..baf756a7be --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40100130.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: xem trước URL, bàn phím Emoji mới, các khả năng cài đặt phòng mới và tuyết cho Giáng Sinh! +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.0.13 diff --git a/fastlane/metadata/android/vi/changelogs/40100140.txt b/fastlane/metadata/android/vi/changelogs/40100140.txt new file mode 100644 index 0000000000..83617e28a3 --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40100140.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: chỉnh sửa quyền phòng, chủ đề Sáng/Tối tự động, và một loạt các bản sửa lỗi. +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.0.14 diff --git a/fastlane/metadata/android/vi/changelogs/40100150.txt b/fastlane/metadata/android/vi/changelogs/40100150.txt new file mode 100644 index 0000000000..b24be2665e --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40100150.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: hỗ trợ đăng nhập từ mạng xã hội. +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.0.15 diff --git a/fastlane/metadata/android/vi/changelogs/40100160.txt b/fastlane/metadata/android/vi/changelogs/40100160.txt new file mode 100644 index 0000000000..3602b769ed --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40100160.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: hỗ trợ đăng nhập từ mạng xã hội. +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.0.15 và https://github.com/vector-im/element-android/releases/tag/v1.0.16 diff --git a/fastlane/metadata/android/vi/changelogs/40100170.txt b/fastlane/metadata/android/vi/changelogs/40100170.txt new file mode 100644 index 0000000000..40bb26719e --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40100170.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: Sửa lỗi! +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.0.17 diff --git a/fastlane/metadata/android/vi/changelogs/40101000.txt b/fastlane/metadata/android/vi/changelogs/40101000.txt new file mode 100644 index 0000000000..7c89b1b7ee --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40101000.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: cải tiến VoIP (các cuộc gọi thoại và video trong Tin nhắn Trực tiếp) và sửa lỗi! +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.1.0 diff --git a/fastlane/metadata/android/vi/changelogs/40101010.txt b/fastlane/metadata/android/vi/changelogs/40101010.txt new file mode 100644 index 0000000000..08b4dbf65c --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40101010.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: cải thiện hiệu suất và sửa lỗi! +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.1.1 diff --git a/fastlane/metadata/android/vi/changelogs/40101020.txt b/fastlane/metadata/android/vi/changelogs/40101020.txt new file mode 100644 index 0000000000..7bdfe395e8 --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40101020.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: cải thiện hiệu suất và sửa lỗi! +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.1.2 diff --git a/fastlane/metadata/android/vi/changelogs/40101030.txt b/fastlane/metadata/android/vi/changelogs/40101030.txt new file mode 100644 index 0000000000..b058f5265f --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40101030.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: cải thiện hiệu suất và sửa lỗi! +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.1.3 diff --git a/fastlane/metadata/android/vi/changelogs/40101040.txt b/fastlane/metadata/android/vi/changelogs/40101040.txt new file mode 100644 index 0000000000..30a5a009b1 --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40101040.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: cải thiện hiệu suất và sửa lỗi! +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.1.4 diff --git a/fastlane/metadata/android/vi/changelogs/40101050.txt b/fastlane/metadata/android/vi/changelogs/40101050.txt new file mode 100644 index 0000000000..01de425aa6 --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40101050.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: sửa lỗi nhanh cho 1.1.4 +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.1.5 diff --git a/fastlane/metadata/android/vi/changelogs/40101060.txt b/fastlane/metadata/android/vi/changelogs/40101060.txt new file mode 100644 index 0000000000..887cc07d8e --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40101060.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: sửa lỗi nhanh cho 1.1.5 +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.1.6 diff --git a/fastlane/metadata/android/vi/changelogs/40101070.txt b/fastlane/metadata/android/vi/changelogs/40101070.txt new file mode 100644 index 0000000000..64441edcc6 --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40101070.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: hỗ trợ beta cho Space. Nén video trước khi gửi. +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.1.7 diff --git a/fastlane/metadata/android/vi/changelogs/40101080.txt b/fastlane/metadata/android/vi/changelogs/40101080.txt new file mode 100644 index 0000000000..2584b0c156 --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40101080.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: Cải tiến cho Space. +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.1.8 diff --git a/fastlane/metadata/android/vi/changelogs/40101090.txt b/fastlane/metadata/android/vi/changelogs/40101090.txt new file mode 100644 index 0000000000..ebf2c0f141 --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40101090.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: thêm hỗ trợ cho mạng gitter.im. +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.1.9 diff --git a/fastlane/metadata/android/vi/changelogs/40101100.txt b/fastlane/metadata/android/vi/changelogs/40101100.txt new file mode 100644 index 0000000000..ba62ad1c9f --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40101100.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: cập nhật chủ đề và phong cách và các tính năng mới cho Space. +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.1.10 diff --git a/fastlane/metadata/android/vi/changelogs/40101110.txt b/fastlane/metadata/android/vi/changelogs/40101110.txt new file mode 100644 index 0000000000..07fce22a35 --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40101110.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: cập nhật chủ đề và phong cách và các tính năng mới cho Space (sửa lỗi cho 1.1.1.0) +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.1.11 diff --git a/fastlane/metadata/android/vi/changelogs/40101120.txt b/fastlane/metadata/android/vi/changelogs/40101120.txt new file mode 100644 index 0000000000..fddcd9656e --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40101120.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: cập nhật chủ đề và phong cách và sửa lỗi sau khi gọi video +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.1.12 diff --git a/fastlane/metadata/android/vi/changelogs/40101130.txt b/fastlane/metadata/android/vi/changelogs/40101130.txt new file mode 100644 index 0000000000..003154de91 --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40101130.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: chủ yếu là sự ổn định và cập nhật sửa lỗi. +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.1.13 diff --git a/fastlane/metadata/android/vi/changelogs/40101140.txt b/fastlane/metadata/android/vi/changelogs/40101140.txt new file mode 100644 index 0000000000..b247be0ce3 --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40101140.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: sửa lỗi các tin nhắn mã hóa. +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.1.14 diff --git a/fastlane/metadata/android/vi/changelogs/40101150.txt b/fastlane/metadata/android/vi/changelogs/40101150.txt new file mode 100644 index 0000000000..28d6523ffb --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40101150.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: triển khai tin nhắn thoại trong cài đặt thí nghiệm. +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.1.15 diff --git a/fastlane/metadata/android/vi/changelogs/40101160.txt b/fastlane/metadata/android/vi/changelogs/40101160.txt new file mode 100644 index 0000000000..4ff2c813dc --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40101160.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: Sửa lỗi gửi tin nhắn mã hóa khi có ai đó trong phòng đăng xuất. +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.1.16 diff --git a/fastlane/metadata/android/vi/changelogs/40102000.txt b/fastlane/metadata/android/vi/changelogs/40102000.txt new file mode 100644 index 0000000000..067d8c5705 --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40102000.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: Tin nhắn Thoại được bật mặc định. +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.2.0 diff --git a/fastlane/metadata/android/vi/changelogs/40102010.txt b/fastlane/metadata/android/vi/changelogs/40102010.txt new file mode 100644 index 0000000000..3e71d31446 --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40102010.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: Nhiều cải tiến trong VoIP và Space (vẫn đang trong beta). +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.2.1 diff --git a/fastlane/metadata/android/vi/changelogs/40103000.txt b/fastlane/metadata/android/vi/changelogs/40103000.txt new file mode 100644 index 0000000000..1bbfba8f38 --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40103000.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: Sắp xếp các phòng của bạn bằng Space! +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.3.0 diff --git a/fastlane/metadata/android/vi/changelogs/40103010.txt b/fastlane/metadata/android/vi/changelogs/40103010.txt new file mode 100644 index 0000000000..d3995343c4 --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40103010.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: Sắp xếp các phòng của bạn bằng Space! V1.3.1 khắc phục sự cố có thể xảy ra ở v1.3.0 +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.3.1 diff --git a/fastlane/metadata/android/vi/changelogs/40103020.txt b/fastlane/metadata/android/vi/changelogs/40103020.txt new file mode 100644 index 0000000000..33a81f4a5d --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40103020.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: Thêm hỗ trợ Android Auto. Sửa rất nhiều lỗi! +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.3.2 diff --git a/fastlane/metadata/android/vi/changelogs/40103030.txt b/fastlane/metadata/android/vi/changelogs/40103030.txt new file mode 100644 index 0000000000..a36a3bb46d --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40103030.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: Hiển thị (các) chính sách máy chủ xác thực trong phần cài đặt. Tạm thời bỏ hỗ trợ Android Auto. +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.3.3 diff --git a/fastlane/metadata/android/vi/changelogs/40103040.txt b/fastlane/metadata/android/vi/changelogs/40103040.txt new file mode 100644 index 0000000000..aadb92827d --- /dev/null +++ b/fastlane/metadata/android/vi/changelogs/40103040.txt @@ -0,0 +1,2 @@ +Những thay đổi chính trong phiên bản này: Thêm hỗ trợ hiển thị, cho phòng Tin nhắn Trực tiếp (lưu ý: hiển thị bị vô hiệu hóa trên matrix.org. Thêm hỗ trợ Android Auto trở lại. +Log thay đổi đầy đủ: https://github.com/vector-im/element-android/releases/tag/v1.3.4 diff --git a/fastlane/metadata/android/vi/full_description.txt b/fastlane/metadata/android/vi/full_description.txt new file mode 100644 index 0000000000..6bccb64fd5 --- /dev/null +++ b/fastlane/metadata/android/vi/full_description.txt @@ -0,0 +1,42 @@ +Element vừa là một ứng dụng nhắn tin an toàn vừa là một ứng dụng cộng tác nhóm năng suất, lý tưởng cho các cuộc trò chuyện nhóm trong khi làm việc từ xa. Ứng dụng trò chuyện này sử dụng mã hóa đầu cuối để cung cấp tính năng hội nghị truyền hình, chia sẻ tệp và cuộc gọi thoại mạnh mẽ. + + Các tính năng của Element bao gồm: +- Các công cụ giao tiếp trực tuyến tiên tiến +- Các tin nhắn được mã hóa hoàn toàn để cho phép liên lạc doanh nghiệp an toàn hơn, ngay cả đối với những người làm việc từ xa +- Trò chuyện phi tập trung dựa trên khung mã nguồn mở Matrix +- Chia sẻ tệp một cách an toàn với dữ liệu được mã hóa trong khi quản lý dự án +- Trò chuyện video với VoIP và chia sẻ màn hình +- Tích hợp dễ dàng với các công cụ cộng tác trực tuyến yêu thích của bạn, công cụ quản lý dự án, dịch vụ VoIP và các ứng dụng nhắn tin nhóm khác + +Element hoàn toàn khác với các ứng dụng nhắn tin và cộng tác khác. Nó hoạt động trên Matrix, một mạng mở để nhắn tin bảo mật và giao tiếp phi tập trung. Nó cho phép tự lưu trữ để cung cấp cho người dùng quyền sở hữu và kiểm soát tối đa dữ liệu và tin nhắn của họ. + + Nhắn tin mã hóa và riêng tư +Element bảo vệ bạn khỏi các quảng cáo không mong muốn, khai thác dữ liệu và khu vườn có tường bao quanh. Nó cũng bảo mật tất cả dữ liệu của bạn, video 1-1 và giao tiếp thoại thông qua mã hóa đầu cuối và xác minh thiết bị có chữ ký chéo. + +Element cung cấp cho bạn quyền kiểm soát quyền riêng tư của mình đồng thời cho phép bạn giao tiếp an toàn với bất kỳ ai trên mạng Ma trận hoặc các công cụ cộng tác kinh doanh khác bằng cách tích hợp với các ứng dụng như Slack. + + Phần tử có thể được tự lưu trữ +Để cho phép kiểm soát nhiều hơn dữ liệu nhạy cảm và các cuộc trò chuyện của bạn, Element có thể được tự host hoặc bạn có thể chọn bất kỳ host Matrix nào - tiêu chuẩn cho giao tiếp phân tán, mã nguồn mở. Element cung cấp cho bạn quyền riêng tư, tuân thủ bảo mật và tính linh hoạt trong tích hợp. + + Sở hữu dữ liệu của bạn +Bạn quyết định nơi lưu giữ dữ liệu và tin nhắn của mình. Không có rủi ro khai thác dữ liệu hoặc truy cập từ bên thứ ba. + +Element giúp bạn kiểm soát theo những cách khác nhau: +1. Nhận một tài khoản miễn phí trên máy chủ công cộng matrix.org do các nhà phát triển Matrix host hoặc chọn từ hàng nghìn máy chủ công cộng do các tình nguyện viên lưu trữ +2. Tự host tài khoản của bạn bằng cách chạy một máy chủ trên cơ sở hạ tầng CNTT của riêng bạn +3. Đăng ký tài khoản trên máy chủ tùy chỉnh bằng cách chỉ cần đăng ký nền tảng Element Matrix Services hosting + + Mở tin nhắn và cộng tác +Bạn có thể trò chuyện với bất kỳ ai trên mạng Matrix, cho dù họ đang sử dụng Element, một ứng dụng Matrix khác hay ngay cả khi họ đang sử dụng một ứng dụng nhắn tin khác. + + Siêu bảo mật +Mã hóa đầu-cuối thực (chỉ những người trong cuộc trò chuyện mới có thể giải mã tin nhắn) và xác minh thiết bị xác thực chéo. + + Giao tiếp và tích hợp hoàn chỉnh +Nhắn tin, cuộc gọi thoại và video, chia sẻ tệp, chia sẻ màn hình và một loạt các tích hợp, bot và widget. Xây dựng phòng, cộng đồng, giữ liên lạc và hoàn thành công việc. + + Tiếp tục nơi bạn đã dừng lại +Giữ liên lạc mọi lúc mọi nơi với lịch sử tin nhắn được đồng bộ hóa hoàn toàn trên tất cả các thiết bị của bạn và trên web tại https://app.element.io + + Mã nguồn mở +Element Android là một dự án mã nguồn mở, được host bởi GitHub. Vui lòng báo cáo lỗi và / hoặc đóng góp vào sự phát triển của nó tại https://github.com/vector-im/element-android diff --git a/fastlane/metadata/android/zh-CN/changelogs/40103040.txt b/fastlane/metadata/android/zh-CN/changelogs/40103040.txt new file mode 100644 index 0000000000..0c3d4d57c3 --- /dev/null +++ b/fastlane/metadata/android/zh-CN/changelogs/40103040.txt @@ -0,0 +1,2 @@ +此版本主要变化:为 Direct Message 聊天室添加 Presence 支持 (注意:Presence 在matrix.org 上是禁用的。再次添加 Android Auto 支持。 +完整更新日志:https://github.com/vector-im/element-android/releases/tag/v1.3.4 diff --git a/fastlane/metadata/android/zh-TW/changelogs/40103040.txt b/fastlane/metadata/android/zh-TW/changelogs/40103040.txt new file mode 100644 index 0000000000..bd82b54e45 --- /dev/null +++ b/fastlane/metadata/android/zh-TW/changelogs/40103040.txt @@ -0,0 +1,2 @@ +此版本中的主要變動:為直接訊息聊天室新增 Presence 支援(請注意:此功能在 matrix.org 上停用)。加回 Android Auto 支援。 +完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.3.4 diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index e3d0f273b8..deea87e8a3 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -31,7 +31,7 @@ android { // that the app's state is completely cleared between tests. testInstrumentationRunnerArguments clearPackageData: 'true' - buildConfigField "String", "SDK_VERSION", "\"1.3.6\"" + buildConfigField "String", "SDK_VERSION", "\"1.3.7\"" buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\"" resValue "string", "git_sdk_revision", "\"${gitRevision()}\"" @@ -156,7 +156,7 @@ dependencies { implementation libs.apache.commonsImaging // Phone number https://github.com/google/libphonenumber - implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.35' + implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.36' testImplementation libs.tests.junit testImplementation 'org.robolectric:robolectric:4.6.1' diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixConfiguration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixConfiguration.kt index 3359e253f6..306ed45500 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixConfiguration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixConfiguration.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.api +import okhttp3.ConnectionSpec import org.matrix.android.sdk.api.crypto.MXCryptoConfig import java.net.Proxy @@ -44,6 +45,10 @@ data class MatrixConfiguration( * You can create one using for instance Proxy(proxyType, InetSocketAddress.createUnresolved(hostname, port). */ val proxy: Proxy? = null, + /** + * TLS versions and cipher suites limitation for unauthenticated requests + */ + val connectionSpec: ConnectionSpec = ConnectionSpec.RESTRICTED_TLS, /** * True to advertise support for call transfers to other parties on Matrix calls. */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt index 5e35917243..9cb784c9c0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt @@ -105,9 +105,15 @@ interface AuthenticationService { /** * Authenticate with a matrixId and a password * Usually call this after a successful call to getWellKnownData() + * @param homeServerConnectionConfig the information about the homeserver and other configuration + * @param matrixId the matrixId of the user + * @param password the password of the account + * @param initialDeviceName the initial device name + * @param deviceId the device id, optional. If not provided or null, the server will generate one. */ suspend fun directAuthentication(homeServerConnectionConfig: HomeServerConnectionConfig, matrixId: String, password: String, - initialDeviceName: String): Session + initialDeviceName: String, + deviceId: String? = null): Session } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/data/HomeServerConnectionConfig.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/data/HomeServerConnectionConfig.kt index 215f0a0351..e87824d6a5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/data/HomeServerConnectionConfig.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/data/HomeServerConnectionConfig.kt @@ -19,6 +19,7 @@ package org.matrix.android.sdk.api.auth.data import android.net.Uri import com.squareup.moshi.JsonClass import okhttp3.CipherSuite +import okhttp3.ConnectionSpec import okhttp3.TlsVersion import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig.Builder import org.matrix.android.sdk.internal.network.ssl.Fingerprint @@ -191,32 +192,25 @@ data class HomeServerConnectionConfig( /** * Convenient method to limit the TLS versions and cipher suites for this Builder * Ref: - * - https://www.ssi.gouv.fr/uploads/2017/02/security-recommendations-for-tls_v1.1.pdf + * - https://www.ssi.gouv.fr/uploads/2017/07/anssi-guide-recommandations_de_securite_relatives_a_tls-v1.2.pdf * - https://developer.android.com/reference/javax/net/ssl/SSLEngine * * @param tlsLimitations true to use Tls limitations * @param enableCompatibilityMode set to true for Android < 20 * @return this builder */ + @Deprecated("TLS versions and cipher suites are limited by default") fun withTlsLimitations(tlsLimitations: Boolean, enableCompatibilityMode: Boolean): Builder { if (tlsLimitations) { withShouldAcceptTlsExtensions(false) - // Tls versions - addAcceptedTlsVersion(TlsVersion.TLS_1_2) - addAcceptedTlsVersion(TlsVersion.TLS_1_3) + // TlS versions + ConnectionSpec.RESTRICTED_TLS.tlsVersions?.let { this.tlsVersions.addAll(it) } forceUsageOfTlsVersions(enableCompatibilityMode) // Cipher suites - addAcceptedTlsCipherSuite(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) - addAcceptedTlsCipherSuite(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256) - addAcceptedTlsCipherSuite(CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) - addAcceptedTlsCipherSuite(CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) - addAcceptedTlsCipherSuite(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384) - addAcceptedTlsCipherSuite(CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) - addAcceptedTlsCipherSuite(CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256) - addAcceptedTlsCipherSuite(CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256) + ConnectionSpec.RESTRICTED_TLS.cipherSuites?.let { this.tlsCipherSuites.addAll(it) } if (enableCompatibilityMode) { // Adopt some preceding cipher suites for Android < 20 to be able to negotiate diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/login/LoginWizard.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/login/LoginWizard.kt index a2a9373837..247d58ce79 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/login/LoginWizard.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/login/LoginWizard.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.api.auth.login import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.util.JsonDict /** * Set of methods to be able to login to an existing account on a homeserver. @@ -34,12 +35,14 @@ interface LoginWizard { * * @param login the login field. Can be a user name, or a msisdn (email or phone number) associated to the account * @param password the password of the account - * @param deviceName the initial device name + * @param initialDeviceName the initial device name + * @param deviceId the device id, optional. If not provided or null, the server will generate one. * @return a [Session] if the login is successful */ suspend fun login(login: String, password: String, - deviceName: String): Session + initialDeviceName: String, + deviceId: String? = null): Session /** * Exchange a login token to an access token. @@ -49,6 +52,12 @@ interface LoginWizard { */ suspend fun loginWithToken(loginToken: String): Session + /** + * Login to the homeserver by sending a custom JsonDict. + * The data should contain at least one entry "type" with a String value. + */ + suspend fun loginCustom(data: JsonDict): Session + /** * Ask the homeserver to reset the user password. The password will not be reset until * [resetPasswordMailConfirmed] is successfully called. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/query/QueryStringValue.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/query/QueryStringValue.kt index 8f83beface..31ec131c5c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/query/QueryStringValue.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/query/QueryStringValue.kt @@ -19,17 +19,41 @@ package org.matrix.android.sdk.api.query /** * Basic query language. All these cases are mutually exclusive. */ -sealed class QueryStringValue { - object NoCondition : QueryStringValue() - object IsNull : QueryStringValue() - object IsNotNull : QueryStringValue() - object IsEmpty : QueryStringValue() - object IsNotEmpty : QueryStringValue() - data class Equals(val string: String, val case: Case = Case.SENSITIVE) : QueryStringValue() - data class Contains(val string: String, val case: Case = Case.SENSITIVE) : QueryStringValue() +sealed interface QueryStringValue { + sealed interface ContentQueryStringValue : QueryStringValue { + val string: String + val case: Case + } + + object NoCondition : QueryStringValue + object IsNull : QueryStringValue + object IsNotNull : QueryStringValue + object IsEmpty : QueryStringValue + object IsNotEmpty : QueryStringValue + + data class Equals(override val string: String, override val case: Case = Case.SENSITIVE) : ContentQueryStringValue + data class Contains(override val string: String, override val case: Case = Case.SENSITIVE) : ContentQueryStringValue enum class Case { + /** + * Match query sensitive to case + */ SENSITIVE, - INSENSITIVE + + /** + * Match query insensitive to case, this only works for Latin-1 character sets + */ + INSENSITIVE, + + /** + * Match query with input normalized (case insensitive) + * Works around Realms inability to sort or filter by case for non Latin-1 character sets + * Expects the target field to contain normalized data + * + * @see org.matrix.android.sdk.internal.util.Normalizer.normalize + */ + NORMALIZED } } + +internal fun QueryStringValue.isNormalized() = this is QueryStringValue.ContentQueryStringValue && case == QueryStringValue.Case.NORMALIZED diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt index d4bfd4ee8c..dfe43aed6f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt @@ -120,9 +120,11 @@ interface Session : fun requireBackgroundSync() /** - * Launches infinite periodic background syncs - * This does not work in doze mode :/ - * If battery optimization is on it can work in app standby but that's all :/ + * Launches infinite self rescheduling background syncs via the WorkManager + * + * While dozing, syncs will only occur during maintenance windows + * For reliability it's recommended to also start a long running foreground service + * along with disabling battery optimizations */ fun startAutomaticBackgroundSync(timeOutInSeconds: Long, repeatDelayInSeconds: Long) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt index 169f90dbca..aad5fce33e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt @@ -22,6 +22,8 @@ import org.json.JSONObject import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.failure.MatrixError import org.matrix.android.sdk.api.session.crypto.MXCryptoError +import org.matrix.android.sdk.api.session.room.model.Membership +import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.session.room.model.message.MessageType import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent @@ -310,3 +312,6 @@ fun Event.isEdition(): Boolean { fun Event.getPresenceContent(): PresenceContent? { return content.toModel() } + +fun Event.isInvitation(): Boolean = type == EventType.STATE_ROOM_MEMBER && + content?.toModel()?.membership == Membership.INVITE diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt index d80faa729c..e4bd498990 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt @@ -94,13 +94,15 @@ interface RoomService { * Get a snapshot list of room summaries. * @return the immutable list of [RoomSummary] */ - fun getRoomSummaries(queryParams: RoomSummaryQueryParams): List + fun getRoomSummaries(queryParams: RoomSummaryQueryParams, + sortOrder: RoomSortOrder = RoomSortOrder.NONE): List /** * Get a live list of room summaries. This list is refreshed as soon as the data changes. * @return the [LiveData] of List[RoomSummary] */ - fun getRoomSummariesLive(queryParams: RoomSummaryQueryParams): LiveData> + fun getRoomSummariesLive(queryParams: RoomSummaryQueryParams, + sortOrder: RoomSortOrder = RoomSortOrder.ACTIVITY): LiveData> /** * Get a snapshot list of Breadcrumbs diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt index f40572518f..357c0b941a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt @@ -19,6 +19,7 @@ package org.matrix.android.sdk.api.session.space import android.net.Uri import androidx.lifecycle.LiveData import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.room.RoomSortOrder import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.internal.session.space.peeking.SpacePeekResult @@ -74,9 +75,11 @@ interface SpaceService { * Get a live list of space summaries. This list is refreshed as soon as the data changes. * @return the [LiveData] of List[SpaceSummary] */ - fun getSpaceSummariesLive(queryParams: SpaceSummaryQueryParams): LiveData> + fun getSpaceSummariesLive(queryParams: SpaceSummaryQueryParams, + sortOrder: RoomSortOrder = RoomSortOrder.NONE): LiveData> - fun getSpaceSummaries(spaceSummaryQueryParams: SpaceSummaryQueryParams): List + fun getSpaceSummaries(spaceSummaryQueryParams: SpaceSummaryQueryParams, + sortOrder: RoomSortOrder = RoomSortOrder.NONE): List suspend fun joinSpace(spaceIdOrAlias: String, reason: String? = null, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthAPI.kt index 50d9e5a06c..554e21ce55 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthAPI.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthAPI.kt @@ -121,6 +121,10 @@ internal interface AuthAPI { @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "login") suspend fun login(@Body loginParams: TokenLoginParams): Credentials + @Headers("CONNECT_TIMEOUT:60000", "READ_TIMEOUT:60000", "WRITE_TIMEOUT:60000") + @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "login") + suspend fun login(@Body loginParams: JsonDict): Credentials + /** * Ask the homeserver to reset the password associated with the provided email. */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt index 641a8f1bb6..8784d85c10 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt @@ -388,8 +388,15 @@ internal class DefaultAuthenticationService @Inject constructor( override suspend fun directAuthentication(homeServerConnectionConfig: HomeServerConnectionConfig, matrixId: String, password: String, - initialDeviceName: String): Session { - return directLoginTask.execute(DirectLoginTask.Params(homeServerConnectionConfig, matrixId, password, initialDeviceName)) + initialDeviceName: String, + deviceId: String?): Session { + return directLoginTask.execute(DirectLoginTask.Params( + homeServerConnectionConfig = homeServerConnectionConfig, + userId = matrixId, + password = password, + deviceName = initialDeviceName, + deviceId = deviceId + )) } private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt index d4b14f1ca9..5be480f633 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt @@ -49,51 +49,54 @@ internal data class PasswordLoginParams( fun userIdentifier(user: String, password: String, - deviceDisplayName: String? = null, - deviceId: String? = null): PasswordLoginParams { + deviceDisplayName: String?, + deviceId: String?): PasswordLoginParams { return PasswordLoginParams( - mapOf( + identifier = mapOf( IDENTIFIER_KEY_TYPE to IDENTIFIER_KEY_TYPE_USER, IDENTIFIER_KEY_USER to user ), - password, - LoginFlowTypes.PASSWORD, - deviceDisplayName, - deviceId) + password = password, + type = LoginFlowTypes.PASSWORD, + deviceDisplayName = deviceDisplayName, + deviceId = deviceId + ) } fun thirdPartyIdentifier(medium: String, address: String, password: String, - deviceDisplayName: String? = null, - deviceId: String? = null): PasswordLoginParams { + deviceDisplayName: String?, + deviceId: String?): PasswordLoginParams { return PasswordLoginParams( - mapOf( + identifier = mapOf( IDENTIFIER_KEY_TYPE to IDENTIFIER_KEY_TYPE_THIRD_PARTY, IDENTIFIER_KEY_MEDIUM to medium, IDENTIFIER_KEY_ADDRESS to address ), - password, - LoginFlowTypes.PASSWORD, - deviceDisplayName, - deviceId) + password = password, + type = LoginFlowTypes.PASSWORD, + deviceDisplayName = deviceDisplayName, + deviceId = deviceId + ) } fun phoneIdentifier(country: String, phone: String, password: String, - deviceDisplayName: String? = null, - deviceId: String? = null): PasswordLoginParams { + deviceDisplayName: String?, + deviceId: String?): PasswordLoginParams { return PasswordLoginParams( - mapOf( + identifier = mapOf( IDENTIFIER_KEY_TYPE to IDENTIFIER_KEY_TYPE_PHONE, IDENTIFIER_KEY_COUNTRY to country, IDENTIFIER_KEY_PHONE to phone ), - password, - LoginFlowTypes.PASSWORD, - deviceDisplayName, - deviceId) + password = password, + type = LoginFlowTypes.PASSWORD, + deviceDisplayName = deviceDisplayName, + deviceId = deviceId + ) } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DefaultLoginWizard.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DefaultLoginWizard.kt index 854caf8a62..b72cff3cf1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DefaultLoginWizard.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DefaultLoginWizard.kt @@ -21,6 +21,7 @@ import org.matrix.android.sdk.api.auth.login.LoginProfileInfo import org.matrix.android.sdk.api.auth.login.LoginWizard import org.matrix.android.sdk.api.auth.registration.RegisterThreePid import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.internal.auth.AuthAPI import org.matrix.android.sdk.internal.auth.PendingSessionStore import org.matrix.android.sdk.internal.auth.SessionCreator @@ -52,11 +53,23 @@ internal class DefaultLoginWizard( override suspend fun login(login: String, password: String, - deviceName: String): Session { + initialDeviceName: String, + deviceId: String?): Session { val loginParams = if (Patterns.EMAIL_ADDRESS.matcher(login).matches()) { - PasswordLoginParams.thirdPartyIdentifier(ThreePidMedium.EMAIL, login, password, deviceName) + PasswordLoginParams.thirdPartyIdentifier( + medium = ThreePidMedium.EMAIL, + address = login, + password = password, + deviceDisplayName = initialDeviceName, + deviceId = deviceId + ) } else { - PasswordLoginParams.userIdentifier(login, password, deviceName) + PasswordLoginParams.userIdentifier( + user = login, + password = password, + deviceDisplayName = initialDeviceName, + deviceId = deviceId + ) } val credentials = executeRequest(null) { authAPI.login(loginParams) @@ -79,6 +92,14 @@ internal class DefaultLoginWizard( return sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig) } + override suspend fun loginCustom(data: JsonDict): Session { + val credentials = executeRequest(null) { + authAPI.login(data) + } + + return sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig) + } + override suspend fun resetPassword(email: String, newPassword: String) { val param = RegisterAddThreePidTask.Params( RegisterThreePid.Email(email), diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DirectLoginTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DirectLoginTask.kt index 8f61afe374..28706c7e80 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DirectLoginTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DirectLoginTask.kt @@ -37,7 +37,8 @@ internal interface DirectLoginTask : Task { val homeServerConnectionConfig: HomeServerConnectionConfig, val userId: String, val password: String, - val deviceName: String + val deviceName: String, + val deviceId: String? ) } @@ -55,7 +56,12 @@ internal class DefaultDirectLoginTask @Inject constructor( val authAPI = retrofitFactory.create(client, homeServerUrl) .create(AuthAPI::class.java) - val loginParams = PasswordLoginParams.userIdentifier(params.userId, params.password, params.deviceName) + val loginParams = PasswordLoginParams.userIdentifier( + user = params.userId, + password = params.password, + deviceDisplayName = params.deviceName, + deviceId = params.deviceId + ) val credentials = try { executeRequest(null) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendEventTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendEventTask.kt index e1e297767b..e40db6af67 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendEventTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendEventTask.kt @@ -34,7 +34,7 @@ internal interface SendEventTask : Task { internal class DefaultSendEventTask @Inject constructor( private val localEchoRepository: LocalEchoRepository, - private val encryptEventTask: DefaultEncryptEventTask, + private val encryptEventTask: EncryptEventTask, private val loadRoomMembersTask: LoadRoomMembersTask, private val roomAPI: RoomAPI, private val globalErrorReceiver: GlobalErrorReceiver) : SendEventTask { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendVerificationMessageTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendVerificationMessageTask.kt index 7fa48c3da1..c4a6ba27d6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendVerificationMessageTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendVerificationMessageTask.kt @@ -34,7 +34,7 @@ internal interface SendVerificationMessageTask : Task 19") + realm.schema.get("RoomSummaryEntity") + ?.addField(RoomSummaryEntityFields.NORMALIZED_DISPLAY_NAME, String::class.java) + ?.transform { + it.getString(RoomSummaryEntityFields.DISPLAY_NAME)?.let { displayName -> + val normalised = normalizer.normalize(displayName) + it.set(RoomSummaryEntityFields.NORMALIZED_DISPLAY_NAME, normalised) + } + } + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/SessionRealmConfigurationFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/SessionRealmConfigurationFactory.kt index 1771c5b202..04ca26a943 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/SessionRealmConfigurationFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/SessionRealmConfigurationFactory.kt @@ -40,6 +40,7 @@ private const val REALM_NAME = "disk_store.realm" */ internal class SessionRealmConfigurationFactory @Inject constructor( private val realmKeysUtils: RealmKeysUtils, + private val realmSessionStoreMigration: RealmSessionStoreMigration, @SessionFilesDirectory val directory: File, @SessionId val sessionId: String, @UserMd5 val userMd5: String, @@ -71,7 +72,7 @@ internal class SessionRealmConfigurationFactory @Inject constructor( .allowWritesOnUiThread(true) .modules(SessionRealmModule()) .schemaVersion(RealmSessionStoreMigration.SESSION_STORE_SCHEMA_VERSION) - .migration(RealmSessionStoreMigration) + .migration(realmSessionStoreMigration) .build() // Try creating a realm instance and if it succeeds we can clear the flag diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomSummaryMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomSummaryMapper.kt index 5900ef6b76..3a15e0acf0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomSummaryMapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomSummaryMapper.kt @@ -21,13 +21,13 @@ import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo import org.matrix.android.sdk.api.session.room.model.SpaceParentInfo import org.matrix.android.sdk.api.session.room.model.tag.RoomTag +import org.matrix.android.sdk.api.session.typing.TypingUsersTracker import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.model.presence.toUserPresence -import org.matrix.android.sdk.internal.session.typing.DefaultTypingUsersTracker import javax.inject.Inject internal class RoomSummaryMapper @Inject constructor(private val timelineEventMapper: TimelineEventMapper, - private val typingUsersTracker: DefaultTypingUsersTracker) { + private val typingUsersTracker: TypingUsersTracker) { fun map(roomSummaryEntity: RoomSummaryEntity): RoomSummary { val tags = roomSummaryEntity.tags().map { @@ -42,7 +42,7 @@ internal class RoomSummaryMapper @Inject constructor(private val timelineEventMa return RoomSummary( roomId = roomSummaryEntity.roomId, - displayName = roomSummaryEntity.displayName ?: "", + displayName = roomSummaryEntity.displayName() ?: "", name = roomSummaryEntity.name ?: "", topic = roomSummaryEntity.topic ?: "", avatarUrl = roomSummaryEntity.avatarUrl ?: "", diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt index 88b8886936..67672f03ad 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt @@ -28,6 +28,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.VersioningState import org.matrix.android.sdk.api.session.room.model.tag.RoomTag import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntity +import org.matrix.android.sdk.internal.session.room.membership.RoomName internal open class RoomSummaryEntity( @PrimaryKey var roomId: String = "", @@ -36,10 +37,24 @@ internal open class RoomSummaryEntity( var children: RealmList = RealmList() ) : RealmObject() { - var displayName: String? = "" - set(value) { - if (value != field) field = value + private var displayName: String? = "" + + fun displayName() = displayName + + fun setDisplayName(roomName: RoomName) { + if (roomName.name != displayName) { + displayName = roomName.name + normalizedDisplayName = roomName.normalizedName } + } + + /** + * Workaround for Realm only supporting Latin-1 character sets when sorting + * or filtering by case + * See https://github.com/realm/realm-core/issues/777 + */ + private var normalizedDisplayName: String? = "" + var avatarUrl: String? = "" set(value) { if (value != field) field = value @@ -284,5 +299,6 @@ internal open class RoomSummaryEntity( roomEncryptionTrustLevelStr = value?.name } } + companion object } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/NetworkModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/NetworkModule.kt index fa59b94c17..ad34a4d8a6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/NetworkModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/NetworkModule.kt @@ -20,6 +20,7 @@ import com.facebook.stetho.okhttp3.StethoInterceptor import com.squareup.moshi.Moshi import dagger.Module import dagger.Provides +import okhttp3.ConnectionSpec import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import org.matrix.android.sdk.BuildConfig @@ -29,6 +30,7 @@ import org.matrix.android.sdk.internal.network.TimeOutInterceptor import org.matrix.android.sdk.internal.network.UserAgentInterceptor import org.matrix.android.sdk.internal.network.interceptors.CurlLoggingInterceptor import org.matrix.android.sdk.internal.network.interceptors.FormattedJsonHttpLogger +import java.util.Collections import java.util.concurrent.TimeUnit @Module @@ -66,6 +68,8 @@ internal object NetworkModule { httpLoggingInterceptor: HttpLoggingInterceptor, curlLoggingInterceptor: CurlLoggingInterceptor, apiInterceptor: ApiInterceptor): OkHttpClient { + val spec = ConnectionSpec.Builder(matrixConfiguration.connectionSpec).build() + return OkHttpClient.Builder() .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(60, TimeUnit.SECONDS) @@ -87,6 +91,7 @@ internal object NetworkModule { proxy(it) } } + .connectionSpecs(Collections.singletonList(spec)) .build() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ssl/CertUtil.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ssl/CertUtil.kt index 92c7f3f236..d8bdc5fc2b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ssl/CertUtil.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ssl/CertUtil.kt @@ -177,15 +177,13 @@ internal object CertUtil { val trustPinned = arrayOf(PinnedTrustManagerProvider.provide(hsConfig.allowedFingerprints, defaultTrustManager)) - val sslSocketFactory: SSLSocketFactory - - if (hsConfig.forceUsageTlsVersions && hsConfig.tlsVersions != null) { + val sslSocketFactory = if (hsConfig.forceUsageTlsVersions && !hsConfig.tlsVersions.isNullOrEmpty()) { // Force usage of accepted Tls Versions for Android < 20 - sslSocketFactory = TLSSocketFactory(trustPinned, hsConfig.tlsVersions) + TLSSocketFactory(trustPinned, hsConfig.tlsVersions) } else { val sslContext = SSLContext.getInstance("TLS") sslContext.init(null, trustPinned, java.security.SecureRandom()) - sslSocketFactory = sslContext.socketFactory + sslContext.socketFactory } return PinnedSSLSocketFactory(sslSocketFactory, defaultTrustManager!!) @@ -237,14 +235,14 @@ internal object CertUtil { * @return a list of accepted TLS specifications. */ fun newConnectionSpecs(hsConfig: HomeServerConnectionConfig): List { - val builder = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) + val builder = ConnectionSpec.Builder(ConnectionSpec.RESTRICTED_TLS) val tlsVersions = hsConfig.tlsVersions - if (null != tlsVersions && tlsVersions.isNotEmpty()) { + if (!tlsVersions.isNullOrEmpty()) { builder.tlsVersions(*tlsVersions.toTypedArray()) } val tlsCipherSuites = hsConfig.tlsCipherSuites - if (null != tlsCipherSuites && tlsCipherSuites.isNotEmpty()) { + if (!tlsCipherSuites.isNullOrEmpty()) { builder.cipherSuites(*tlsCipherSuites.toTypedArray()) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/query/QueryStringValueProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/query/QueryStringValueProcessor.kt index fd33682231..b42bf2b8c7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/query/QueryStringValueProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/query/QueryStringValueProcessor.kt @@ -20,24 +20,41 @@ import io.realm.Case import io.realm.RealmObject import io.realm.RealmQuery import org.matrix.android.sdk.api.query.QueryStringValue -import timber.log.Timber +import org.matrix.android.sdk.api.query.QueryStringValue.ContentQueryStringValue +import org.matrix.android.sdk.internal.util.Normalizer +import javax.inject.Inject -fun RealmQuery.process(field: String, queryStringValue: QueryStringValue): RealmQuery { - when (queryStringValue) { - is QueryStringValue.NoCondition -> Timber.v("No condition to process") - is QueryStringValue.IsNotNull -> isNotNull(field) - is QueryStringValue.IsNull -> isNull(field) - is QueryStringValue.IsEmpty -> isEmpty(field) - is QueryStringValue.IsNotEmpty -> isNotEmpty(field) - is QueryStringValue.Equals -> equalTo(field, queryStringValue.string, queryStringValue.case.toRealmCase()) - is QueryStringValue.Contains -> contains(field, queryStringValue.string, queryStringValue.case.toRealmCase()) +class QueryStringValueProcessor @Inject constructor( + private val normalizer: Normalizer +) { + + fun RealmQuery.process(field: String, queryStringValue: QueryStringValue): RealmQuery { + return when (queryStringValue) { + is QueryStringValue.NoCondition -> this + is QueryStringValue.IsNotNull -> isNotNull(field) + is QueryStringValue.IsNull -> isNull(field) + is QueryStringValue.IsEmpty -> isEmpty(field) + is QueryStringValue.IsNotEmpty -> isNotEmpty(field) + is ContentQueryStringValue -> when (queryStringValue) { + is QueryStringValue.Equals -> equalTo(field, queryStringValue.toRealmValue(), queryStringValue.case.toRealmCase()) + is QueryStringValue.Contains -> contains(field, queryStringValue.toRealmValue(), queryStringValue.case.toRealmCase()) + } + } + } + + private fun ContentQueryStringValue.toRealmValue(): String { + return when (case) { + QueryStringValue.Case.NORMALIZED -> normalizer.normalize(string) + QueryStringValue.Case.SENSITIVE, + QueryStringValue.Case.INSENSITIVE -> string + } } - return this } private fun QueryStringValue.Case.toRealmCase(): Case { return when (this) { QueryStringValue.Case.INSENSITIVE -> Case.INSENSITIVE - QueryStringValue.Case.SENSITIVE -> Case.SENSITIVE + QueryStringValue.Case.SENSITIVE, + QueryStringValue.Case.NORMALIZED -> Case.SENSITIVE } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt index 6a6bce5ce2..c52462612a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt @@ -41,6 +41,7 @@ import org.matrix.android.sdk.api.session.file.ContentDownloadStateTracker import org.matrix.android.sdk.api.session.file.FileService import org.matrix.android.sdk.api.session.group.GroupService import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService +import org.matrix.android.sdk.api.session.identity.IdentityService import org.matrix.android.sdk.api.session.initsync.SyncStatusService import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService import org.matrix.android.sdk.api.session.media.MediaService @@ -72,7 +73,6 @@ import org.matrix.android.sdk.internal.di.SessionId import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificate import org.matrix.android.sdk.internal.di.WorkManagerProvider import org.matrix.android.sdk.internal.network.GlobalErrorHandler -import org.matrix.android.sdk.internal.session.identity.DefaultIdentityService import org.matrix.android.sdk.internal.session.sync.SyncTokenStore import org.matrix.android.sdk.internal.session.sync.job.SyncThread import org.matrix.android.sdk.internal.session.sync.job.SyncWorker @@ -124,7 +124,7 @@ internal class DefaultSession @Inject constructor( private val _sharedSecretStorageService: Lazy, private val accountService: Lazy, private val eventService: Lazy, - private val defaultIdentityService: DefaultIdentityService, + private val identityService: IdentityService, private val integrationManagerService: IntegrationManagerService, private val thirdPartyService: Lazy, private val callSignalingService: Lazy, @@ -275,7 +275,7 @@ internal class DefaultSession @Inject constructor( override fun cryptoService(): CryptoService = cryptoService.get() - override fun identityService() = defaultIdentityService + override fun identityService() = identityService override fun fileService(): FileService = defaultFileService.get() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt index dc59277f64..ebc2176a13 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt @@ -163,6 +163,7 @@ internal abstract class SessionModule { @JvmStatic @Provides @SessionFilesDirectory + @SessionScope fun providesFilesDir(@UserMd5 userMd5: String, @SessionId sessionId: String, context: Context): File { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/FileUploader.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/FileUploader.kt index bdebb0addf..1b0ccbb489 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/FileUploader.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/FileUploader.kt @@ -35,12 +35,12 @@ import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.MatrixError import org.matrix.android.sdk.api.session.content.ContentUrlResolver import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities +import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService import org.matrix.android.sdk.internal.di.Authenticated import org.matrix.android.sdk.internal.network.GlobalErrorReceiver import org.matrix.android.sdk.internal.network.ProgressRequestBody import org.matrix.android.sdk.internal.network.awaitResponse import org.matrix.android.sdk.internal.network.toFailure -import org.matrix.android.sdk.internal.session.homeserver.DefaultHomeServerCapabilitiesService import org.matrix.android.sdk.internal.util.TemporaryFileCreator import java.io.File import java.io.FileNotFoundException @@ -50,7 +50,7 @@ import javax.inject.Inject internal class FileUploader @Inject constructor( @Authenticated private val okHttpClient: OkHttpClient, private val globalErrorReceiver: GlobalErrorReceiver, - private val homeServerCapabilitiesService: DefaultHomeServerCapabilitiesService, + private val homeServerCapabilitiesService: HomeServerCapabilitiesService, private val context: Context, private val temporaryFileCreator: TemporaryFileCreator, contentUrlResolver: ContentUrlResolver, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt index 0c5a90ca60..b657d950bd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt @@ -214,8 +214,11 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter .also { filesToDelete.add(it) } } VideoCompressionResult.CompressionNotNeeded, - VideoCompressionResult.CompressionCancelled, + VideoCompressionResult.CompressionCancelled -> { + workingFile + } is VideoCompressionResult.CompressionFailed -> { + Timber.e(videoCompressionResult.failure, "Video compression failed") workingFile } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/VideoCompressor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/VideoCompressor.kt index 05aaf4e9f1..a43f8abf33 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/VideoCompressor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/VideoCompressor.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.session.content import com.otaliastudios.transcoder.Transcoder import com.otaliastudios.transcoder.TranscoderListener +import com.otaliastudios.transcoder.source.FilePathDataSource import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.withContext @@ -43,7 +44,16 @@ internal class VideoCompressor @Inject constructor( var result: Int = -1 var failure: Throwable? = null Transcoder.into(destinationFile.path) - .addDataSource(videoFile.path) + .addDataSource(object : FilePathDataSource(videoFile.path) { + // https://github.com/natario1/Transcoder/issues/154 + @Suppress("SENSELESS_COMPARISON") // Source is annotated as @NonNull, but can actually be null... + override fun isInitialized(): Boolean { + if (source == null) { + return false + } + return super.isInitialized() + } + }) .setListener(object : TranscoderListener { override fun onTranscodeProgress(progress: Double) { Timber.d("Compressing: $progress%") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/DefaultGroupService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/DefaultGroupService.kt index 8dc5f3931d..9334d09377 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/DefaultGroupService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/DefaultGroupService.kt @@ -30,12 +30,16 @@ import org.matrix.android.sdk.internal.database.model.GroupSummaryEntity import org.matrix.android.sdk.internal.database.model.GroupSummaryEntityFields import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.SessionDatabase +import org.matrix.android.sdk.internal.query.QueryStringValueProcessor import org.matrix.android.sdk.internal.query.process import org.matrix.android.sdk.internal.util.fetchCopyMap import javax.inject.Inject -internal class DefaultGroupService @Inject constructor(@SessionDatabase private val monarchy: Monarchy, - private val groupFactory: GroupFactory) : GroupService { +internal class DefaultGroupService @Inject constructor( + @SessionDatabase private val monarchy: Monarchy, + private val groupFactory: GroupFactory, + private val queryStringValueProcessor: QueryStringValueProcessor, +) : GroupService { override fun getGroup(groupId: String): Group? { return Realm.getInstance(monarchy.realmConfiguration).use { realm -> @@ -67,8 +71,10 @@ internal class DefaultGroupService @Inject constructor(@SessionDatabase private } private fun groupSummariesQuery(realm: Realm, queryParams: GroupSummaryQueryParams): RealmQuery { - return GroupSummaryEntity.where(realm) - .process(GroupSummaryEntityFields.DISPLAY_NAME, queryParams.displayName) - .process(GroupSummaryEntityFields.MEMBERSHIP_STR, queryParams.memberships) + return with(queryStringValueProcessor) { + GroupSummaryEntity.where(realm) + .process(GroupSummaryEntityFields.DISPLAY_NAME, queryParams.displayName) + .process(GroupSummaryEntityFields.MEMBERSHIP_STR, queryParams.memberships) + } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt index 37d9a4e74f..c8a9c0f09a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt @@ -80,7 +80,7 @@ internal class DefaultIdentityService @Inject constructor( private val identityApiProvider: IdentityApiProvider, private val accountDataDataSource: UserAccountDataDataSource, private val homeServerCapabilitiesService: HomeServerCapabilitiesService, - private val sign3pidInvitationTask: DefaultSign3pidInvitationTask, + private val sign3pidInvitationTask: Sign3pidInvitationTask, private val sessionParams: SessionParams ) : IdentityService, SessionLifecycleObserver { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityModule.kt index 19e602d7a7..65794e6b14 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityModule.kt @@ -21,6 +21,7 @@ import dagger.Module import dagger.Provides import io.realm.RealmConfiguration import okhttp3.OkHttpClient +import org.matrix.android.sdk.api.session.identity.IdentityService import org.matrix.android.sdk.internal.database.RealmKeysUtils import org.matrix.android.sdk.internal.di.AuthenticatedIdentity import org.matrix.android.sdk.internal.di.IdentityDatabase @@ -75,6 +76,9 @@ internal abstract class IdentityModule { } } + @Binds + abstract fun bindIdentityService(service: DefaultIdentityService): IdentityService + @Binds @AuthenticatedIdentity abstract fun bindAccessTokenProvider(provider: IdentityAccessTokenProvider): AccessTokenProvider diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt index 1321f8dd62..3c74888eda 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.session.notification import org.matrix.android.sdk.api.pushrules.rest.PushRule import org.matrix.android.sdk.api.session.events.model.EventType +import org.matrix.android.sdk.api.session.events.model.isInvitation import org.matrix.android.sdk.api.session.sync.model.RoomsSyncResponse import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.task.Task @@ -48,14 +49,18 @@ internal class DefaultProcessEventForPushTask @Inject constructor( } val newJoinEvents = params.syncResponse.join .mapNotNull { (key, value) -> - value.timeline?.events?.map { it.copy(roomId = key) } + value.timeline?.events?.mapNotNull { + it.takeIf { !it.isInvitation() }?.copy(roomId = key) + } } .flatten() + val inviteEvents = params.syncResponse.invite .mapNotNull { (key, value) -> value.inviteState?.events?.map { it.copy(roomId = key) } } .flatten() + val allEvents = (newJoinEvents + inviteEvents).filter { event -> when (event.type) { EventType.MESSAGE, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt index 5b2499c130..7ca64aa66a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt @@ -85,12 +85,14 @@ internal class DefaultRoomService @Inject constructor( return roomSummaryDataSource.getRoomSummary(roomIdOrAlias) } - override fun getRoomSummaries(queryParams: RoomSummaryQueryParams): List { - return roomSummaryDataSource.getRoomSummaries(queryParams) + override fun getRoomSummaries(queryParams: RoomSummaryQueryParams, + sortOrder: RoomSortOrder): List { + return roomSummaryDataSource.getRoomSummaries(queryParams, sortOrder) } - override fun getRoomSummariesLive(queryParams: RoomSummaryQueryParams): LiveData> { - return roomSummaryDataSource.getRoomSummariesLive(queryParams) + override fun getRoomSummariesLive(queryParams: RoomSummaryQueryParams, + sortOrder: RoomSortOrder): LiveData> { + return roomSummaryDataSource.getRoomSummariesLive(queryParams, sortOrder) } override fun getPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt index 204deb72b4..6cf82dde44 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt @@ -33,6 +33,7 @@ import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.di.UserId +import org.matrix.android.sdk.internal.query.QueryStringValueProcessor import org.matrix.android.sdk.internal.query.process import org.matrix.android.sdk.internal.session.room.membership.admin.MembershipAdminTask import org.matrix.android.sdk.internal.session.room.membership.joining.InviteTask @@ -51,7 +52,8 @@ internal class DefaultMembershipService @AssistedInject constructor( private val leaveRoomTask: LeaveRoomTask, private val membershipAdminTask: MembershipAdminTask, @UserId - private val userId: String + private val userId: String, + private val queryStringValueProcessor: QueryStringValueProcessor ) : MembershipService { @AssistedFactory @@ -94,15 +96,17 @@ internal class DefaultMembershipService @AssistedInject constructor( } private fun roomMembersQuery(realm: Realm, queryParams: RoomMemberQueryParams): RealmQuery { - return RoomMemberHelper(realm, roomId).queryRoomMembersEvent() - .process(RoomMemberSummaryEntityFields.USER_ID, queryParams.userId) - .process(RoomMemberSummaryEntityFields.MEMBERSHIP_STR, queryParams.memberships) - .process(RoomMemberSummaryEntityFields.DISPLAY_NAME, queryParams.displayName) - .apply { - if (queryParams.excludeSelf) { - notEqualTo(RoomMemberSummaryEntityFields.USER_ID, userId) + return with(queryStringValueProcessor) { + RoomMemberHelper(realm, roomId).queryRoomMembersEvent() + .process(RoomMemberSummaryEntityFields.USER_ID, queryParams.userId) + .process(RoomMemberSummaryEntityFields.MEMBERSHIP_STR, queryParams.memberships) + .process(RoomMemberSummaryEntityFields.DISPLAY_NAME, queryParams.displayName) + .apply { + if (queryParams.excludeSelf) { + notEqualTo(RoomMemberSummaryEntityFields.USER_ID, userId) + } } - } + } } override fun getNumberOfJoinedMembers(): Int { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt index 5e77dd157a..bd9f2ecc36 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt @@ -34,6 +34,7 @@ import org.matrix.android.sdk.internal.database.query.getOrNull import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.session.displayname.DisplayNameResolver +import org.matrix.android.sdk.internal.util.Normalizer import javax.inject.Inject /** @@ -42,6 +43,7 @@ import javax.inject.Inject internal class RoomDisplayNameResolver @Inject constructor( matrixConfiguration: MatrixConfiguration, private val displayNameResolver: DisplayNameResolver, + private val normalizer: Normalizer, @UserId private val userId: String ) { @@ -54,7 +56,7 @@ internal class RoomDisplayNameResolver @Inject constructor( * @param roomId: the roomId to resolve the name of. * @return the room display name */ - fun resolve(realm: Realm, roomId: String): String { + fun resolve(realm: Realm, roomId: String): RoomName { // this algorithm is the one defined in // https://github.com/matrix-org/matrix-js-sdk/blob/develop/lib/models/room.js#L617 // calculateRoomName(room, userId) @@ -66,12 +68,12 @@ internal class RoomDisplayNameResolver @Inject constructor( val roomName = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_NAME, stateKey = "")?.root name = ContentMapper.map(roomName?.content).toModel()?.name if (!name.isNullOrEmpty()) { - return name + return name.toRoomName() } val canonicalAlias = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_CANONICAL_ALIAS, stateKey = "")?.root name = ContentMapper.map(canonicalAlias?.content).toModel()?.canonicalAlias if (!name.isNullOrEmpty()) { - return name + return name.toRoomName() } val roomMembers = RoomMemberHelper(realm, roomId) @@ -152,7 +154,7 @@ internal class RoomDisplayNameResolver @Inject constructor( } } } - return name ?: roomId + return (name ?: roomId).toRoomName() } /** See [org.matrix.android.sdk.api.session.room.sender.SenderInfo.disambiguatedDisplayName] */ @@ -165,4 +167,8 @@ internal class RoomDisplayNameResolver @Inject constructor( "${roomMemberSummary.displayName} (${roomMemberSummary.userId})" } } + + private fun String.toRoomName() = RoomName(this, normalizedName = normalizer.normalize(this)) } + +internal data class RoomName(val name: String, val normalizedName: String) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/StateEventDataSource.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/StateEventDataSource.kt index a25a362bfa..2114b9c590 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/StateEventDataSource.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/StateEventDataSource.kt @@ -31,11 +31,15 @@ import org.matrix.android.sdk.internal.database.mapper.asDomain import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntityFields import org.matrix.android.sdk.internal.di.SessionDatabase +import org.matrix.android.sdk.internal.query.QueryStringValueProcessor import org.matrix.android.sdk.internal.query.process import javax.inject.Inject -internal class StateEventDataSource @Inject constructor(@SessionDatabase private val monarchy: Monarchy, - private val realmSessionProvider: RealmSessionProvider) { +internal class StateEventDataSource @Inject constructor( + @SessionDatabase private val monarchy: Monarchy, + private val realmSessionProvider: RealmSessionProvider, + private val queryStringValueProcessor: QueryStringValueProcessor +) { fun getStateEvent(roomId: String, eventType: String, stateKey: QueryStringValue): Event? { return realmSessionProvider.withRealm { realm -> @@ -78,13 +82,15 @@ internal class StateEventDataSource @Inject constructor(@SessionDatabase private eventTypes: Set, stateKey: QueryStringValue ): RealmQuery { - return realm.where() - .equalTo(CurrentStateEventEntityFields.ROOM_ID, roomId) - .apply { - if (eventTypes.isNotEmpty()) { - `in`(CurrentStateEventEntityFields.TYPE, eventTypes.toTypedArray()) + return with(queryStringValueProcessor) { + realm.where() + .equalTo(CurrentStateEventEntityFields.ROOM_ID, roomId) + .apply { + if (eventTypes.isNotEmpty()) { + `in`(CurrentStateEventEntityFields.TYPE, eventTypes.toTypedArray()) + } } - } - .process(CurrentStateEventEntityFields.STATE_KEY, stateKey) + .process(CurrentStateEventEntityFields.STATE_KEY, stateKey) + } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryDataSource.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryDataSource.kt index 0b8c6df806..c9fc3c9575 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryDataSource.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryDataSource.kt @@ -25,10 +25,10 @@ import androidx.paging.PagedList import com.zhuinden.monarchy.Monarchy import io.realm.Realm import io.realm.RealmQuery -import io.realm.Sort import io.realm.kotlin.where import org.matrix.android.sdk.api.query.ActiveSpaceFilter import org.matrix.android.sdk.api.query.RoomCategoryFilter +import org.matrix.android.sdk.api.query.isNormalized import org.matrix.android.sdk.api.session.room.ResultBoundaries import org.matrix.android.sdk.api.session.room.RoomSortOrder import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams @@ -48,12 +48,16 @@ import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields import org.matrix.android.sdk.internal.database.query.findByAlias import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.SessionDatabase +import org.matrix.android.sdk.internal.query.QueryStringValueProcessor import org.matrix.android.sdk.internal.query.process import org.matrix.android.sdk.internal.util.fetchCopyMap import javax.inject.Inject -internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase private val monarchy: Monarchy, - private val roomSummaryMapper: RoomSummaryMapper) { +internal class RoomSummaryDataSource @Inject constructor( + @SessionDatabase private val monarchy: Monarchy, + private val roomSummaryMapper: RoomSummaryMapper, + private val queryStringValueProcessor: QueryStringValueProcessor +) { fun getRoomSummary(roomIdOrAlias: String): RoomSummary? { return monarchy @@ -80,25 +84,27 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat } } - fun getRoomSummaries(queryParams: RoomSummaryQueryParams): List { + fun getRoomSummaries(queryParams: RoomSummaryQueryParams, + sortOrder: RoomSortOrder = RoomSortOrder.NONE): List { return monarchy.fetchAllMappedSync( - { roomSummariesQuery(it, queryParams) }, + { roomSummariesQuery(it, queryParams).process(sortOrder) }, { roomSummaryMapper.map(it) } ) } - fun getRoomSummariesLive(queryParams: RoomSummaryQueryParams): LiveData> { + fun getRoomSummariesLive(queryParams: RoomSummaryQueryParams, + sortOrder: RoomSortOrder = RoomSortOrder.NONE): LiveData> { return monarchy.findAllMappedWithChanges( { - roomSummariesQuery(it, queryParams) - .sort(RoomSummaryEntityFields.LAST_ACTIVITY_TIME, Sort.DESCENDING) + roomSummariesQuery(it, queryParams).process(sortOrder) }, { roomSummaryMapper.map(it) } ) } - fun getSpaceSummariesLive(queryParams: SpaceSummaryQueryParams): LiveData> { - return getRoomSummariesLive(queryParams) + fun getSpaceSummariesLive(queryParams: SpaceSummaryQueryParams, + sortOrder: RoomSortOrder = RoomSortOrder.NONE): LiveData> { + return getRoomSummariesLive(queryParams, sortOrder) } fun getSpaceSummary(roomIdOrAlias: String): RoomSummary? { @@ -122,8 +128,9 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat } } - fun getSpaceSummaries(spaceSummaryQueryParams: SpaceSummaryQueryParams): List { - return getRoomSummaries(spaceSummaryQueryParams) + fun getSpaceSummaries(spaceSummaryQueryParams: SpaceSummaryQueryParams, + sortOrder: RoomSortOrder = RoomSortOrder.NONE): List { + return getRoomSummaries(spaceSummaryQueryParams, sortOrder) } fun getRootSpaceSummaries(): List { @@ -238,12 +245,20 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat } private fun roomSummariesQuery(realm: Realm, queryParams: RoomSummaryQueryParams): RealmQuery { - val query = RoomSummaryEntity.where(realm) - query.process(RoomSummaryEntityFields.ROOM_ID, queryParams.roomId) - query.process(RoomSummaryEntityFields.DISPLAY_NAME, queryParams.displayName) - query.process(RoomSummaryEntityFields.CANONICAL_ALIAS, queryParams.canonicalAlias) - query.process(RoomSummaryEntityFields.MEMBERSHIP_STR, queryParams.memberships) - query.equalTo(RoomSummaryEntityFields.IS_HIDDEN_FROM_USER, false) + val query = with(queryStringValueProcessor) { + RoomSummaryEntity.where(realm) + .process(RoomSummaryEntityFields.ROOM_ID, queryParams.roomId) + .let { + if (queryParams.displayName.isNormalized()) { + it.process(RoomSummaryEntityFields.NORMALIZED_DISPLAY_NAME, queryParams.displayName) + } else { + it.process(RoomSummaryEntityFields.DISPLAY_NAME, queryParams.displayName) + } + } + .process(RoomSummaryEntityFields.CANONICAL_ALIAS, queryParams.canonicalAlias) + .process(RoomSummaryEntityFields.MEMBERSHIP_STR, queryParams.memberships) + .equalTo(RoomSummaryEntityFields.IS_HIDDEN_FROM_USER, false) + } queryParams.roomCategoryFilter?.let { when (it) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt index 30014f4539..3556cabb33 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt @@ -65,6 +65,7 @@ import org.matrix.android.sdk.internal.session.room.accountdata.RoomAccountDataD import org.matrix.android.sdk.internal.session.room.membership.RoomDisplayNameResolver import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper import org.matrix.android.sdk.internal.session.room.relationship.RoomChildRelationInfo +import org.matrix.android.sdk.internal.util.Normalizer import timber.log.Timber import javax.inject.Inject import kotlin.system.measureTimeMillis @@ -75,7 +76,8 @@ internal class RoomSummaryUpdater @Inject constructor( private val roomAvatarResolver: RoomAvatarResolver, private val eventDecryptor: EventDecryptor, private val crossSigningService: DefaultCrossSigningService, - private val roomAccountDataDataSource: RoomAccountDataDataSource) { + private val roomAccountDataDataSource: RoomAccountDataDataSource, + private val normalizer: Normalizer) { fun update(realm: Realm, roomId: String, @@ -136,7 +138,7 @@ internal class RoomSummaryUpdater @Inject constructor( // avoid this call if we are sure there are unread events !isEventRead(realm.configuration, userId, roomId, latestPreviewableEvent?.eventId) - roomSummaryEntity.displayName = roomDisplayNameResolver.resolve(realm, roomId) + roomSummaryEntity.setDisplayName(roomDisplayNameResolver.resolve(realm, roomId)) roomSummaryEntity.avatarUrl = roomAvatarResolver.resolve(realm, roomId) roomSummaryEntity.name = ContentMapper.map(lastNameEvent?.content).toModel()?.name roomSummaryEntity.topic = ContentMapper.map(lastTopicEvent?.content).toModel()?.topic diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/signout/SignInAgainTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/signout/SignInAgainTask.kt index 563e85aefc..42fdf30501 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/signout/SignInAgainTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/signout/SignInAgainTask.kt @@ -42,11 +42,12 @@ internal class DefaultSignInAgainTask @Inject constructor( signOutAPI.loginAgain( PasswordLoginParams.userIdentifier( // Reuse the same userId - sessionParams.userId, - params.password, + user = sessionParams.userId, + password = params.password, // The spec says the initial device name will be ignored // https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-login // but https://github.com/matrix-org/synapse/issues/6525 + deviceDisplayName = null, // Reuse the same deviceId deviceId = sessionParams.deviceId ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt index ac20c79058..ebd5f2578e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt @@ -23,6 +23,7 @@ import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.room.RoomSortOrder import org.matrix.android.sdk.api.session.room.model.GuestAccess import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent @@ -94,12 +95,14 @@ internal class DefaultSpaceService @Inject constructor( return spaceGetter.get(spaceId) } - override fun getSpaceSummariesLive(queryParams: SpaceSummaryQueryParams): LiveData> { - return roomSummaryDataSource.getSpaceSummariesLive(queryParams) + override fun getSpaceSummariesLive(queryParams: SpaceSummaryQueryParams, + sortOrder: RoomSortOrder): LiveData> { + return roomSummaryDataSource.getSpaceSummariesLive(queryParams, sortOrder) } - override fun getSpaceSummaries(spaceSummaryQueryParams: SpaceSummaryQueryParams): List { - return roomSummaryDataSource.getSpaceSummaries(spaceSummaryQueryParams) + override fun getSpaceSummaries(spaceSummaryQueryParams: SpaceSummaryQueryParams, + sortOrder: RoomSortOrder): List { + return roomSummaryDataSource.getSpaceSummaries(spaceSummaryQueryParams, sortOrder) } override fun getRootSpaceSummaries(): List { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncEphemeralTemporaryStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncEphemeralTemporaryStore.kt index e8f74bbd48..8c68e224dc 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncEphemeralTemporaryStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncEphemeralTemporaryStore.kt @@ -39,8 +39,11 @@ internal class RoomSyncEphemeralTemporaryStoreFile @Inject constructor( moshi: Moshi ) : RoomSyncEphemeralTemporaryStore { - private val workingDir = File(fileDirectory, "rr") - .also { it.mkdirs() } + private val workingDir: File by lazy { + File(fileDirectory, "rr").also { + it.mkdirs() + } + } private val roomSyncEphemeralAdapter = moshi.adapter(RoomSyncEphemeral::class.java) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/UserAccountDataSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/UserAccountDataSyncHandler.kt index 3e38cd7839..7f80486c70 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/UserAccountDataSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/UserAccountDataSyncHandler.kt @@ -166,7 +166,7 @@ internal class UserAccountDataSyncHandler @Inject constructor( roomSummaryEntity.directUserId = userId // Also update the avatar and displayname, there is a specific treatment for DMs roomSummaryEntity.avatarUrl = roomAvatarResolver.resolve(realm, roomId) - roomSummaryEntity.displayName = roomDisplayNameResolver.resolve(realm, roomId) + roomSummaryEntity.setDisplayName(roomDisplayNameResolver.resolve(realm, roomId)) } } } @@ -178,7 +178,7 @@ internal class UserAccountDataSyncHandler @Inject constructor( it.directUserId = null // Also update the avatar and displayname, there was a specific treatment for DMs it.avatarUrl = roomAvatarResolver.resolve(realm, it.roomId) - it.displayName = roomDisplayNameResolver.resolve(realm, it.roomId) + it.setDisplayName(roomDisplayNameResolver.resolve(realm, it.roomId)) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncService.kt index 9480cc73f1..c17b31b910 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncService.kt @@ -105,9 +105,8 @@ abstract class SyncService : Service() { } } } - - // It's ok to be not sticky because we will explicitly start it again on the next alarm? - return START_NOT_STICKY + // Attempt to continue scheduling syncs after killed service is restarted + return START_REDELIVER_INTENT } override fun onDestroy() { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncWorker.kt index b81804feb5..41bb1a44a6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncWorker.kt @@ -121,9 +121,9 @@ internal class SyncWorker(context: Context, .setBackoffCriteria(BackoffPolicy.LINEAR, WorkManagerProvider.BACKOFF_DELAY_MILLIS, TimeUnit.MILLISECONDS) .setInitialDelay(delayInSeconds, TimeUnit.SECONDS) .build() - + // Avoid risking multiple chains of syncs by replacing the existing chain workManagerProvider.workManager - .enqueueUniqueWork(BG_SYNC_WORK_NAME, ExistingWorkPolicy.APPEND_OR_REPLACE, workRequest) + .enqueueUniqueWork(BG_SYNC_WORK_NAME, ExistingWorkPolicy.REPLACE, workRequest) } fun stopAnyBackgroundSync(workManagerProvider: WorkManagerProvider) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/BackgroundDetectionObserver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/BackgroundDetectionObserver.kt index 7cc00d023f..a12587ac56 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/BackgroundDetectionObserver.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/BackgroundDetectionObserver.kt @@ -29,7 +29,7 @@ import javax.inject.Inject @MatrixScope internal class BackgroundDetectionObserver @Inject constructor() : LifecycleObserver { - var isInBackground: Boolean = false + var isInBackground: Boolean = true private set private diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/Normalizer.kt similarity index 64% rename from vector/src/main/java/im/vector/app/features/home/room/list/RoomListModule.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/Normalizer.kt index 5dcef663e6..0e9c885394 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/Normalizer.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019 New Vector Ltd + * Copyright 2021 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. @@ -14,14 +14,14 @@ * limitations under the License. */ -package im.vector.app.features.home.room.list +package org.matrix.android.sdk.internal.util -import dagger.Binds -import dagger.Module +import java.text.Normalizer +import javax.inject.Inject -@Module -abstract class RoomListModule { +class Normalizer @Inject constructor() { - @Binds - abstract fun providesRoomListViewModelFactory(factory: RoomListViewModelFactory): RoomListViewModel.Factory + fun normalize(input: String): String { + return Normalizer.normalize(input.lowercase(), Normalizer.Form.NFD) + } } diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigrationTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigrationTest.kt new file mode 100644 index 0000000000..3a0574e95a --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigrationTest.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.database + +import io.mockk.mockk +import org.amshove.kluent.shouldBeEqualTo +import org.junit.Test + +class RealmSessionStoreMigrationTest { + + @Test + fun `when creating multiple migration instances then they are equal`() { + RealmSessionStoreMigration(normalizer = mockk()) shouldBeEqualTo RealmSessionStoreMigration(normalizer = mockk()) + } +} diff --git a/tools/check/forbidden_strings_in_code.txt b/tools/check/forbidden_strings_in_code.txt index 8f8625fe1c..29077c3a76 100644 --- a/tools/check/forbidden_strings_in_code.txt +++ b/tools/check/forbidden_strings_in_code.txt @@ -160,7 +160,7 @@ Formatter\.formatShortFileSize===1 # android\.text\.TextUtils ### This is not a rule, but a warning: the number of "enum class" has changed. For Json classes, it is mandatory that they have `@JsonClass(generateAdapter = false)`. If the enum is not used as a Json class, change the value in file forbidden_strings_in_code.txt -enum class===106 +enum class===107 ### Do not import temporary legacy classes import org.matrix.android.sdk.internal.legacy.riot===3 diff --git a/tools/release/pushPlayStoreMetaData.sh b/tools/release/pushPlayStoreMetaData.sh index fe7cf7eca6..276ea32fce 100755 --- a/tools/release/pushPlayStoreMetaData.sh +++ b/tools/release/pushPlayStoreMetaData.sh @@ -68,15 +68,6 @@ else removeFullDes_th=1 fi -if [[ -f "./fastlane/metadata/android/vi/full_description.txt" ]]; then - echo "It appears that file ./fastlane/metadata/android/vi/full_description.txt now exists. This can be removed." - removeFullDes_vi=0 -else - echo "Copy default full description to ./fastlane/metadata/android/vi" - cp ./fastlane/metadata/android/en-US/full_description.txt ./fastlane/metadata/android/vi - removeFullDes_vi=1 -fi - # Run fastlane echo "Run fastlane to push to the PlaysStore" fastlane deployMeta @@ -103,8 +94,4 @@ if [[ ${removeFullDes_th} -eq 1 ]]; then rm ./fastlane/metadata/android/th/full_description.txt fi -if [[ ${removeFullDes_vi} -eq 1 ]]; then - rm ./fastlane/metadata/android/vi/full_description.txt -fi - echo "Success!" diff --git a/vector/build.gradle b/vector/build.gradle index eed3e7909e..03b35465bf 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -6,6 +6,7 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-parcelize' apply plugin: 'kotlin-kapt' apply plugin: 'placeholder-resolver' +apply plugin: 'dagger.hilt.android.plugin' kapt { correctErrorTypes = true @@ -14,7 +15,7 @@ kapt { // Note: 2 digits max for each value ext.versionMajor = 1 ext.versionMinor = 3 -ext.versionPatch = 6 +ext.versionPatch = 7 static def getGitTimestamp() { def cmd = 'git show -s --format=%ct' @@ -371,7 +372,7 @@ dependencies { implementation 'com.facebook.stetho:stetho:1.6.0' // Phone number https://github.com/google/libphonenumber - implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.35' + implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.36' // rx implementation libs.rx.rxKotlin @@ -456,11 +457,11 @@ dependencies { implementation 'nl.dionsegijn:konfetti:1.3.2' implementation 'com.github.jetradarmobile:android-snowfall:1.2.1' // DI - implementation libs.dagger.dagger - kapt libs.dagger.daggerCompiler + implementation libs.dagger.hilt + kapt libs.dagger.hiltCompiler // gplay flavor only - gplayImplementation('com.google.firebase:firebase-messaging:22.0.0') { + gplayImplementation('com.google.firebase:firebase-messaging:23.0.0') { exclude group: 'com.google.firebase', module: 'firebase-core' exclude group: 'com.google.firebase', module: 'firebase-analytics' exclude group: 'com.google.firebase', module: 'firebase-measurement-connector' @@ -486,7 +487,7 @@ dependencies { // QR-code // Stick to 3.3.3 because of https://github.com/zxing/zxing/issues/1170 - implementation 'com.google.zxing:core:3.4.1' + implementation 'com.google.zxing:core:3.3.3' implementation 'me.dm7.barcodescanner:zxing:1.9.13' // Emoji Keyboard diff --git a/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt b/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt index 303a3a14ba..960994b169 100644 --- a/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt +++ b/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt @@ -24,9 +24,9 @@ import android.os.Build import androidx.core.app.NotificationCompat import androidx.core.app.Person import androidx.core.content.getSystemService +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO @@ -50,6 +50,7 @@ import org.matrix.android.sdk.internal.crypto.verification.qrcode.toQrCodeData import timber.log.Timber import javax.inject.Inject +@AndroidEntryPoint class DebugMenuActivity : VectorBaseActivity() { override fun getBinding() = ActivityDebugMenuBinding.inflate(layoutInflater) @@ -57,10 +58,6 @@ class DebugMenuActivity : VectorBaseActivity() { @Inject lateinit var activeSessionHolder: ActiveSessionHolder - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - private lateinit var buffer: ByteArray override fun initUiAndData() { diff --git a/vector/src/debug/java/im/vector/app/features/debug/DebugPermissionActivity.kt b/vector/src/debug/java/im/vector/app/features/debug/DebugPermissionActivity.kt index 048c64bc3a..a35bb40f8f 100644 --- a/vector/src/debug/java/im/vector/app/features/debug/DebugPermissionActivity.kt +++ b/vector/src/debug/java/im/vector/app/features/debug/DebugPermissionActivity.kt @@ -22,6 +22,7 @@ import android.os.Build import android.widget.Toast import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.utils.checkPermissions @@ -31,6 +32,7 @@ import im.vector.app.core.utils.registerForPermissionsResult import im.vector.app.databinding.ActivityDebugPermissionBinding import timber.log.Timber +@AndroidEntryPoint class DebugPermissionActivity : VectorBaseActivity() { override fun getBinding() = ActivityDebugPermissionBinding.inflate(layoutInflater) diff --git a/vector/src/fdroid/AndroidManifest.xml b/vector/src/fdroid/AndroidManifest.xml index 3a7c107138..022b08f16d 100644 --- a/vector/src/fdroid/AndroidManifest.xml +++ b/vector/src/fdroid/AndroidManifest.xml @@ -1,9 +1,11 @@ + $event") - } - } else { - if (notifiableEvent is NotifiableMessageEvent) { - if (notifiableEvent.senderName.isNullOrEmpty()) { - notifiableEvent.senderName = data["sender_display_name"] ?: data["sender"] ?: "" - } - if (notifiableEvent.roomName.isNullOrEmpty()) { - notifiableEvent.roomName = findRoomNameBestEffort(data, session) ?: "" - } - } - - notifiableEvent.isPushGatewayEvent = true - notifiableEvent.matrixID = session.myUserId - notificationDrawerManager.onNotifiableEventReceived(notifiableEvent) - notificationDrawerManager.refreshNotificationDrawer() - } - } - } - - private fun findRoomNameBestEffort(data: Map, session: Session?): String? { - var roomName: String? = data["room_name"] - val roomId = data["room_id"] - if (null == roomName && null != roomId) { - // Try to get the room name from our store - roomName = session?.getRoom(roomId)?.roomSummary()?.displayName - } - return roomName - } - - /** - * Try to create an event from the FCM data - * - * @param data the FCM data - * @return the event or null if required data are missing - */ - private fun parseEvent(data: Map?): Event? { - return Event( - eventId = data?.get("event_id") ?: return null, - senderId = data["sender"], - roomId = data["room_id"] ?: return null, - type = data["type"] ?: return null, - originServerTs = System.currentTimeMillis() - ) - } } diff --git a/vector/src/gplay/java/im/vector/app/push/fcm/FcmHelper.kt b/vector/src/gplay/java/im/vector/app/push/fcm/FcmHelper.kt index fe091ffda3..52ad4be087 100755 --- a/vector/src/gplay/java/im/vector/app/push/fcm/FcmHelper.kt +++ b/vector/src/gplay/java/im/vector/app/push/fcm/FcmHelper.kt @@ -108,6 +108,6 @@ object FcmHelper { @Suppress("UNUSED_PARAMETER") fun onEnterBackground(context: Context, vectorPreferences: VectorPreferences, activeSessionHolder: ActiveSessionHolder) { - // TODO FCM fallback + // No op } } diff --git a/vector/src/main/java/im/vector/app/VectorApplication.kt b/vector/src/main/java/im/vector/app/VectorApplication.kt index c2fd4c92be..d9027231da 100644 --- a/vector/src/main/java/im/vector/app/VectorApplication.kt +++ b/vector/src/main/java/im/vector/app/VectorApplication.kt @@ -39,10 +39,8 @@ import com.facebook.stetho.Stetho import com.gabrielittner.threetenbp.LazyThreeTen import com.vanniktech.emoji.EmojiManager import com.vanniktech.emoji.google.GoogleEmojiProvider +import dagger.hilt.android.HiltAndroidApp import im.vector.app.core.di.ActiveSessionHolder -import im.vector.app.core.di.DaggerVectorComponent -import im.vector.app.core.di.HasVectorInjector -import im.vector.app.core.di.VectorComponent import im.vector.app.core.extensions.configureAndStart import im.vector.app.core.extensions.startSyncing import im.vector.app.core.rx.RxConfig @@ -55,6 +53,7 @@ import im.vector.app.features.notifications.NotificationDrawerManager import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.pin.PinLocker import im.vector.app.features.popup.PopupAlertManager +import im.vector.app.features.rageshake.VectorFileLogger import im.vector.app.features.rageshake.VectorUncaughtExceptionHandler import im.vector.app.features.room.VectorRoomDisplayNameFallbackProvider import im.vector.app.features.settings.VectorLocale @@ -75,9 +74,9 @@ import java.util.concurrent.Executors import javax.inject.Inject import androidx.work.Configuration as WorkConfiguration +@HiltAndroidApp class VectorApplication : Application(), - HasVectorInjector, MatrixConfiguration.Provider, WorkConfiguration.Provider { @@ -99,8 +98,7 @@ class VectorApplication : @Inject lateinit var pinLocker: PinLocker @Inject lateinit var callManager: WebRtcCallManager @Inject lateinit var invitesAcceptor: InvitesAcceptor - - lateinit var vectorComponent: VectorComponent + @Inject lateinit var vectorFileLogger: VectorFileLogger // font thread handler private var fontThreadHandler: Handler? = null @@ -118,8 +116,6 @@ class VectorApplication : enableStrictModeIfNeeded() super.onCreate() appContext = this - vectorComponent = DaggerVectorComponent.factory().create(this) - vectorComponent.inject(this) invitesAcceptor.initialize() vectorUncaughtExceptionHandler.activate(this) rxConfig.setupRxPlugin() @@ -132,7 +128,7 @@ class VectorApplication : if (BuildConfig.DEBUG) { Timber.plant(Timber.DebugTree()) } - Timber.plant(vectorComponent.vectorFileLogger()) + Timber.plant(vectorFileLogger) if (BuildConfig.DEBUG) { Stetho.initializeWithDefaults(this) @@ -236,10 +232,6 @@ class VectorApplication : .build() } - override fun injector(): VectorComponent { - return vectorComponent - } - private fun logInfo() { val appVersion = versionProvider.getVersion(longFormat = true, useBuildNumber = true) val sdkVersion = Matrix.getSdkVersion() 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 e739cac95f..6b7b5f7f62 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 @@ -18,6 +18,7 @@ package im.vector.app.core.di import arrow.core.Option import im.vector.app.ActiveSessionDataSource +import im.vector.app.core.services.GuardServiceStarter import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.crypto.keysrequest.KeyRequestHandler import im.vector.app.features.crypto.verification.IncomingVerificationRequestHandler @@ -36,7 +37,8 @@ class ActiveSessionHolder @Inject constructor(private val sessionObservableStore private val callManager: WebRtcCallManager, private val pushRuleTriggerListener: PushRuleTriggerListener, private val sessionListener: SessionListener, - private val imageManager: ImageManager + private val imageManager: ImageManager, + private val guardServiceStarter: GuardServiceStarter ) { private var activeSession: AtomicReference = AtomicReference() @@ -52,6 +54,7 @@ class ActiveSessionHolder @Inject constructor(private val sessionObservableStore pushRuleTriggerListener.startWithSession(session) session.callSignalingService().addCallListener(callManager) imageManager.onSessionStarted(session) + guardServiceStarter.start() } fun clearActiveSession() { diff --git a/vector/src/main/java/im/vector/app/core/di/ActivityEntryPoint.kt b/vector/src/main/java/im/vector/app/core/di/ActivityEntryPoint.kt new file mode 100644 index 0000000000..c5f7317ebe --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/di/ActivityEntryPoint.kt @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.core.di + +import androidx.fragment.app.FragmentFactory +import androidx.lifecycle.ViewModelProvider +import dagger.hilt.EntryPoint +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ActivityComponent + +@InstallIn(ActivityComponent::class) +@EntryPoint +interface ActivityEntryPoint { + fun fragmentFactory(): FragmentFactory + fun viewModelFactory(): ViewModelProvider.Factory +} diff --git a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt index 3bc8e30851..bf72dcb076 100644 --- a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt @@ -21,6 +21,8 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentFactory import dagger.Binds import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ActivityComponent import dagger.multibindings.IntoMap import im.vector.app.features.attachments.preview.AttachmentsPreviewFragment import im.vector.app.features.contactsbook.ContactsBookFragment @@ -157,6 +159,7 @@ import im.vector.app.features.usercode.ShowUserCodeFragment import im.vector.app.features.userdirectory.UserListFragment import im.vector.app.features.widgets.WidgetFragment +@InstallIn(ActivityComponent::class) @Module interface FragmentModule { /** diff --git a/vector/src/main/java/im/vector/app/core/di/HiltMavericksViewModelFactory.kt b/vector/src/main/java/im/vector/app/core/di/HiltMavericksViewModelFactory.kt new file mode 100644 index 0000000000..13702053e8 --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/di/HiltMavericksViewModelFactory.kt @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.core.di + +import com.airbnb.mvrx.MavericksState +import com.airbnb.mvrx.MavericksViewModel +import com.airbnb.mvrx.MavericksViewModelFactory +import com.airbnb.mvrx.ViewModelContext +import dagger.hilt.DefineComponent +import dagger.hilt.EntryPoint +import dagger.hilt.EntryPoints +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent + +/** + * To connect Mavericks ViewModel creation with Hilt's dependency injection, add the following Factory and companion object to your MavericksViewModel. + * + * Example: + * + * class MyViewModel @AssistedInject constructor(...): MavericksViewModel(...) { + * + * @AssistedFactory + * interface Factory : AssistedViewModelFactory { + * ... + * } + * + * companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() + * } + */ + +inline fun , S : MavericksState> hiltMavericksViewModelFactory() = HiltMavericksViewModelFactory(VM::class.java) + +class HiltMavericksViewModelFactory, S : MavericksState>( + private val viewModelClass: Class> +) : MavericksViewModelFactory { + + override fun create(viewModelContext: ViewModelContext, state: S): VM { + // We want to create the ViewModelComponent. In order to do that, we need to get its parent: ActivityComponent. + val componentBuilder = EntryPoints.get(viewModelContext.app(), CreateMavericksViewModelComponent::class.java).mavericksViewModelComponentBuilder() + val viewModelComponent = componentBuilder.build() + val viewModelFactoryMap = EntryPoints.get(viewModelComponent, HiltMavericksEntryPoint::class.java).viewModelFactories + val viewModelFactory = viewModelFactoryMap[viewModelClass] + + @Suppress("UNCHECKED_CAST") + val castedViewModelFactory = viewModelFactory as? MavericksAssistedViewModelFactory + return castedViewModelFactory?.create(state) as VM + } + + override fun initialState(viewModelContext: ViewModelContext): S? { + return super.initialState(viewModelContext) + } +} + +/** + * Hilt's ViewModelComponent's parent is ActivityRetainedComponent but there is no easy way to access it. SingletonComponent should be sufficient + * because the ViewModel that gets created is the only object with a reference to the created component so the lifecycle of it will + * still be correct. + */ +@MavericksViewModelScoped +@DefineComponent(parent = SingletonComponent::class) +interface MavericksViewModelComponent + +@DefineComponent.Builder +interface MavericksViewModelComponentBuilder { + fun build(): MavericksViewModelComponent +} + +@EntryPoint +@InstallIn(SingletonComponent::class) +interface CreateMavericksViewModelComponent { + fun mavericksViewModelComponentBuilder(): MavericksViewModelComponentBuilder +} + +@EntryPoint +@InstallIn(MavericksViewModelComponent::class) +interface HiltMavericksEntryPoint { + val viewModelFactories: Map>, MavericksAssistedViewModelFactory<*, *>> +} diff --git a/vector/src/main/java/im/vector/app/core/di/HomeModule.kt b/vector/src/main/java/im/vector/app/core/di/HomeModule.kt new file mode 100644 index 0000000000..1af67cdc83 --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/di/HomeModule.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.core.di + +import android.os.Handler +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ActivityComponent +import im.vector.app.features.home.room.detail.timeline.TimelineEventControllerHandler +import im.vector.app.features.home.room.detail.timeline.helper.TimelineAsyncHelper + +@Module +@InstallIn(ActivityComponent::class) +object HomeModule { + @Provides + @JvmStatic + @TimelineEventControllerHandler + fun providesTimelineBackgroundHandler(): Handler { + return TimelineAsyncHelper.getBackgroundHandler() + } +} diff --git a/vector/src/main/java/im/vector/app/core/di/MavericksAssistedViewModelFactory.kt b/vector/src/main/java/im/vector/app/core/di/MavericksAssistedViewModelFactory.kt new file mode 100644 index 0000000000..7cd6245d64 --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/di/MavericksAssistedViewModelFactory.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.core.di + +import com.airbnb.mvrx.MavericksState +import com.airbnb.mvrx.MavericksViewModel + +/** + * This factory allows Mavericks to supply the initial or restored [MavericksState] to Hilt. + * + * Add this interface inside of your [MavericksViewModel] class then create the following Hilt module: + * + * @Module + * @InstallIn(MavericksViewModelComponent::class) + * interface ViewModelsModule { + * @Binds + * @IntoMap + * @ViewModelKey(MyViewModel::class) + * fun myViewModelFactory(factory: MyViewModel.Factory): AssistedViewModelFactory<*, *> + * } + * + * If you already have a ViewModelsModule then all you have to do is add the multibinding entry for your new [MavericksViewModel]. + */ +interface MavericksAssistedViewModelFactory, S : MavericksState> { + fun create(initialState: S): VM +} 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 new file mode 100644 index 0000000000..f1ff55f765 --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt @@ -0,0 +1,549 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.core.di + +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.multibindings.IntoMap +import im.vector.app.features.auth.ReAuthViewModel +import im.vector.app.features.call.VectorCallViewModel +import im.vector.app.features.call.conference.JitsiCallViewModel +import im.vector.app.features.call.transfer.CallTransferViewModel +import im.vector.app.features.contactsbook.ContactsBookViewModel +import im.vector.app.features.createdirect.CreateDirectRoomViewModel +import im.vector.app.features.crypto.keysbackup.settings.KeysBackupSettingsViewModel +import im.vector.app.features.crypto.quads.SharedSecureStorageViewModel +import im.vector.app.features.crypto.recover.BootstrapSharedViewModel +import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel +import im.vector.app.features.crypto.verification.choose.VerificationChooseMethodViewModel +import im.vector.app.features.crypto.verification.emoji.VerificationEmojiCodeViewModel +import im.vector.app.features.devtools.RoomDevToolViewModel +import im.vector.app.features.discovery.DiscoverySettingsViewModel +import im.vector.app.features.discovery.change.SetIdentityServerViewModel +import im.vector.app.features.home.HomeActivityViewModel +import im.vector.app.features.home.HomeDetailViewModel +import im.vector.app.features.home.PromoteRestrictedViewModel +import im.vector.app.features.home.UnknownDeviceDetectorSharedViewModel +import im.vector.app.features.home.UnreadMessagesSharedViewModel +import im.vector.app.features.home.room.breadcrumbs.BreadcrumbsViewModel +import im.vector.app.features.home.room.detail.composer.TextComposerViewModel +import im.vector.app.features.home.room.detail.search.SearchViewModel +import im.vector.app.features.home.room.detail.timeline.action.MessageActionsViewModel +import im.vector.app.features.home.room.detail.timeline.edithistory.ViewEditHistoryViewModel +import im.vector.app.features.home.room.detail.timeline.reactions.ViewReactionsViewModel +import im.vector.app.features.home.room.detail.upgrade.MigrateRoomViewModel +import im.vector.app.features.home.room.list.RoomListViewModel +import im.vector.app.features.homeserver.HomeServerCapabilitiesViewModel +import im.vector.app.features.invite.InviteUsersToRoomViewModel +import im.vector.app.features.login.LoginViewModel +import im.vector.app.features.login2.LoginViewModel2 +import im.vector.app.features.login2.created.AccountCreatedViewModel +import im.vector.app.features.matrixto.MatrixToBottomSheetViewModel +import im.vector.app.features.rageshake.BugReportViewModel +import im.vector.app.features.reactions.EmojiSearchResultViewModel +import im.vector.app.features.room.RequireActiveMembershipViewModel +import im.vector.app.features.roomdirectory.RoomDirectoryViewModel +import im.vector.app.features.roomdirectory.createroom.CreateRoomViewModel +import im.vector.app.features.roomdirectory.picker.RoomDirectoryPickerViewModel +import im.vector.app.features.roomdirectory.roompreview.RoomPreviewViewModel +import im.vector.app.features.roommemberprofile.RoomMemberProfileViewModel +import im.vector.app.features.roommemberprofile.devices.DeviceListBottomSheetViewModel +import im.vector.app.features.roomprofile.RoomProfileViewModel +import im.vector.app.features.roomprofile.alias.RoomAliasViewModel +import im.vector.app.features.roomprofile.alias.detail.RoomAliasBottomSheetViewModel +import im.vector.app.features.roomprofile.banned.RoomBannedMemberListViewModel +import im.vector.app.features.roomprofile.members.RoomMemberListViewModel +import im.vector.app.features.roomprofile.notifications.RoomNotificationSettingsViewModel +import im.vector.app.features.roomprofile.permissions.RoomPermissionsViewModel +import im.vector.app.features.roomprofile.settings.RoomSettingsViewModel +import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedViewModel +import im.vector.app.features.roomprofile.uploads.RoomUploadsViewModel +import im.vector.app.features.settings.account.deactivation.DeactivateAccountViewModel +import im.vector.app.features.settings.crosssigning.CrossSigningSettingsViewModel +import im.vector.app.features.settings.devices.DeviceVerificationInfoBottomSheetViewModel +import im.vector.app.features.settings.devices.DevicesViewModel +import im.vector.app.features.settings.devtools.AccountDataViewModel +import im.vector.app.features.settings.devtools.GossipingEventsPaperTrailViewModel +import im.vector.app.features.settings.devtools.KeyRequestListViewModel +import im.vector.app.features.settings.devtools.KeyRequestViewModel +import im.vector.app.features.settings.homeserver.HomeserverSettingsViewModel +import im.vector.app.features.settings.ignored.IgnoredUsersViewModel +import im.vector.app.features.settings.locale.LocalePickerViewModel +import im.vector.app.features.settings.push.PushGatewaysViewModel +import im.vector.app.features.settings.threepids.ThreePidsSettingsViewModel +import im.vector.app.features.share.IncomingShareViewModel +import im.vector.app.features.signout.soft.SoftLogoutViewModel +import im.vector.app.features.spaces.SpaceListViewModel +import im.vector.app.features.spaces.SpaceMenuViewModel +import im.vector.app.features.spaces.create.CreateSpaceViewModel +import im.vector.app.features.spaces.explore.SpaceDirectoryViewModel +import im.vector.app.features.spaces.invite.SpaceInviteBottomSheetViewModel +import im.vector.app.features.spaces.leave.SpaceLeaveAdvancedViewModel +import im.vector.app.features.spaces.manage.SpaceAddRoomsViewModel +import im.vector.app.features.spaces.manage.SpaceManageRoomsViewModel +import im.vector.app.features.spaces.manage.SpaceManageSharedViewModel +import im.vector.app.features.spaces.people.SpacePeopleViewModel +import im.vector.app.features.spaces.preview.SpacePreviewViewModel +import im.vector.app.features.spaces.share.ShareSpaceViewModel +import im.vector.app.features.terms.ReviewTermsViewModel +import im.vector.app.features.usercode.UserCodeSharedViewModel +import im.vector.app.features.userdirectory.UserListViewModel +import im.vector.app.features.widgets.WidgetViewModel +import im.vector.app.features.widgets.permissions.RoomWidgetPermissionViewModel +import im.vector.app.features.workers.signout.ServerBackupStatusViewModel +import im.vector.app.features.workers.signout.SignoutCheckViewModel + +@InstallIn(MavericksViewModelComponent::class) +@Module +interface MavericksViewModelModule { + + @Binds + @IntoMap + @MavericksViewModelKey(RoomListViewModel::class) + fun roomListViewModelFactory(factory: RoomListViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(SpaceManageRoomsViewModel::class) + fun spaceManageRoomsViewModelFactory(factory: SpaceManageRoomsViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(SpaceManageSharedViewModel::class) + fun spaceManageSharedViewModelFactory(factory: SpaceManageSharedViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(SpaceListViewModel::class) + fun spaceListViewModelFactory(factory: SpaceListViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(ReAuthViewModel::class) + fun reAuthViewModelFactory(factory: ReAuthViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(VectorCallViewModel::class) + fun vectorCallViewModelFactory(factory: VectorCallViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(JitsiCallViewModel::class) + fun jitsiCallViewModelFactory(factory: JitsiCallViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(RoomDirectoryViewModel::class) + fun roomDirectoryViewModelFactory(factory: RoomDirectoryViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(ViewReactionsViewModel::class) + fun viewReactionsViewModelFactory(factory: ViewReactionsViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(RoomWidgetPermissionViewModel::class) + fun roomWidgetPermissionViewModelFactory(factory: RoomWidgetPermissionViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(WidgetViewModel::class) + fun widgetViewModelFactory(factory: WidgetViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(ServerBackupStatusViewModel::class) + fun serverBackupStatusViewModelFactory(factory: ServerBackupStatusViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(SignoutCheckViewModel::class) + fun signoutCheckViewModelFactory(factory: SignoutCheckViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(RoomDirectoryPickerViewModel::class) + fun roomDirectoryPickerViewModelFactory(factory: RoomDirectoryPickerViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(RoomDevToolViewModel::class) + fun roomDevToolViewModelFactory(factory: RoomDevToolViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(MigrateRoomViewModel::class) + fun migrateRoomViewModelFactory(factory: MigrateRoomViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(IgnoredUsersViewModel::class) + fun ignoredUsersViewModelFactory(factory: IgnoredUsersViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(CallTransferViewModel::class) + fun callTransferViewModelFactory(factory: CallTransferViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(ContactsBookViewModel::class) + fun contactsBookViewModelFactory(factory: ContactsBookViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(CreateDirectRoomViewModel::class) + fun createDirectRoomViewModelFactory(factory: CreateDirectRoomViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(RoomNotificationSettingsViewModel::class) + fun roomNotificationSettingsViewModelFactory(factory: RoomNotificationSettingsViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(KeysBackupSettingsViewModel::class) + fun keysBackupSettingsViewModelFactory(factory: KeysBackupSettingsViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(SharedSecureStorageViewModel::class) + fun sharedSecureStorageViewModelFactory(factory: SharedSecureStorageViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(PromoteRestrictedViewModel::class) + fun promoteRestrictedViewModelFactory(factory: PromoteRestrictedViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(UserListViewModel::class) + fun userListViewModelFactory(factory: UserListViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(UserCodeSharedViewModel::class) + fun userCodeSharedViewModelFactory(factory: UserCodeSharedViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(ReviewTermsViewModel::class) + fun reviewTermsViewModelFactory(factory: ReviewTermsViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(ShareSpaceViewModel::class) + fun shareSpaceViewModelFactory(factory: ShareSpaceViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(SpacePreviewViewModel::class) + fun spacePreviewViewModelFactory(factory: SpacePreviewViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(SpacePeopleViewModel::class) + fun spacePeopleViewModelFactory(factory: SpacePeopleViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(SpaceAddRoomsViewModel::class) + fun spaceAddRoomsViewModelFactory(factory: SpaceAddRoomsViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(SpaceLeaveAdvancedViewModel::class) + fun spaceLeaveAdvancedViewModelFactory(factory: SpaceLeaveAdvancedViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(SpaceInviteBottomSheetViewModel::class) + fun spaceInviteBottomSheetViewModelFactory(factory: SpaceInviteBottomSheetViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(SpaceDirectoryViewModel::class) + fun spaceDirectoryViewModelFactory(factory: SpaceDirectoryViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(CreateSpaceViewModel::class) + fun createSpaceViewModelFactory(factory: CreateSpaceViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(SpaceMenuViewModel::class) + fun spaceMenuViewModelFactory(factory: SpaceMenuViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(SoftLogoutViewModel::class) + fun softLogoutViewModelFactory(factory: SoftLogoutViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(IncomingShareViewModel::class) + fun incomingShareViewModelFactory(factory: IncomingShareViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(ThreePidsSettingsViewModel::class) + fun threePidsSettingsViewModelFactory(factory: ThreePidsSettingsViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(PushGatewaysViewModel::class) + fun pushGatewaysViewModelFactory(factory: PushGatewaysViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(HomeserverSettingsViewModel::class) + fun homeserverSettingsViewModelFactory(factory: HomeserverSettingsViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(LocalePickerViewModel::class) + fun localePickerViewModelFactory(factory: LocalePickerViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(GossipingEventsPaperTrailViewModel::class) + fun gossipingEventsPaperTrailViewModelFactory(factory: GossipingEventsPaperTrailViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(AccountDataViewModel::class) + fun accountDataViewModelFactory(factory: AccountDataViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(DevicesViewModel::class) + fun devicesViewModelFactory(factory: DevicesViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(KeyRequestListViewModel::class) + fun keyRequestListViewModelFactory(factory: KeyRequestListViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(KeyRequestViewModel::class) + fun keyRequestViewModelFactory(factory: KeyRequestViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(CrossSigningSettingsViewModel::class) + fun crossSigningSettingsViewModelFactory(factory: CrossSigningSettingsViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(DeactivateAccountViewModel::class) + fun deactivateAccountViewModelFactory(factory: DeactivateAccountViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(RoomUploadsViewModel::class) + fun roomUploadsViewModelFactory(factory: RoomUploadsViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(RoomJoinRuleChooseRestrictedViewModel::class) + fun roomJoinRuleChooseRestrictedViewModelFactory(factory: RoomJoinRuleChooseRestrictedViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(RoomSettingsViewModel::class) + fun roomSettingsViewModelFactory(factory: RoomSettingsViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(RoomPermissionsViewModel::class) + fun roomPermissionsViewModelFactory(factory: RoomPermissionsViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(RoomMemberListViewModel::class) + fun roomMemberListViewModelFactory(factory: RoomMemberListViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(RoomBannedMemberListViewModel::class) + fun roomBannedMemberListViewModelFactory(factory: RoomBannedMemberListViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(RoomAliasViewModel::class) + fun roomAliasViewModelFactory(factory: RoomAliasViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(RoomAliasBottomSheetViewModel::class) + fun roomAliasBottomSheetViewModelFactory(factory: RoomAliasBottomSheetViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(RoomProfileViewModel::class) + fun roomProfileViewModelFactory(factory: RoomProfileViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(RoomMemberProfileViewModel::class) + fun roomMemberProfileViewModelFactory(factory: RoomMemberProfileViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(RoomPreviewViewModel::class) + fun roomPreviewViewModelFactory(factory: RoomPreviewViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(CreateRoomViewModel::class) + fun createRoomViewModelFactory(factory: CreateRoomViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(RequireActiveMembershipViewModel::class) + fun requireActiveMembershipViewModelFactory(factory: RequireActiveMembershipViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(EmojiSearchResultViewModel::class) + fun emojiSearchResultViewModelFactory(factory: EmojiSearchResultViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(BugReportViewModel::class) + fun bugReportViewModelFactory(factory: BugReportViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(MatrixToBottomSheetViewModel::class) + fun matrixToBottomSheetViewModelFactory(factory: MatrixToBottomSheetViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(AccountCreatedViewModel::class) + fun accountCreatedViewModelFactory(factory: AccountCreatedViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(LoginViewModel2::class) + fun loginViewModel2Factory(factory: LoginViewModel2.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(LoginViewModel::class) + fun loginViewModelFactory(factory: LoginViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(HomeServerCapabilitiesViewModel::class) + fun homeServerCapabilitiesViewModelFactory(factory: HomeServerCapabilitiesViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(InviteUsersToRoomViewModel::class) + fun inviteUsersToRoomViewModelFactory(factory: InviteUsersToRoomViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(ViewEditHistoryViewModel::class) + fun viewEditHistoryViewModelFactory(factory: ViewEditHistoryViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(MessageActionsViewModel::class) + fun messageActionsViewModelFactory(factory: MessageActionsViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(VerificationChooseMethodViewModel::class) + fun verificationChooseMethodViewModelFactory(factory: VerificationChooseMethodViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(VerificationEmojiCodeViewModel::class) + fun verificationEmojiCodeViewModelFactory(factory: VerificationEmojiCodeViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(SearchViewModel::class) + fun searchViewModelFactory(factory: SearchViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(UnreadMessagesSharedViewModel::class) + fun unreadMessagesSharedViewModelFactory(factory: UnreadMessagesSharedViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(UnknownDeviceDetectorSharedViewModel::class) + fun unknownDeviceDetectorSharedViewModelFactory(factory: UnknownDeviceDetectorSharedViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(DiscoverySettingsViewModel::class) + fun discoverySettingsViewModelFactory(factory: DiscoverySettingsViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(TextComposerViewModel::class) + fun textComposerViewModelFactory(factory: TextComposerViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(SetIdentityServerViewModel::class) + fun setIdentityServerViewModelFactory(factory: SetIdentityServerViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(BreadcrumbsViewModel::class) + fun breadcrumbsViewModelFactory(factory: BreadcrumbsViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(HomeDetailViewModel::class) + fun homeDetailViewModelFactory(factory: HomeDetailViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(DeviceVerificationInfoBottomSheetViewModel::class) + fun deviceVerificationInfoBottomSheetViewModelFactory(factory: DeviceVerificationInfoBottomSheetViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(DeviceListBottomSheetViewModel::class) + fun deviceListBottomSheetViewModelFactory(factory: DeviceListBottomSheetViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(HomeActivityViewModel::class) + fun homeActivityViewModelFactory(factory: HomeActivityViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(BootstrapSharedViewModel::class) + fun bootstrapSharedViewModelFactory(factory: BootstrapSharedViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(VerificationBottomSheetViewModel::class) + fun verificationBottomSheetViewModelFactory(factory: VerificationBottomSheetViewModel.Factory): MavericksAssistedViewModelFactory<*, *> +} diff --git a/vector/src/main/java/im/vector/app/core/di/ScreenScope.kt b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelScoped.kt similarity index 71% rename from vector/src/main/java/im/vector/app/core/di/ScreenScope.kt rename to vector/src/main/java/im/vector/app/core/di/MavericksViewModelScoped.kt index c39d6a947e..58b9246fe5 100644 --- a/vector/src/main/java/im/vector/app/core/di/ScreenScope.kt +++ b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelScoped.kt @@ -1,11 +1,11 @@ /* - * Copyright 2019 New Vector Ltd + * Copyright (c) 2021 New Vector Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -18,7 +18,8 @@ package im.vector.app.core.di import javax.inject.Scope +/** + * Scope annotation for bindings that should exist for the life of an MavericksViewModel. + */ @Scope -@MustBeDocumented -@Retention(AnnotationRetention.RUNTIME) -annotation class ScreenScope +annotation class MavericksViewModelScoped diff --git a/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt b/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt deleted file mode 100644 index 76b511d2bd..0000000000 --- a/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright 2019 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.app.core.di - -import androidx.appcompat.app.AppCompatActivity -import androidx.fragment.app.FragmentFactory -import androidx.lifecycle.ViewModelProvider -import dagger.BindsInstance -import dagger.Component -import im.vector.app.core.dialogs.UnrecognizedCertificateDialog -import im.vector.app.core.error.ErrorFormatter -import im.vector.app.core.preference.UserAvatarPreference -import im.vector.app.features.MainActivity -import im.vector.app.features.auth.ReAuthActivity -import im.vector.app.features.call.CallControlsBottomSheet -import im.vector.app.features.call.VectorCallActivity -import im.vector.app.features.call.conference.VectorJitsiActivity -import im.vector.app.features.call.transfer.CallTransferActivity -import im.vector.app.features.createdirect.CreateDirectRoomActivity -import im.vector.app.features.crypto.keysbackup.settings.KeysBackupManageActivity -import im.vector.app.features.crypto.keysbackup.setup.KeysBackupSetupActivity -import im.vector.app.features.crypto.quads.SharedSecureStorageActivity -import im.vector.app.features.crypto.recover.BootstrapBottomSheet -import im.vector.app.features.crypto.verification.VerificationBottomSheet -import im.vector.app.features.debug.DebugMenuActivity -import im.vector.app.features.devtools.RoomDevToolActivity -import im.vector.app.features.home.HomeActivity -import im.vector.app.features.home.HomeModule -import im.vector.app.features.home.room.detail.JoinReplacementRoomBottomSheet -import im.vector.app.features.home.room.detail.RoomDetailActivity -import im.vector.app.features.home.room.detail.readreceipts.DisplayReadReceiptsBottomSheet -import im.vector.app.features.home.room.detail.search.SearchActivity -import im.vector.app.features.home.room.detail.timeline.action.MessageActionsBottomSheet -import im.vector.app.features.home.room.detail.timeline.edithistory.ViewEditHistoryBottomSheet -import im.vector.app.features.home.room.detail.timeline.reactions.ViewReactionsBottomSheet -import im.vector.app.features.home.room.detail.upgrade.MigrateRoomBottomSheet -import im.vector.app.features.home.room.detail.widget.RoomWidgetsBottomSheet -import im.vector.app.features.home.room.filtered.FilteredRoomsActivity -import im.vector.app.features.home.room.list.RoomListModule -import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet -import im.vector.app.features.invite.AutoAcceptInvites -import im.vector.app.features.invite.InviteUsersToRoomActivity -import im.vector.app.features.invite.VectorInviteView -import im.vector.app.features.link.LinkHandlerActivity -import im.vector.app.features.login.LoginActivity -import im.vector.app.features.login2.LoginActivity2 -import im.vector.app.features.matrixto.MatrixToBottomSheet -import im.vector.app.features.media.BigImageViewerActivity -import im.vector.app.features.media.VectorAttachmentViewerActivity -import im.vector.app.features.navigation.Navigator -import im.vector.app.features.pin.PinLocker -import im.vector.app.features.qrcode.QrCodeScannerActivity -import im.vector.app.features.rageshake.BugReportActivity -import im.vector.app.features.rageshake.BugReporter -import im.vector.app.features.rageshake.RageShake -import im.vector.app.features.reactions.EmojiReactionPickerActivity -import im.vector.app.features.reactions.widget.ReactionButton -import im.vector.app.features.roomdirectory.RoomDirectoryActivity -import im.vector.app.features.roomdirectory.createroom.CreateRoomActivity -import im.vector.app.features.roommemberprofile.RoomMemberProfileActivity -import im.vector.app.features.roommemberprofile.devices.DeviceListBottomSheet -import im.vector.app.features.roomprofile.RoomProfileActivity -import im.vector.app.features.roomprofile.alias.detail.RoomAliasBottomSheet -import im.vector.app.features.roomprofile.settings.historyvisibility.RoomHistoryVisibilityBottomSheet -import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleActivity -import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleBottomSheet -import im.vector.app.features.settings.VectorSettingsActivity -import im.vector.app.features.settings.devices.DeviceVerificationInfoBottomSheet -import im.vector.app.features.share.IncomingShareActivity -import im.vector.app.features.signout.soft.SoftLogoutActivity -import im.vector.app.features.spaces.InviteRoomSpaceChooserBottomSheet -import im.vector.app.features.spaces.LeaveSpaceBottomSheet -import im.vector.app.features.spaces.SpaceCreationActivity -import im.vector.app.features.spaces.SpaceExploreActivity -import im.vector.app.features.spaces.SpaceSettingsMenuBottomSheet -import im.vector.app.features.spaces.invite.SpaceInviteBottomSheet -import im.vector.app.features.spaces.leave.SpaceLeaveAdvancedActivity -import im.vector.app.features.spaces.manage.SpaceManageActivity -import im.vector.app.features.spaces.share.ShareSpaceBottomSheet -import im.vector.app.features.terms.ReviewTermsActivity -import im.vector.app.features.ui.UiStateRepository -import im.vector.app.features.usercode.UserCodeActivity -import im.vector.app.features.widgets.WidgetActivity -import im.vector.app.features.widgets.permissions.RoomWidgetPermissionBottomSheet -import im.vector.app.features.workers.signout.SignOutBottomSheetDialogFragment -import kotlinx.coroutines.CoroutineScope - -@Component( - dependencies = [ - VectorComponent::class - ], - modules = [ - ViewModelModule::class, - FragmentModule::class, - HomeModule::class, - RoomListModule::class, - ScreenModule::class - ] -) -@ScreenScope -interface ScreenComponent { - - /* ========================================================================================== - * Shortcut to VectorComponent elements - * ========================================================================================== */ - - fun activeSessionHolder(): ActiveSessionHolder - fun fragmentFactory(): FragmentFactory - fun viewModelFactory(): ViewModelProvider.Factory - fun bugReporter(): BugReporter - fun rageShake(): RageShake - fun navigator(): Navigator - fun pinLocker(): PinLocker - fun errorFormatter(): ErrorFormatter - fun uiStateRepository(): UiStateRepository - fun unrecognizedCertificateDialog(): UnrecognizedCertificateDialog - fun autoAcceptInvites(): AutoAcceptInvites - fun appCoroutineScope(): CoroutineScope - - /* ========================================================================================== - * Activities - * ========================================================================================== */ - - fun inject(activity: HomeActivity) - fun inject(activity: RoomDetailActivity) - fun inject(activity: RoomProfileActivity) - fun inject(activity: RoomMemberProfileActivity) - fun inject(activity: VectorSettingsActivity) - fun inject(activity: KeysBackupManageActivity) - fun inject(activity: EmojiReactionPickerActivity) - fun inject(activity: LoginActivity) - fun inject(activity: LoginActivity2) - fun inject(activity: LinkHandlerActivity) - fun inject(activity: MainActivity) - fun inject(activity: RoomDirectoryActivity) - fun inject(activity: KeysBackupSetupActivity) - fun inject(activity: BugReportActivity) - fun inject(activity: FilteredRoomsActivity) - fun inject(activity: CreateRoomActivity) - fun inject(activity: CreateDirectRoomActivity) - fun inject(activity: IncomingShareActivity) - fun inject(activity: SoftLogoutActivity) - fun inject(activity: QrCodeScannerActivity) - fun inject(activity: DebugMenuActivity) - fun inject(activity: SharedSecureStorageActivity) - fun inject(activity: BigImageViewerActivity) - fun inject(activity: InviteUsersToRoomActivity) - fun inject(activity: ReviewTermsActivity) - fun inject(activity: WidgetActivity) - fun inject(activity: VectorCallActivity) - fun inject(activity: VectorAttachmentViewerActivity) - fun inject(activity: VectorJitsiActivity) - fun inject(activity: SearchActivity) - fun inject(activity: UserCodeActivity) - fun inject(activity: CallTransferActivity) - fun inject(activity: ReAuthActivity) - fun inject(activity: RoomDevToolActivity) - fun inject(activity: SpaceCreationActivity) - fun inject(activity: SpaceExploreActivity) - fun inject(activity: SpaceManageActivity) - fun inject(activity: RoomJoinRuleActivity) - fun inject(activity: SpaceLeaveAdvancedActivity) - - /* ========================================================================================== - * BottomSheets - * ========================================================================================== */ - - fun inject(bottomSheet: MessageActionsBottomSheet) - fun inject(bottomSheet: ViewReactionsBottomSheet) - fun inject(bottomSheet: ViewEditHistoryBottomSheet) - fun inject(bottomSheet: DisplayReadReceiptsBottomSheet) - fun inject(bottomSheet: RoomListQuickActionsBottomSheet) - fun inject(bottomSheet: RoomAliasBottomSheet) - fun inject(bottomSheet: RoomHistoryVisibilityBottomSheet) - fun inject(bottomSheet: RoomJoinRuleBottomSheet) - fun inject(bottomSheet: VerificationBottomSheet) - fun inject(bottomSheet: DeviceVerificationInfoBottomSheet) - fun inject(bottomSheet: DeviceListBottomSheet) - fun inject(bottomSheet: BootstrapBottomSheet) - fun inject(bottomSheet: RoomWidgetPermissionBottomSheet) - fun inject(bottomSheet: RoomWidgetsBottomSheet) - fun inject(bottomSheet: CallControlsBottomSheet) - fun inject(bottomSheet: SignOutBottomSheetDialogFragment) - fun inject(bottomSheet: MatrixToBottomSheet) - fun inject(bottomSheet: ShareSpaceBottomSheet) - fun inject(bottomSheet: SpaceSettingsMenuBottomSheet) - fun inject(bottomSheet: InviteRoomSpaceChooserBottomSheet) - fun inject(bottomSheet: SpaceInviteBottomSheet) - fun inject(bottomSheet: JoinReplacementRoomBottomSheet) - fun inject(bottomSheet: MigrateRoomBottomSheet) - fun inject(bottomSheet: LeaveSpaceBottomSheet) - - /* ========================================================================================== - * Others - * ========================================================================================== */ - - fun inject(view: VectorInviteView) - fun inject(preference: UserAvatarPreference) - fun inject(button: ReactionButton) - - /* ========================================================================================== - * Factory - * ========================================================================================== */ - - @Component.Factory - interface Factory { - fun create(vectorComponent: VectorComponent, - @BindsInstance context: AppCompatActivity - ): ScreenComponent - } -} diff --git a/vector/src/main/java/im/vector/app/core/di/ScreenModule.kt b/vector/src/main/java/im/vector/app/core/di/ScreenModule.kt index 5f50f186d0..2dab05378c 100644 --- a/vector/src/main/java/im/vector/app/core/di/ScreenModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/ScreenModule.kt @@ -1,11 +1,11 @@ /* - * Copyright 2019 New Vector Ltd + * Copyright (c) 2021 New Vector Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -20,9 +20,13 @@ import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.RecyclerView import dagger.Module import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ActivityComponent +import dagger.hilt.android.scopes.ActivityScoped import im.vector.app.core.glide.GlideApp @Module +@InstallIn(ActivityComponent::class) object ScreenModule { @Provides @@ -31,6 +35,6 @@ object ScreenModule { @Provides @JvmStatic - @ScreenScope + @ActivityScoped fun providesSharedViewPool() = RecyclerView.RecycledViewPool() } diff --git a/vector/src/main/java/im/vector/app/core/di/SingletonEntryPoint.kt b/vector/src/main/java/im/vector/app/core/di/SingletonEntryPoint.kt new file mode 100644 index 0000000000..52316751e6 --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/di/SingletonEntryPoint.kt @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.core.di + +import dagger.hilt.EntryPoint +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import im.vector.app.core.dialogs.UnrecognizedCertificateDialog +import im.vector.app.core.error.ErrorFormatter +import im.vector.app.features.call.webrtc.WebRtcCallManager +import im.vector.app.features.home.AvatarRenderer +import im.vector.app.features.navigation.Navigator +import im.vector.app.features.pin.PinLocker +import im.vector.app.features.rageshake.BugReporter +import im.vector.app.features.session.SessionListener +import im.vector.app.features.settings.VectorPreferences +import im.vector.app.features.ui.UiStateRepository +import kotlinx.coroutines.CoroutineScope + +@InstallIn(SingletonComponent::class) +@EntryPoint +interface SingletonEntryPoint { + + fun sessionListener(): SessionListener + + fun avatarRenderer(): AvatarRenderer + + fun activeSessionHolder(): ActiveSessionHolder + + fun unrecognizedCertificateDialog(): UnrecognizedCertificateDialog + + fun navigator(): Navigator + + fun errorFormatter(): ErrorFormatter + + fun bugReporter(): BugReporter + + fun vectorPreferences(): VectorPreferences + + fun uiStateRepository(): UiStateRepository + + fun pinLocker(): PinLocker + + fun webRtcCallManager(): WebRtcCallManager + + fun appCoroutineScope(): CoroutineScope +} diff --git a/vector/src/main/java/im/vector/app/core/di/VectorModule.kt b/vector/src/main/java/im/vector/app/core/di/SingletonModule.kt similarity index 55% rename from vector/src/main/java/im/vector/app/core/di/VectorModule.kt rename to vector/src/main/java/im/vector/app/core/di/SingletonModule.kt index ddb765cef8..e89a060022 100644 --- a/vector/src/main/java/im/vector/app/core/di/VectorModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/SingletonModule.kt @@ -16,6 +16,7 @@ package im.vector.app.core.di +import android.app.Application import android.content.Context import android.content.Context.MODE_PRIVATE import android.content.SharedPreferences @@ -23,6 +24,8 @@ import android.content.res.Resources import dagger.Binds import dagger.Module import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent import im.vector.app.core.dispatchers.CoroutineDispatchers import im.vector.app.core.error.DefaultErrorFormatter import im.vector.app.core.error.ErrorFormatter @@ -45,74 +48,9 @@ import org.matrix.android.sdk.api.raw.RawService import org.matrix.android.sdk.api.session.Session import javax.inject.Singleton +@InstallIn(SingletonComponent::class) @Module -abstract class VectorModule { - - @Module - companion object { - - @Provides - @JvmStatic - fun providesResources(context: Context): Resources { - return context.resources - } - - @Provides - @JvmStatic - fun providesSharedPreferences(context: Context): SharedPreferences { - return context.getSharedPreferences("im.vector.riot", MODE_PRIVATE) - } - - @Provides - @JvmStatic - fun providesMatrix(context: Context): Matrix { - return Matrix.getInstance(context) - } - - @Provides - @JvmStatic - fun providesCurrentSession(activeSessionHolder: ActiveSessionHolder): Session { - // TODO: handle session injection better - return activeSessionHolder.getActiveSession() - } - - @Provides - @JvmStatic - fun providesLegacySessionImporter(matrix: Matrix): LegacySessionImporter { - return matrix.legacySessionImporter() - } - - @Provides - @JvmStatic - fun providesAuthenticationService(matrix: Matrix): AuthenticationService { - return matrix.authenticationService() - } - - @Provides - @JvmStatic - fun providesRawService(matrix: Matrix): RawService { - return matrix.rawService() - } - - @Provides - @JvmStatic - fun providesHomeServerHistoryService(matrix: Matrix): HomeServerHistoryService { - return matrix.homeServerHistoryService() - } - - @Provides - @JvmStatic - @Singleton - fun providesApplicationCoroutineScope(): CoroutineScope { - return CoroutineScope(SupervisorJob() + Dispatchers.Main) - } - - @Provides - @JvmStatic - fun providesCoroutineDispatchers(): CoroutineDispatchers { - return CoroutineDispatchers(io = Dispatchers.IO) - } - } +abstract class VectorBindModule { @Binds abstract fun bindNavigator(navigator: DefaultNavigator): Navigator @@ -129,3 +67,76 @@ abstract class VectorModule { @Binds abstract fun bindAutoAcceptInvites(autoAcceptInvites: CompileTimeAutoAcceptInvites): AutoAcceptInvites } + +@InstallIn(SingletonComponent::class) +@Module +object VectorStaticModule { + + @Provides + @JvmStatic + fun providesContext(application: Application): Context { + return application.applicationContext + } + + @Provides + @JvmStatic + fun providesResources(context: Context): Resources { + return context.resources + } + + @Provides + @JvmStatic + fun providesSharedPreferences(context: Context): SharedPreferences { + return context.getSharedPreferences("im.vector.riot", MODE_PRIVATE) + } + + @Provides + @JvmStatic + fun providesMatrix(context: Context): Matrix { + return Matrix.getInstance(context) + } + + @Provides + @JvmStatic + fun providesCurrentSession(activeSessionHolder: ActiveSessionHolder): Session { + // TODO: handle session injection better + return activeSessionHolder.getActiveSession() + } + + @Provides + @JvmStatic + fun providesLegacySessionImporter(matrix: Matrix): LegacySessionImporter { + return matrix.legacySessionImporter() + } + + @Provides + @JvmStatic + fun providesAuthenticationService(matrix: Matrix): AuthenticationService { + return matrix.authenticationService() + } + + @Provides + @JvmStatic + fun providesRawService(matrix: Matrix): RawService { + return matrix.rawService() + } + + @Provides + @JvmStatic + fun providesHomeServerHistoryService(matrix: Matrix): HomeServerHistoryService { + return matrix.homeServerHistoryService() + } + + @Provides + @JvmStatic + @Singleton + fun providesApplicationCoroutineScope(): CoroutineScope { + return CoroutineScope(SupervisorJob() + Dispatchers.Main) + } + + @Provides + @JvmStatic + fun providesCoroutineDispatchers(): CoroutineDispatchers { + return CoroutineDispatchers(io = Dispatchers.IO) + } +} diff --git a/vector/src/main/java/im/vector/app/core/di/VectorComponent.kt b/vector/src/main/java/im/vector/app/core/di/VectorComponent.kt deleted file mode 100644 index a8bf128367..0000000000 --- a/vector/src/main/java/im/vector/app/core/di/VectorComponent.kt +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2019 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.app.core.di - -import android.content.Context -import android.content.res.Resources -import dagger.BindsInstance -import dagger.Component -import im.vector.app.ActiveSessionDataSource -import im.vector.app.AppStateHandler -import im.vector.app.EmojiCompatFontProvider -import im.vector.app.EmojiCompatWrapper -import im.vector.app.VectorApplication -import im.vector.app.core.dialogs.UnrecognizedCertificateDialog -import im.vector.app.core.dispatchers.CoroutineDispatchers -import im.vector.app.core.error.ErrorFormatter -import im.vector.app.core.network.WifiDetector -import im.vector.app.core.pushers.PushersManager -import im.vector.app.core.utils.AssetReader -import im.vector.app.core.utils.DimensionConverter -import im.vector.app.features.call.conference.JitsiActiveConferenceHolder -import im.vector.app.features.call.webrtc.WebRtcCallManager -import im.vector.app.features.configuration.VectorConfiguration -import im.vector.app.features.crypto.keysrequest.KeyRequestHandler -import im.vector.app.features.crypto.verification.IncomingVerificationRequestHandler -import im.vector.app.features.home.AvatarRenderer -import im.vector.app.features.home.CurrentSpaceSuggestedRoomListDataSource -import im.vector.app.features.home.room.detail.RoomDetailPendingActionStore -import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider -import im.vector.app.features.html.EventHtmlRenderer -import im.vector.app.features.html.VectorHtmlCompressor -import im.vector.app.features.invite.AutoAcceptInvites -import im.vector.app.features.login.ReAuthHelper -import im.vector.app.features.navigation.Navigator -import im.vector.app.features.notifications.NotifiableEventResolver -import im.vector.app.features.notifications.NotificationBroadcastReceiver -import im.vector.app.features.notifications.NotificationDrawerManager -import im.vector.app.features.notifications.NotificationUtils -import im.vector.app.features.notifications.PushRuleTriggerListener -import im.vector.app.features.pin.PinCodeStore -import im.vector.app.features.pin.PinLocker -import im.vector.app.features.popup.PopupAlertManager -import im.vector.app.features.rageshake.BugReporter -import im.vector.app.features.rageshake.VectorFileLogger -import im.vector.app.features.rageshake.VectorUncaughtExceptionHandler -import im.vector.app.features.reactions.data.EmojiDataSource -import im.vector.app.features.session.SessionListener -import im.vector.app.features.settings.VectorDataStore -import im.vector.app.features.settings.VectorPreferences -import im.vector.app.features.ui.UiStateRepository -import kotlinx.coroutines.CoroutineScope -import org.matrix.android.sdk.api.Matrix -import org.matrix.android.sdk.api.auth.AuthenticationService -import org.matrix.android.sdk.api.auth.HomeServerHistoryService -import org.matrix.android.sdk.api.raw.RawService -import org.matrix.android.sdk.api.session.Session -import javax.inject.Singleton - -@Component(modules = [VectorModule::class]) -@Singleton -interface VectorComponent { - - fun inject(notificationBroadcastReceiver: NotificationBroadcastReceiver) - - fun inject(vectorApplication: VectorApplication) - - fun matrix(): Matrix - - fun matrixItemColorProvider(): MatrixItemColorProvider - - fun sessionListener(): SessionListener - - fun currentSession(): Session - - fun notificationUtils(): NotificationUtils - - fun notificationDrawerManager(): NotificationDrawerManager - - fun appContext(): Context - - fun resources(): Resources - - fun assetReader(): AssetReader - - fun dimensionConverter(): DimensionConverter - - fun vectorConfiguration(): VectorConfiguration - - fun avatarRenderer(): AvatarRenderer - - fun activeSessionHolder(): ActiveSessionHolder - - fun unrecognizedCertificateDialog(): UnrecognizedCertificateDialog - - fun emojiCompatFontProvider(): EmojiCompatFontProvider - - fun emojiCompatWrapper(): EmojiCompatWrapper - - fun eventHtmlRenderer(): EventHtmlRenderer - - fun vectorHtmlCompressor(): VectorHtmlCompressor - - fun navigator(): Navigator - - fun errorFormatter(): ErrorFormatter - - fun appStateHandler(): AppStateHandler - - fun currentSpaceSuggestedRoomListDataSource(): CurrentSpaceSuggestedRoomListDataSource - - fun roomDetailPendingActionStore(): RoomDetailPendingActionStore - - fun activeSessionObservableStore(): ActiveSessionDataSource - - fun incomingVerificationRequestHandler(): IncomingVerificationRequestHandler - - fun incomingKeyRequestHandler(): KeyRequestHandler - - fun authenticationService(): AuthenticationService - - fun rawService(): RawService - - fun homeServerHistoryService(): HomeServerHistoryService - - fun bugReporter(): BugReporter - - fun vectorUncaughtExceptionHandler(): VectorUncaughtExceptionHandler - - fun pushRuleTriggerListener(): PushRuleTriggerListener - - fun pusherManager(): PushersManager - - fun notifiableEventResolver(): NotifiableEventResolver - - fun vectorPreferences(): VectorPreferences - - fun vectorDataStore(): VectorDataStore - - fun wifiDetector(): WifiDetector - - fun vectorFileLogger(): VectorFileLogger - - fun uiStateRepository(): UiStateRepository - - fun pinCodeStore(): PinCodeStore - - fun emojiDataSource(): EmojiDataSource - - fun alertManager(): PopupAlertManager - - fun reAuthHelper(): ReAuthHelper - - fun pinLocker(): PinLocker - - fun autoAcceptInvites(): AutoAcceptInvites - - fun webRtcCallManager(): WebRtcCallManager - - fun appCoroutineScope(): CoroutineScope - - fun coroutineDispatchers(): CoroutineDispatchers - - fun jitsiActiveConferenceHolder(): JitsiActiveConferenceHolder - - @Component.Factory - interface Factory { - fun create(@BindsInstance context: Context): VectorComponent - } -} diff --git a/vector/src/main/java/im/vector/app/core/di/ViewModelKey.kt b/vector/src/main/java/im/vector/app/core/di/ViewModelKey.kt index 5f0ee30821..2782edd558 100644 --- a/vector/src/main/java/im/vector/app/core/di/ViewModelKey.kt +++ b/vector/src/main/java/im/vector/app/core/di/ViewModelKey.kt @@ -17,6 +17,7 @@ package im.vector.app.core.di import androidx.lifecycle.ViewModel +import com.airbnb.mvrx.MavericksViewModel import dagger.MapKey import kotlin.reflect.KClass @@ -24,3 +25,8 @@ import kotlin.reflect.KClass @Retention(AnnotationRetention.RUNTIME) @MapKey annotation class ViewModelKey(val value: KClass) + +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FUNCTION) +@MapKey +annotation class MavericksViewModelKey(val value: KClass>) diff --git a/vector/src/main/java/im/vector/app/core/di/ViewModelModule.kt b/vector/src/main/java/im/vector/app/core/di/ViewModelModule.kt index 4e07c1e2ca..4f8329c026 100644 --- a/vector/src/main/java/im/vector/app/core/di/ViewModelModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/ViewModelModule.kt @@ -20,6 +20,8 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import dagger.Binds import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ActivityComponent import dagger.multibindings.IntoMap import im.vector.app.core.platform.ConfigurationViewModel import im.vector.app.features.call.SharedKnownCallsViewModel @@ -42,6 +44,7 @@ import im.vector.app.features.spaces.SpacePreviewSharedActionViewModel import im.vector.app.features.spaces.people.SpacePeopleSharedActionViewModel import im.vector.app.features.userdirectory.UserListSharedActionViewModel +@InstallIn(ActivityComponent::class) @Module interface ViewModelModule { diff --git a/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileMatrixItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileMatrixItem.kt index e6451b34ef..0af342641e 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileMatrixItem.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileMatrixItem.kt @@ -32,6 +32,7 @@ abstract class ProfileMatrixItem : BaseProfileMatrixItem(R.id.matrixItemTitle) val subtitleView by bind(R.id.matrixItemSubtitle) + val powerLabel by bind(R.id.matrixItemPowerLevelLabel) val presenceImageView by bind(R.id.matrixItemPresenceImageView) val avatarImageView by bind(R.id.matrixItemAvatar) val avatarDecorationImageView by bind(R.id.matrixItemAvatarDecoration) diff --git a/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileMatrixItemWithPowerLevel.kt b/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileMatrixItemWithPowerLevel.kt index b7fd597789..12189dc8f4 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileMatrixItemWithPowerLevel.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileMatrixItemWithPowerLevel.kt @@ -17,7 +17,6 @@ package im.vector.app.core.epoxy.profiles -import android.widget.TextView import androidx.core.view.isVisible import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass @@ -25,7 +24,7 @@ import im.vector.app.R import im.vector.app.core.extensions.setTextOrHide @EpoxyModelClass(layout = R.layout.item_profile_matrix_item) -abstract class ProfileMatrixItemWithPowerLevel : BaseProfileMatrixItem() { +abstract class ProfileMatrixItemWithPowerLevel : ProfileMatrixItem() { @EpoxyAttribute var powerLevelLabel: CharSequence? = null @@ -34,8 +33,4 @@ abstract class ProfileMatrixItemWithPowerLevel : BaseProfileMatrixItem(R.id.matrixItemPowerLevelLabel) - } } diff --git a/vector/src/main/java/im/vector/app/core/extensions/Context.kt b/vector/src/main/java/im/vector/app/core/extensions/Context.kt index c1c435edf2..59847da7c9 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/Context.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/Context.kt @@ -17,14 +17,9 @@ package im.vector.app.core.extensions import android.content.Context -import im.vector.app.core.di.HasVectorInjector -import im.vector.app.core.di.VectorComponent +import dagger.hilt.EntryPoints +import im.vector.app.core.di.SingletonEntryPoint -fun Context.vectorComponent(): VectorComponent { - val appContext = applicationContext - if (appContext is HasVectorInjector) { - return appContext.injector() - } else { - throw IllegalStateException("Your application context doesn't implement HasVectorInjector") - } +fun Context.singletonEntryPoint(): SingletonEntryPoint { + return EntryPoints.get(applicationContext, SingletonEntryPoint::class.java) } diff --git a/vector/src/main/java/im/vector/app/core/extensions/Session.kt b/vector/src/main/java/im/vector/app/core/extensions/Session.kt index f066fd6784..90b08ef92b 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/Session.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/Session.kt @@ -34,7 +34,7 @@ fun Session.configureAndStart(context: Context, startSyncing: Boolean = true) { startSyncing(context) } refreshPushers() - context.vectorComponent().webRtcCallManager().checkForProtocolsSupportIfNeeded() + context.singletonEntryPoint().webRtcCallManager().checkForProtocolsSupportIfNeeded() } fun Session.startSyncing(context: Context) { diff --git a/vector/src/main/java/im/vector/app/core/glide/AvatarPlaceholder.kt b/vector/src/main/java/im/vector/app/core/glide/AvatarPlaceholder.kt index 9675e30042..e61f81de55 100644 --- a/vector/src/main/java/im/vector/app/core/glide/AvatarPlaceholder.kt +++ b/vector/src/main/java/im/vector/app/core/glide/AvatarPlaceholder.kt @@ -26,7 +26,7 @@ import com.bumptech.glide.load.model.ModelLoader import com.bumptech.glide.load.model.ModelLoaderFactory import com.bumptech.glide.load.model.MultiModelLoaderFactory import com.bumptech.glide.signature.ObjectKey -import im.vector.app.core.extensions.vectorComponent +import im.vector.app.core.extensions.singletonEntryPoint import org.matrix.android.sdk.api.util.MatrixItem data class AvatarPlaceholder(val matrixItem: MatrixItem) @@ -57,7 +57,7 @@ class AvatarPlaceholderModelLoader(private val context: Context) : class AvatarPlaceholderDataFetcher(context: Context, private val data: AvatarPlaceholder) : DataFetcher { - private val avatarRenderer = context.vectorComponent().avatarRenderer() + private val avatarRenderer = context.singletonEntryPoint().avatarRenderer() override fun loadData(priority: Priority, callback: DataFetcher.DataCallback) { val avatarPlaceholder = avatarRenderer.getPlaceholderDrawable(data.matrixItem) diff --git a/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt b/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt index 7dfee7d981..6b42e3fff8 100644 --- a/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt +++ b/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt @@ -25,7 +25,7 @@ import com.bumptech.glide.load.model.ModelLoader import com.bumptech.glide.load.model.ModelLoaderFactory import com.bumptech.glide.load.model.MultiModelLoaderFactory import com.bumptech.glide.signature.ObjectKey -import im.vector.app.core.extensions.vectorComponent +import im.vector.app.core.extensions.singletonEntryPoint import im.vector.app.core.files.LocalFilesHelper import im.vector.app.features.media.ImageContentRenderer import im.vector.app.features.session.coroutineScope @@ -67,7 +67,7 @@ class VectorGlideDataFetcher(context: Context, DataFetcher { private val localFilesHelper = LocalFilesHelper(context) - private val activeSessionHolder = context.vectorComponent().activeSessionHolder() + private val activeSessionHolder = context.singletonEntryPoint().activeSessionHolder() private val client = activeSessionHolder.getSafeActiveSession()?.getOkHttpClient() ?: OkHttpClient() diff --git a/vector/src/main/java/im/vector/app/core/platform/SimpleFragmentActivity.kt b/vector/src/main/java/im/vector/app/core/platform/SimpleFragmentActivity.kt index 7573bf2e8e..a70b2d66e6 100644 --- a/vector/src/main/java/im/vector/app/core/platform/SimpleFragmentActivity.kt +++ b/vector/src/main/java/im/vector/app/core/platform/SimpleFragmentActivity.kt @@ -15,13 +15,10 @@ */ package im.vector.app.core.platform -import androidx.annotation.CallSuper import androidx.core.view.isGone import androidx.core.view.isVisible -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.hideKeyboard import im.vector.app.databinding.ActivityBinding -import org.matrix.android.sdk.api.session.Session /** * Simple activity with a toolbar, a waiting overlay, and a fragment container and a session. @@ -32,13 +29,6 @@ abstract class SimpleFragmentActivity : VectorBaseActivity() { final override fun getCoordinatorLayout() = views.coordinatorLayout - lateinit var session: Session - - @CallSuper - override fun injectWith(injector: ScreenComponent) { - session = injector.activeSessionHolder().getActiveSession() - } - override fun initUiAndData() { configureToolbar(views.toolbar) waitingView = views.waitingView.waitingView diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt index fbfba10d21..4d06dbe6a2 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt @@ -45,14 +45,11 @@ import com.bumptech.glide.util.Util import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.snackbar.Snackbar import com.jakewharton.rxbinding3.view.clicks +import dagger.hilt.android.EntryPointAccessors import im.vector.app.BuildConfig import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder -import im.vector.app.core.di.DaggerScreenComponent -import im.vector.app.core.di.HasScreenInjector -import im.vector.app.core.di.HasVectorInjector -import im.vector.app.core.di.ScreenComponent -import im.vector.app.core.di.VectorComponent +import im.vector.app.core.di.ActivityEntryPoint import im.vector.app.core.dialogs.DialogLocker import im.vector.app.core.dialogs.UnrecognizedCertificateDialog import im.vector.app.core.extensions.exhaustive @@ -61,7 +58,7 @@ import im.vector.app.core.extensions.observeNotNull import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.restart import im.vector.app.core.extensions.setTextOrHide -import im.vector.app.core.extensions.vectorComponent +import im.vector.app.core.extensions.singletonEntryPoint import im.vector.app.core.utils.toast import im.vector.app.features.MainActivity import im.vector.app.features.MainActivityArgs @@ -87,9 +84,9 @@ import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.failure.GlobalError import timber.log.Timber import java.util.concurrent.TimeUnit -import kotlin.system.measureTimeMillis +import javax.inject.Inject -abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector, MavericksView { +abstract class VectorBaseActivity : AppCompatActivity(), MavericksView { /* ========================================================================================== * View * ========================================================================================== */ @@ -136,8 +133,8 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasSc private lateinit var sessionListener: SessionListener protected lateinit var bugReporter: BugReporter private lateinit var pinLocker: PinLocker + @Inject lateinit var rageShake: RageShake - lateinit var navigator: Navigator private set private lateinit var fragmentFactory: FragmentFactory @@ -156,8 +153,6 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasSc private val uiDisposables = CompositeDisposable() private val restorables = ArrayList() - private lateinit var screenComponent: ScreenComponent - override fun attachBaseContext(base: Context) { val vectorConfiguration = VectorConfiguration(this) super.attachBaseContext(vectorConfiguration.getLocalisedContext(base)) @@ -187,25 +182,19 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasSc @CallSuper override fun onCreate(savedInstanceState: Bundle?) { Timber.i("onCreate Activity ${javaClass.simpleName}") - val vectorComponent = getVectorComponent() - screenComponent = DaggerScreenComponent.factory().create(vectorComponent, this) - val timeForInjection = measureTimeMillis { - injectWith(screenComponent) - } - Timber.v("Injecting dependencies into ${javaClass.simpleName} took $timeForInjection ms") + val singletonEntryPoint = singletonEntryPoint() + val activityEntryPoint = EntryPointAccessors.fromActivity(this, ActivityEntryPoint::class.java) ThemeUtils.setActivityTheme(this, getOtherThemes()) - fragmentFactory = screenComponent.fragmentFactory() + fragmentFactory = activityEntryPoint.fragmentFactory() supportFragmentManager.fragmentFactory = fragmentFactory + viewModelFactory = activityEntryPoint.viewModelFactory() super.onCreate(savedInstanceState) - viewModelFactory = screenComponent.viewModelFactory() configurationViewModel = viewModelProvider.get(ConfigurationViewModel::class.java) - bugReporter = screenComponent.bugReporter() - pinLocker = screenComponent.pinLocker() - // Shake detector - rageShake = screenComponent.rageShake() - navigator = screenComponent.navigator() - activeSessionHolder = screenComponent.activeSessionHolder() - vectorPreferences = vectorComponent.vectorPreferences() + bugReporter = singletonEntryPoint.bugReporter() + pinLocker = singletonEntryPoint.pinLocker() + navigator = singletonEntryPoint.navigator() + activeSessionHolder = singletonEntryPoint.activeSessionHolder() + vectorPreferences = singletonEntryPoint.vectorPreferences() configurationViewModel.activityRestarter.observe(this) { if (!it.hasBeenHandled) { // Recreate the Activity because configuration has changed @@ -217,7 +206,7 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasSc navigator.openPinCode(this, pinStartForActivityResult, PinMode.AUTH) } } - sessionListener = vectorComponent.sessionListener() + sessionListener = singletonEntryPoint.sessionListener() sessionListener.globalErrorLiveData.observeEvent(this) { handleGlobalError(it) } @@ -273,7 +262,7 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasSc } private fun handleCertificateError(certificateError: GlobalError.CertificateError) { - vectorComponent() + singletonEntryPoint() .unrecognizedCertificateDialog() .show(this, certificateError.fingerprint, @@ -403,12 +392,6 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasSc bugReporter.inMultiWindowMode = isInMultiWindowMode } - override fun injector(): ScreenComponent { - return screenComponent - } - - protected open fun injectWith(injector: ScreenComponent) = Unit - protected fun createFragment(fragmentClass: Class, args: Bundle?): Fragment { return fragmentFactory.instantiate(classLoader, fragmentClass.name).apply { arguments = args @@ -419,10 +402,6 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasSc * PRIVATE METHODS * ========================================================================================== */ - internal fun getVectorComponent(): VectorComponent { - return (application as HasVectorInjector).injector() - } - /** * Force to render the activity in fullscreen */ diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt index 68765d615e..711b2b144b 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt @@ -33,8 +33,8 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.jakewharton.rxbinding3.view.clicks -import im.vector.app.core.di.DaggerScreenComponent -import im.vector.app.core.di.ScreenComponent +import dagger.hilt.android.EntryPointAccessors +import im.vector.app.core.di.ActivityEntryPoint import im.vector.app.core.utils.DimensionConverter import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable @@ -47,8 +47,6 @@ import java.util.concurrent.TimeUnit */ abstract class VectorBaseBottomSheetDialogFragment : BottomSheetDialogFragment(), MavericksView { - private lateinit var screenComponent: ScreenComponent - /* ========================================================================================== * View * ========================================================================================== */ @@ -122,14 +120,11 @@ abstract class VectorBaseBottomSheetDialogFragment : BottomShe } override fun onAttach(context: Context) { - screenComponent = DaggerScreenComponent.factory().create(vectorBaseActivity.getVectorComponent(), vectorBaseActivity) - viewModelFactory = screenComponent.viewModelFactory() + val activityEntryPoint = EntryPointAccessors.fromActivity(vectorBaseActivity, ActivityEntryPoint::class.java) + viewModelFactory = activityEntryPoint.viewModelFactory() super.onAttach(context) - injectWith(screenComponent) } - protected open fun injectWith(injector: ScreenComponent) = Unit - override fun onResume() { super.onResume() Timber.i("onResume BottomSheet ${javaClass.simpleName}") diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt index 64b55291fb..d3c66ec61d 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt @@ -35,12 +35,12 @@ import com.bumptech.glide.util.Util.assertMainThread import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.jakewharton.rxbinding3.view.clicks +import dagger.hilt.android.EntryPointAccessors import im.vector.app.R -import im.vector.app.core.di.DaggerScreenComponent -import im.vector.app.core.di.HasScreenInjector -import im.vector.app.core.di.ScreenComponent +import im.vector.app.core.di.ActivityEntryPoint import im.vector.app.core.dialogs.UnrecognizedCertificateDialog import im.vector.app.core.error.ErrorFormatter +import im.vector.app.core.extensions.singletonEntryPoint import im.vector.app.core.extensions.toMvRxBundle import im.vector.app.features.navigation.Navigator import im.vector.lib.ui.styles.dialogs.MaterialProgressDialog @@ -50,7 +50,7 @@ import io.reactivex.disposables.Disposable import timber.log.Timber import java.util.concurrent.TimeUnit -abstract class VectorBaseFragment : Fragment(), MavericksView, HasScreenInjector { +abstract class VectorBaseFragment : Fragment(), MavericksView { protected val vectorBaseActivity: VectorBaseActivity<*> by lazy { activity as VectorBaseActivity<*> @@ -60,8 +60,6 @@ abstract class VectorBaseFragment : Fragment(), MavericksView, * Navigator and other common objects * ========================================================================================== */ - private lateinit var screenComponent: ScreenComponent - protected lateinit var navigator: Navigator protected lateinit var errorFormatter: ErrorFormatter protected lateinit var unrecognizedCertificateDialog: UnrecognizedCertificateDialog @@ -95,12 +93,13 @@ abstract class VectorBaseFragment : Fragment(), MavericksView, * ========================================================================================== */ override fun onAttach(context: Context) { - screenComponent = DaggerScreenComponent.factory().create(vectorBaseActivity.getVectorComponent(), vectorBaseActivity) - navigator = screenComponent.navigator() - errorFormatter = screenComponent.errorFormatter() - unrecognizedCertificateDialog = screenComponent.unrecognizedCertificateDialog() - viewModelFactory = screenComponent.viewModelFactory() - childFragmentManager.fragmentFactory = screenComponent.fragmentFactory() + val singletonEntryPoint = context.singletonEntryPoint() + val activityEntryPoint = EntryPointAccessors.fromActivity(vectorBaseActivity, ActivityEntryPoint::class.java) + navigator = singletonEntryPoint.navigator() + errorFormatter = singletonEntryPoint.errorFormatter() + unrecognizedCertificateDialog = singletonEntryPoint.unrecognizedCertificateDialog() + viewModelFactory = activityEntryPoint.viewModelFactory() + childFragmentManager.fragmentFactory = activityEntryPoint.fragmentFactory() super.onAttach(context) } @@ -161,10 +160,6 @@ abstract class VectorBaseFragment : Fragment(), MavericksView, super.onDestroy() } - override fun injector(): ScreenComponent { - return screenComponent - } - /* ========================================================================================== * Restorable * ========================================================================================== */ diff --git a/vector/src/main/java/im/vector/app/core/preference/UserAvatarPreference.kt b/vector/src/main/java/im/vector/app/core/preference/UserAvatarPreference.kt index 3bb50c6284..3095f98ea5 100755 --- a/vector/src/main/java/im/vector/app/core/preference/UserAvatarPreference.kt +++ b/vector/src/main/java/im/vector/app/core/preference/UserAvatarPreference.kt @@ -23,7 +23,7 @@ import android.widget.ProgressBar import androidx.preference.Preference import androidx.preference.PreferenceViewHolder import im.vector.app.R -import im.vector.app.core.extensions.vectorComponent +import im.vector.app.core.extensions.singletonEntryPoint import im.vector.app.features.home.AvatarRenderer import org.matrix.android.sdk.api.session.user.model.User import org.matrix.android.sdk.api.util.MatrixItem @@ -33,7 +33,7 @@ class UserAvatarPreference : Preference { private var mAvatarView: ImageView? = null private var mLoadingProgressBar: ProgressBar? = null - private var avatarRenderer: AvatarRenderer = context.vectorComponent().avatarRenderer() + private var avatarRenderer: AvatarRenderer = context.singletonEntryPoint().avatarRenderer() private var userItem: MatrixItem.UserItem? = null diff --git a/vector/src/main/java/im/vector/app/core/services/CallRingPlayer.kt b/vector/src/main/java/im/vector/app/core/services/CallRingPlayer.kt index faa921b99e..524ff37914 100644 --- a/vector/src/main/java/im/vector/app/core/services/CallRingPlayer.kt +++ b/vector/src/main/java/im/vector/app/core/services/CallRingPlayer.kt @@ -62,6 +62,10 @@ class CallRingPlayerIncoming( val ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE) ringtone = RingtoneManager.getRingtone(applicationContext, ringtoneUri) Timber.v("Play ringtone for incoming call") + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + ringtone?.isLooping = true + } ringtone?.play() } diff --git a/vector/src/main/java/im/vector/app/core/services/CallService.kt b/vector/src/main/java/im/vector/app/core/services/CallService.kt index 5e07bb76c6..d194434641 100644 --- a/vector/src/main/java/im/vector/app/core/services/CallService.kt +++ b/vector/src/main/java/im/vector/app/core/services/CallService.kt @@ -26,7 +26,8 @@ import androidx.core.app.NotificationManagerCompat import androidx.core.content.ContextCompat import androidx.media.session.MediaButtonReceiver import com.airbnb.mvrx.Mavericks -import im.vector.app.core.extensions.vectorComponent +import dagger.hilt.android.AndroidEntryPoint +import im.vector.app.core.extensions.singletonEntryPoint import im.vector.app.features.call.CallArgs import im.vector.app.features.call.VectorCallActivity import im.vector.app.features.call.telecom.CallConnection @@ -42,23 +43,25 @@ import org.matrix.android.sdk.api.logger.LoggerTag import org.matrix.android.sdk.api.session.room.model.call.EndCallReason import org.matrix.android.sdk.api.util.MatrixItem import timber.log.Timber +import javax.inject.Inject private val loggerTag = LoggerTag("CallService", LoggerTag.VOIP) /** * Foreground service to manage calls */ +@AndroidEntryPoint class CallService : VectorService() { private val connections = mutableMapOf() private val knownCalls = mutableMapOf() private val connectedCallIds = mutableSetOf() - private lateinit var notificationManager: NotificationManagerCompat - private lateinit var notificationUtils: NotificationUtils - private lateinit var callManager: WebRtcCallManager - private lateinit var avatarRenderer: AvatarRenderer - private lateinit var alertManager: PopupAlertManager + lateinit var notificationManager: NotificationManagerCompat + @Inject lateinit var notificationUtils: NotificationUtils + @Inject lateinit var callManager: WebRtcCallManager + @Inject lateinit var avatarRenderer: AvatarRenderer + @Inject lateinit var alertManager: PopupAlertManager private var callRingPlayerIncoming: CallRingPlayerIncoming? = null private var callRingPlayerOutgoing: CallRingPlayerOutgoing? = null @@ -80,10 +83,6 @@ class CallService : VectorService() { override fun onCreate() { super.onCreate() notificationManager = NotificationManagerCompat.from(this) - notificationUtils = vectorComponent().notificationUtils() - callManager = vectorComponent().webRtcCallManager() - avatarRenderer = vectorComponent().avatarRenderer() - alertManager = vectorComponent().alertManager() callRingPlayerIncoming = CallRingPlayerIncoming(applicationContext, notificationUtils) callRingPlayerOutgoing = CallRingPlayerOutgoing(applicationContext) } @@ -298,7 +297,7 @@ class CallService : VectorService() { callId = this.callId, nativeRoomId = this.nativeRoomId, opponentUserId = this.mxCall.opponentUserId, - opponentMatrixItem = vectorComponent().activeSessionHolder().getSafeActiveSession()?.let { + opponentMatrixItem = singletonEntryPoint().activeSessionHolder().getSafeActiveSession()?.let { this.getOpponentAsMatrixItem(it) }, isVideoCall = this.mxCall.isVideoCall, diff --git a/vector/src/main/java/im/vector/app/core/di/HasVectorInjector.kt b/vector/src/main/java/im/vector/app/core/services/GuardServiceStarter.kt similarity index 80% rename from vector/src/main/java/im/vector/app/core/di/HasVectorInjector.kt rename to vector/src/main/java/im/vector/app/core/services/GuardServiceStarter.kt index 79254defcc..71c6816cea 100644 --- a/vector/src/main/java/im/vector/app/core/di/HasVectorInjector.kt +++ b/vector/src/main/java/im/vector/app/core/services/GuardServiceStarter.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019 New Vector Ltd + * Copyright (c) 2021 New Vector Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,9 +14,9 @@ * limitations under the License. */ -package im.vector.app.core.di +package im.vector.app.core.services -interface HasVectorInjector { - - fun injector(): VectorComponent +interface GuardServiceStarter { + fun start() {} + fun stop() {} } diff --git a/vector/src/main/java/im/vector/app/core/services/VectorSyncService.kt b/vector/src/main/java/im/vector/app/core/services/VectorSyncService.kt index 2a00e94976..08db8227a6 100644 --- a/vector/src/main/java/im/vector/app/core/services/VectorSyncService.kt +++ b/vector/src/main/java/im/vector/app/core/services/VectorSyncService.kt @@ -30,13 +30,15 @@ import androidx.work.WorkManager import androidx.work.WorkRequest import androidx.work.Worker import androidx.work.WorkerParameters +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.extensions.vectorComponent import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.settings.BackgroundSyncMode import org.matrix.android.sdk.internal.session.sync.job.SyncService import timber.log.Timber +import javax.inject.Inject +@AndroidEntryPoint class VectorSyncService : SyncService() { companion object { @@ -71,12 +73,7 @@ class VectorSyncService : SyncService() { } } - private lateinit var notificationUtils: NotificationUtils - - override fun onCreate() { - super.onCreate() - notificationUtils = vectorComponent().notificationUtils() - } + @Inject lateinit var notificationUtils: NotificationUtils override fun getDefaultSyncDelaySeconds() = BackgroundSyncMode.DEFAULT_SYNC_DELAY_SECONDS @@ -86,7 +83,7 @@ class VectorSyncService : SyncService() { val notificationSubtitleRes = if (isInitialSync) { R.string.notification_initial_sync } else { - R.string.notification_listening_for_events + R.string.notification_listening_for_notifications } val notification = notificationUtils.buildForegroundServiceNotification(notificationSubtitleRes, false) startForeground(NotificationUtils.NOTIFICATION_ID_FOREGROUND_SERVICE, notification) diff --git a/vector/src/main/java/im/vector/app/features/MainActivity.kt b/vector/src/main/java/im/vector/app/features/MainActivity.kt index e124dbdf6e..8e0995b426 100644 --- a/vector/src/main/java/im/vector/app/features/MainActivity.kt +++ b/vector/src/main/java/im/vector/app/features/MainActivity.kt @@ -24,9 +24,9 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import com.bumptech.glide.Glide import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.extensions.startSyncing import im.vector.app.core.platform.VectorBaseActivity @@ -67,6 +67,7 @@ data class MainActivityArgs( * This Activity, when started with argument, is also doing some cleanup when user signs out, * clears cache, is logged out, or is soft logged out */ +@AndroidEntryPoint class MainActivity : VectorBaseActivity(), UnlockedActivity { companion object { @@ -98,10 +99,6 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity @Inject lateinit var pinLocker: PinLocker @Inject lateinit var popupAlertManager: PopupAlertManager - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) args = parseArgs() diff --git a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewActivity.kt b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewActivity.kt index 6c25f688bd..939dd9f11d 100644 --- a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewActivity.kt +++ b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewActivity.kt @@ -20,6 +20,7 @@ package im.vector.app.features.attachments.preview import android.content.Context import android.content.Intent import com.google.android.material.appbar.MaterialToolbar +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R import im.vector.app.core.extensions.addFragment import im.vector.app.core.platform.ToolbarConfigurable @@ -28,6 +29,7 @@ import im.vector.app.databinding.ActivitySimpleBinding import im.vector.app.features.themes.ActivityOtherThemes import org.matrix.android.sdk.api.session.content.ContentAttachmentData +@AndroidEntryPoint class AttachmentsPreviewActivity : VectorBaseActivity(), ToolbarConfigurable { companion object { diff --git a/vector/src/main/java/im/vector/app/features/auth/ReAuthActivity.kt b/vector/src/main/java/im/vector/app/features/auth/ReAuthActivity.kt index b7f570672b..e3c9795b13 100644 --- a/vector/src/main/java/im/vector/app/features/auth/ReAuthActivity.kt +++ b/vector/src/main/java/im/vector/app/features/auth/ReAuthActivity.kt @@ -29,8 +29,8 @@ import androidx.browser.customtabs.CustomTabsSession import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.withState +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.addFragment import im.vector.app.core.platform.SimpleFragmentActivity import im.vector.app.core.utils.openUrlInChromeCustomTab @@ -42,7 +42,8 @@ import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage import timber.log.Timber import javax.inject.Inject -class ReAuthActivity : SimpleFragmentActivity(), ReAuthViewModel.Factory { +@AndroidEntryPoint +class ReAuthActivity : SimpleFragmentActivity() { @Parcelize data class Args( @@ -59,14 +60,6 @@ class ReAuthActivity : SimpleFragmentActivity(), ReAuthViewModel.Factory { private var customTabsSession: CustomTabsSession? = null @Inject lateinit var authenticationService: AuthenticationService - @Inject lateinit var reAuthViewModelFactory: ReAuthViewModel.Factory - - override fun create(initialState: ReAuthState) = reAuthViewModelFactory.create(initialState) - - override fun injectWith(injector: ScreenComponent) { - super.injectWith(injector) - injector.inject(this) - } private val sharedViewModel: ReAuthViewModel by viewModel() diff --git a/vector/src/main/java/im/vector/app/features/auth/ReAuthViewModel.kt b/vector/src/main/java/im/vector/app/features/auth/ReAuthViewModel.kt index 449270644e..830fd4e79d 100644 --- a/vector/src/main/java/im/vector/app/features/auth/ReAuthViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/auth/ReAuthViewModel.kt @@ -16,13 +16,12 @@ package im.vector.app.features.auth -import com.airbnb.mvrx.ActivityViewModelContext -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory -import com.airbnb.mvrx.ViewModelContext 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.VectorViewModel import org.matrix.android.sdk.api.auth.data.LoginFlowTypes import org.matrix.android.sdk.api.session.Session @@ -35,20 +34,11 @@ class ReAuthViewModel @AssistedInject constructor( ) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: ReAuthState): ReAuthViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: ReAuthState): ReAuthViewModel } - companion object : MavericksViewModelFactory { - - override fun create(viewModelContext: ViewModelContext, state: ReAuthState): ReAuthViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() override fun handle(action: ReAuthActions) = withState { state -> when (action) { diff --git a/vector/src/main/java/im/vector/app/features/call/CallControlsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/call/CallControlsBottomSheet.kt index f9e2338077..b4f49db781 100644 --- a/vector/src/main/java/im/vector/app/features/call/CallControlsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/call/CallControlsBottomSheet.kt @@ -23,10 +23,12 @@ import android.view.ViewGroup import androidx.core.content.ContextCompat import androidx.core.view.isVisible import com.airbnb.mvrx.activityViewModel +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.databinding.BottomSheetCallControlsBinding +@AndroidEntryPoint class CallControlsBottomSheet : VectorBaseBottomSheetDialogFragment() { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetCallControlsBinding { return BottomSheetCallControlsBinding.inflate(inflater, container, false) diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt index 0654942d4b..879a357dfb 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt @@ -41,8 +41,8 @@ import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.withState import com.google.android.material.card.MaterialCardView import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.utils.PERMISSIONS_FOR_AUDIO_IP_CALL @@ -85,21 +85,16 @@ data class CallArgs( private val loggerTag = LoggerTag("VectorCallActivity", LoggerTag.VOIP) +@AndroidEntryPoint class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionListener { override fun getBinding() = ActivityCallBinding.inflate(layoutInflater) + @Inject lateinit var callManager: WebRtcCallManager @Inject lateinit var avatarRenderer: AvatarRenderer - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - private val callViewModel: VectorCallViewModel by viewModel() - @Inject lateinit var callManager: WebRtcCallManager - @Inject lateinit var viewModelFactory: VectorCallViewModel.Factory - private val dialPadCallback = object : DialPadFragment.Callback { override fun onDigitAppended(digit: String) { callViewModel.handle(VectorCallViewActions.SendDtmfDigit(digit)) diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt index d8aaca9bd8..5af2b826af 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt @@ -21,10 +21,11 @@ import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success -import com.airbnb.mvrx.ViewModelContext 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.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.call.audio.CallAudioManager @@ -341,16 +342,9 @@ class VectorCallViewModel @AssistedInject constructor( } @AssistedFactory - interface Factory { - fun create(initialState: VectorCallViewState): VectorCallViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: VectorCallViewState): VectorCallViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: VectorCallViewState): VectorCallViewModel { - val callActivity: VectorCallActivity = viewModelContext.activity() - return callActivity.viewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() } diff --git a/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewModel.kt b/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewModel.kt index c46b4e459d..d04bebfd1b 100644 --- a/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewModel.kt @@ -22,10 +22,11 @@ import com.airbnb.mvrx.Fail import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext 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.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.Job @@ -46,8 +47,8 @@ class JitsiCallViewModel @AssistedInject constructor( ) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: JitsiCallViewState): JitsiCallViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: JitsiCallViewState): JitsiCallViewModel } private var currentWidgetObserver: Job? = null @@ -143,24 +144,7 @@ class JitsiCallViewModel @AssistedInject constructor( } } - companion object : MavericksViewModelFactory { - + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() { const val ENABLE_VIDEO_OPTION = "ENABLE_VIDEO_OPTION" - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: JitsiCallViewState): JitsiCallViewModel? { - val callActivity: VectorJitsiActivity = viewModelContext.activity() - return callActivity.viewModelFactory.create(state) - } - - override fun initialState(viewModelContext: ViewModelContext): JitsiCallViewState? { - val args: VectorJitsiActivity.Args = viewModelContext.args() - - return JitsiCallViewState( - roomId = args.roomId, - widgetId = args.widgetId, - enableVideo = args.enableVideo - ) - } } } diff --git a/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewState.kt b/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewState.kt index d4c70d7333..7ecd44b9b0 100644 --- a/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewState.kt +++ b/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewState.kt @@ -26,4 +26,11 @@ data class JitsiCallViewState( val widgetId: String = "", val enableVideo: Boolean = false, val widget: Async = Uninitialized -) : MavericksState +) : MavericksState { + + constructor(args: VectorJitsiActivity.Args) : this( + roomId = args.roomId, + widgetId = args.widgetId, + enableVideo = args.enableVideo + ) +} diff --git a/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt b/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt index 62d017467f..3fcefc9c8e 100644 --- a/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt @@ -33,8 +33,8 @@ import com.airbnb.mvrx.Success import com.airbnb.mvrx.viewModel import com.facebook.react.modules.core.PermissionListener import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivityJitsiBinding @@ -48,8 +48,8 @@ import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.util.JsonDict import timber.log.Timber import java.net.URL -import javax.inject.Inject +@AndroidEntryPoint class VectorJitsiActivity : VectorBaseActivity(), JitsiMeetActivityInterface { @Parcelize @@ -61,16 +61,10 @@ class VectorJitsiActivity : VectorBaseActivity(), JitsiMee override fun getBinding() = ActivityJitsiBinding.inflate(layoutInflater) - @Inject lateinit var viewModelFactory: JitsiCallViewModel.Factory - private var jitsiMeetView: JitsiMeetView? = null private val jitsiViewModel: JitsiCallViewModel by viewModel() - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/vector/src/main/java/im/vector/app/features/call/service/CallHeadsUpActionReceiver.kt b/vector/src/main/java/im/vector/app/features/call/service/CallHeadsUpActionReceiver.kt index 5a1d8cd396..161aa33d1d 100644 --- a/vector/src/main/java/im/vector/app/features/call/service/CallHeadsUpActionReceiver.kt +++ b/vector/src/main/java/im/vector/app/features/call/service/CallHeadsUpActionReceiver.kt @@ -19,7 +19,7 @@ package im.vector.app.features.call.service import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import im.vector.app.core.di.HasVectorInjector +import im.vector.app.core.extensions.singletonEntryPoint import im.vector.app.features.call.webrtc.WebRtcCallManager import timber.log.Timber @@ -32,11 +32,7 @@ class CallHeadsUpActionReceiver : BroadcastReceiver() { } override fun onReceive(context: Context, intent: Intent?) { - val webRtcCallManager = (context.applicationContext as? HasVectorInjector) - ?.injector() - ?.webRtcCallManager() - ?: return - + val webRtcCallManager = context.singletonEntryPoint().webRtcCallManager() when (intent?.getIntExtra(EXTRA_CALL_ACTION_KEY, 0)) { CALL_ACTION_REJECT -> { val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: return diff --git a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferActivity.kt b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferActivity.kt index 2a50dc85f9..c03b526f8c 100644 --- a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferActivity.kt @@ -23,15 +23,11 @@ import android.os.Parcelable import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import com.google.android.material.tabs.TabLayoutMediator +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivityCallTransferBinding -import im.vector.app.features.contactsbook.ContactsBookViewModel -import im.vector.app.features.contactsbook.ContactsBookViewState -import im.vector.app.features.userdirectory.UserListViewModel -import im.vector.app.features.userdirectory.UserListViewState import kotlinx.parcelize.Parcelize import javax.inject.Inject @@ -40,14 +36,9 @@ data class CallTransferArgs(val callId: String) : Parcelable private const val USER_LIST_FRAGMENT_TAG = "USER_LIST_FRAGMENT_TAG" -class CallTransferActivity : VectorBaseActivity(), - CallTransferViewModel.Factory, - UserListViewModel.Factory, - ContactsBookViewModel.Factory { +@AndroidEntryPoint +class CallTransferActivity : VectorBaseActivity() { - @Inject lateinit var userListViewModelFactory: UserListViewModel.Factory - @Inject lateinit var callTransferViewModelFactory: CallTransferViewModel.Factory - @Inject lateinit var contactsBookViewModelFactory: ContactsBookViewModel.Factory @Inject lateinit var errorFormatter: ErrorFormatter private lateinit var sectionsPagerAdapter: CallTransferPagerAdapter @@ -58,22 +49,6 @@ class CallTransferActivity : VectorBaseActivity(), override fun getCoordinatorLayout() = views.vectorCoordinatorLayout - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - - override fun create(initialState: UserListViewState): UserListViewModel { - return userListViewModelFactory.create(initialState) - } - - override fun create(initialState: CallTransferViewState): CallTransferViewModel { - return callTransferViewModelFactory.create(initialState) - } - - override fun create(initialState: ContactsBookViewState): ContactsBookViewModel { - return contactsBookViewModelFactory.create(initialState) - } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) waitingView = views.waitingView.waitingView diff --git a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewModel.kt b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewModel.kt index a26b03a3aa..ffc6ff9bc3 100644 --- a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferViewModel.kt @@ -16,13 +16,12 @@ package im.vector.app.features.call.transfer -import androidx.lifecycle.viewModelScope -import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory -import com.airbnb.mvrx.ViewModelContext 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.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.call.dialpad.DialPadLookup @@ -41,18 +40,11 @@ class CallTransferViewModel @AssistedInject constructor(@Assisted initialState: VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: CallTransferViewState): CallTransferViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: CallTransferViewState): CallTransferViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: CallTransferViewState): CallTransferViewModel? { - val activity: CallTransferActivity = (viewModelContext as ActivityViewModelContext).activity() - return activity.callTransferViewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() private val call = callManager.getCallById(initialState.callId) private val callListener = object : WebRtcCall.Listener { diff --git a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt index ea1841d870..d79ad308de 100644 --- a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt +++ b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt @@ -43,9 +43,8 @@ import java.util.concurrent.TimeUnit import javax.inject.Inject class ContactsBookFragment @Inject constructor( - private val contactsBookViewModelFactory: ContactsBookViewModel.Factory, private val contactsBookController: ContactsBookController -) : VectorBaseFragment(), ContactsBookController.Callback, ContactsBookViewModel.Factory { +) : VectorBaseFragment(), ContactsBookController.Callback { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentContactsBookBinding { return FragmentContactsBookBinding.inflate(inflater, container, false) @@ -58,10 +57,6 @@ class ContactsBookFragment @Inject constructor( private lateinit var sharedActionViewModel: UserListSharedActionViewModel - override fun create(initialState: ContactsBookViewState): ContactsBookViewModel { - return contactsBookViewModelFactory.create(initialState) - } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) sharedActionViewModel = activityViewModelProvider.get(UserListSharedActionViewModel::class.java) diff --git a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt index 6b5a6465a6..7f0dbcb7b2 100644 --- a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt @@ -17,17 +17,16 @@ package im.vector.app.features.contactsbook import androidx.lifecycle.viewModelScope -import com.airbnb.mvrx.ActivityViewModelContext -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success -import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.core.contacts.ContactsDataSource import im.vector.app.core.contacts.MappedContact +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel @@ -45,20 +44,11 @@ class ContactsBookViewModel @AssistedInject constructor(@Assisted VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: ContactsBookViewState): ContactsBookViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: ContactsBookViewState): ContactsBookViewModel } - companion object : MavericksViewModelFactory { - - override fun create(viewModelContext: ViewModelContext, state: ContactsBookViewState): ContactsBookViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() private var allContacts: List = emptyList() private var mappedContacts: List = emptyList() diff --git a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt index ae3af4b3e9..28da72714a 100644 --- a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt +++ b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt @@ -28,8 +28,8 @@ import com.airbnb.mvrx.Loading import com.airbnb.mvrx.Success import com.airbnb.mvrx.viewModel import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.extensions.addFragment import im.vector.app.core.extensions.addFragmentToBackstack @@ -42,39 +42,22 @@ import im.vector.app.core.utils.checkPermissions import im.vector.app.core.utils.onPermissionDeniedSnackbar import im.vector.app.core.utils.registerForPermissionsResult import im.vector.app.features.contactsbook.ContactsBookFragment -import im.vector.app.features.contactsbook.ContactsBookViewModel -import im.vector.app.features.contactsbook.ContactsBookViewState import im.vector.app.features.userdirectory.UserListFragment import im.vector.app.features.userdirectory.UserListFragmentArgs import im.vector.app.features.userdirectory.UserListSharedAction import im.vector.app.features.userdirectory.UserListSharedActionViewModel -import im.vector.app.features.userdirectory.UserListViewModel -import im.vector.app.features.userdirectory.UserListViewState import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure import java.net.HttpURLConnection import javax.inject.Inject -class CreateDirectRoomActivity : SimpleFragmentActivity(), UserListViewModel.Factory, CreateDirectRoomViewModel.Factory, ContactsBookViewModel.Factory { +@AndroidEntryPoint +class CreateDirectRoomActivity : SimpleFragmentActivity() { private val viewModel: CreateDirectRoomViewModel by viewModel() private lateinit var sharedActionViewModel: UserListSharedActionViewModel - @Inject lateinit var userListViewModelFactory: UserListViewModel.Factory - @Inject lateinit var createDirectRoomViewModelFactory: CreateDirectRoomViewModel.Factory - @Inject lateinit var contactsBookViewModelFactory: ContactsBookViewModel.Factory @Inject lateinit var errorFormatter: ErrorFormatter - override fun injectWith(injector: ScreenComponent) { - super.injectWith(injector) - injector.inject(this) - } - - override fun create(initialState: UserListViewState) = userListViewModelFactory.create(initialState) - - override fun create(initialState: CreateDirectRoomViewState) = createDirectRoomViewModelFactory.create(initialState) - - override fun create(initialState: ContactsBookViewState) = contactsBookViewModelFactory.create(initialState) - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) views.toolbar.visibility = View.GONE diff --git a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt index 347dcdc410..41360eab93 100644 --- a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt @@ -16,16 +16,14 @@ package im.vector.app.features.createdirect -import androidx.lifecycle.viewModelScope -import com.airbnb.mvrx.ActivityViewModelContext -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success -import com.airbnb.mvrx.ViewModelContext 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.extensions.exhaustive import im.vector.app.core.mvrx.runCatchingToAsync import im.vector.app.core.platform.VectorViewModel @@ -45,21 +43,11 @@ class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: CreateDirectRoomViewState): CreateDirectRoomViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: CreateDirectRoomViewState): CreateDirectRoomViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: CreateDirectRoomViewState): CreateDirectRoomViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() override fun handle(action: CreateDirectRoomAction) { when (action) { diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt index e80853b035..bdae975846 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt @@ -19,7 +19,9 @@ import android.app.Activity import android.content.Context import android.content.Intent import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R +import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.extensions.addFragmentToBackstack import im.vector.app.core.extensions.observeEvent import im.vector.app.core.extensions.registerStartForActivityResult @@ -28,7 +30,9 @@ import im.vector.app.core.platform.SimpleFragmentActivity import im.vector.app.core.ui.views.KeysBackupBanner import im.vector.app.features.crypto.quads.SharedSecureStorageActivity import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME +import javax.inject.Inject +@AndroidEntryPoint class KeysBackupRestoreActivity : SimpleFragmentActivity() { companion object { @@ -48,10 +52,12 @@ class KeysBackupRestoreActivity : SimpleFragmentActivity() { super.onBackPressed() } + @Inject lateinit var activeSessionHolder: ActiveSessionHolder + override fun initUiAndData() { super.initUiAndData() viewModel = viewModelProvider.get(KeysBackupRestoreSharedViewModel::class.java) - viewModel.initSession(session) + viewModel.initSession(activeSessionHolder.getActiveSession()) viewModel.keySourceModel.observe(this) { keySource -> if (keySource != null && !keySource.isInQuadS && supportFragmentManager.fragments.isEmpty()) { diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupManageActivity.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupManageActivity.kt index 716f02369a..2b666e9cf8 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupManageActivity.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupManageActivity.kt @@ -21,13 +21,13 @@ import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading import com.airbnb.mvrx.viewModel import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.replaceFragment import im.vector.app.core.platform.SimpleFragmentActivity import im.vector.app.core.platform.WaitingViewData -import javax.inject.Inject +@AndroidEntryPoint class KeysBackupManageActivity : SimpleFragmentActivity() { companion object { @@ -40,12 +40,6 @@ class KeysBackupManageActivity : SimpleFragmentActivity() { override fun getTitleRes() = R.string.encryption_message_recovery private val viewModel: KeysBackupSettingsViewModel by viewModel() - @Inject lateinit var keysBackupSettingsViewModelFactory: KeysBackupSettingsViewModel.Factory - - override fun injectWith(injector: ScreenComponent) { - super.injectWith(injector) - injector.inject(this) - } override fun initUiAndData() { super.initUiAndData() diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt index 6814b376c2..d6d9e10669 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt @@ -15,16 +15,16 @@ */ package im.vector.app.features.crypto.keysbackup.settings -import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext 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.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import org.matrix.android.sdk.api.MatrixCallback @@ -41,18 +41,11 @@ class KeysBackupSettingsViewModel @AssistedInject constructor(@Assisted initialS KeysBackupStateListener { @AssistedFactory - interface Factory { - fun create(initialState: KeysBackupSettingViewState): KeysBackupSettingsViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: KeysBackupSettingViewState): KeysBackupSettingsViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: KeysBackupSettingViewState): KeysBackupSettingsViewModel? { - val activity: KeysBackupManageActivity = (viewModelContext as ActivityViewModelContext).activity() - return activity.keysBackupSettingsViewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() private val keysBackupService: KeysBackupService = session.cryptoService().keysBackupService() diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt index 7cc46ef62c..0f7c09ca16 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt @@ -23,8 +23,9 @@ import androidx.core.view.isVisible import androidx.fragment.app.FragmentManager import androidx.lifecycle.lifecycleScope import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent +import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.dialogs.ExportKeysDialog import im.vector.app.core.extensions.observeEvent import im.vector.app.core.extensions.queryExportKeys @@ -36,6 +37,7 @@ import im.vector.app.features.crypto.keys.KeysExporter import kotlinx.coroutines.launch import javax.inject.Inject +@AndroidEntryPoint class KeysBackupSetupActivity : SimpleFragmentActivity() { override fun getTitleRes() = R.string.title_activity_keys_backup_setup @@ -43,10 +45,10 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() { private lateinit var viewModel: KeysBackupSetupSharedViewModel @Inject lateinit var keysExporter: KeysExporter + @Inject lateinit var activeSessionHolder: ActiveSessionHolder - override fun injectWith(injector: ScreenComponent) { - super.injectWith(injector) - injector.inject(this) + private val session by lazy { + activeSessionHolder.getActiveSession() } override fun initUiAndData() { diff --git a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageActivity.kt b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageActivity.kt index bd7195ad1e..61c8ab8f0a 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageActivity.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageActivity.kt @@ -28,8 +28,8 @@ import androidx.fragment.app.FragmentOnAttachListener import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.extensions.commitTransaction import im.vector.app.core.platform.SimpleFragmentActivity @@ -39,6 +39,7 @@ import kotlinx.parcelize.Parcelize import javax.inject.Inject import kotlin.reflect.KClass +@AndroidEntryPoint class SharedSecureStorageActivity : SimpleFragmentActivity(), VectorBaseBottomSheetDialogFragment.ResultListener, @@ -52,14 +53,8 @@ class SharedSecureStorageActivity : ) : Parcelable private val viewModel: SharedSecureStorageViewModel by viewModel() - @Inject lateinit var viewModelFactory: SharedSecureStorageViewModel.Factory @Inject lateinit var errorFormatter: ErrorFormatter - override fun injectWith(injector: ScreenComponent) { - super.injectWith(injector) - injector.inject(this) - } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) supportFragmentManager.addFragmentOnAttachListener(this) diff --git a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt index bb4d4ce99a..8994ad901b 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt @@ -19,16 +19,16 @@ package im.vector.app.features.crypto.quads import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.R +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.WaitingViewData @@ -54,8 +54,18 @@ data class SharedSecureStorageViewState( val step: Step = Step.EnterPassphrase, val activeDeviceCount: Int = 0, val showResetAllAction: Boolean = false, - val userId: String = "" + val userId: String = "", + val keyId: String?, + val requestedSecrets: List, + val resultKeyStoreAlias: String ) : MavericksState { + + constructor(args: SharedSecureStorageActivity.Args) : this( + keyId = args.keyId, + requestedSecrets = args.requestedSecrets, + resultKeyStoreAlias = args.resultKeyStoreAlias + ) + enum class Step { EnterPassphrase, EnterKey, @@ -64,23 +74,22 @@ data class SharedSecureStorageViewState( } class SharedSecureStorageViewModel @AssistedInject constructor( - @Assisted initialState: SharedSecureStorageViewState, - @Assisted val args: SharedSecureStorageActivity.Args, + @Assisted private val initialState: SharedSecureStorageViewState, private val stringProvider: StringProvider, private val session: Session) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: SharedSecureStorageViewState, args: SharedSecureStorageActivity.Args): SharedSecureStorageViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: SharedSecureStorageViewState): SharedSecureStorageViewModel } init { setState { copy(userId = session.myUserId) } - val isValid = session.sharedSecretStorageService.checkShouldBeAbleToAccessSecrets(args.requestedSecrets, args.keyId) is IntegrityResult.Success - if (!isValid) { + val integrityResult = session.sharedSecretStorageService.checkShouldBeAbleToAccessSecrets(initialState.requestedSecrets, initialState.keyId) + if (integrityResult !is IntegrityResult.Success) { _viewEvents.post( SharedSecureStorageViewEvent.Error( stringProvider.getString(R.string.enter_secret_storage_invalid), @@ -88,7 +97,7 @@ class SharedSecureStorageViewModel @AssistedInject constructor( ) ) } - val keyResult = args.keyId?.let { session.sharedSecretStorageService.getKey(it) } + val keyResult = initialState.keyId?.let { session.sharedSecretStorageService.getKey(it) } ?: session.sharedSecretStorageService.getDefaultKey() if (!keyResult.isSuccess()) { @@ -218,7 +227,7 @@ class SharedSecureStorageViewModel @AssistedInject constructor( } withContext(Dispatchers.IO) { - args.requestedSecrets.forEach { + initialState.requestedSecrets.forEach { if (session.accountDataService().getUserAccountDataEvent(it) != null) { val res = session.sharedSecretStorageService.getSecret( name = it, @@ -235,7 +244,7 @@ class SharedSecureStorageViewModel @AssistedInject constructor( _viewEvents.post(SharedSecureStorageViewEvent.HideModalLoading) val safeForIntentCypher = ByteArrayOutputStream().also { it.use { - session.securelyStoreObject(decryptedSecretMap as Map, args.resultKeyStoreAlias, it) + session.securelyStoreObject(decryptedSecretMap as Map, initialState.resultKeyStoreAlias, it) } }.toByteArray().toBase64NoPadding() _viewEvents.post(SharedSecureStorageViewEvent.FinishSuccess(safeForIntentCypher)) @@ -287,7 +296,7 @@ class SharedSecureStorageViewModel @AssistedInject constructor( ) withContext(Dispatchers.IO) { - args.requestedSecrets.forEach { + initialState.requestedSecrets.forEach { if (session.accountDataService().getUserAccountDataEvent(it) != null) { val res = session.sharedSecretStorageService.getSecret( name = it, @@ -304,7 +313,7 @@ class SharedSecureStorageViewModel @AssistedInject constructor( _viewEvents.post(SharedSecureStorageViewEvent.HideModalLoading) val safeForIntentCypher = ByteArrayOutputStream().also { it.use { - session.securelyStoreObject(decryptedSecretMap as Map, args.resultKeyStoreAlias, it) + session.securelyStoreObject(decryptedSecretMap as Map, initialState.resultKeyStoreAlias, it) } }.toByteArray().toBase64NoPadding() _viewEvents.post(SharedSecureStorageViewEvent.FinishSuccess(safeForIntentCypher)) @@ -320,13 +329,5 @@ class SharedSecureStorageViewModel @AssistedInject constructor( _viewEvents.post(SharedSecureStorageViewEvent.Dismiss) } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: SharedSecureStorageViewState): SharedSecureStorageViewModel? { - val activity: SharedSecureStorageActivity = viewModelContext.activity() - val args: SharedSecureStorageActivity.Args = activity.intent.getParcelableExtra(Mavericks.KEY_ARG) ?: error("Missing args") - return activity.viewModelFactory.create(state, args) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() } diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapBottomSheet.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapBottomSheet.kt index a42c3d2dda..50f9526da5 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapBottomSheet.kt @@ -33,8 +33,8 @@ import androidx.fragment.app.FragmentManager import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.commitTransaction import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.registerStartForActivityResult @@ -43,9 +43,9 @@ import im.vector.app.databinding.BottomSheetBootstrapBinding import im.vector.app.features.auth.ReAuthActivity import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.auth.data.LoginFlowTypes -import javax.inject.Inject import kotlin.reflect.KClass +@AndroidEntryPoint class BootstrapBottomSheet : VectorBaseBottomSheetDialogFragment() { @Parcelize @@ -55,15 +55,8 @@ class BootstrapBottomSheet : VectorBaseBottomSheetDialogFragment(initialState) { private var doesKeyBackupExist: Boolean = false @@ -73,10 +69,12 @@ class BootstrapSharedViewModel @AssistedInject constructor( private val zxcvbn = Zxcvbn() @AssistedFactory - interface Factory { - fun create(initialState: BootstrapViewState, args: BootstrapBottomSheet.Args): BootstrapSharedViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: BootstrapViewState): BootstrapSharedViewModel } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() + // private var _pendingSession: String? = null var uiaContinuation: Continuation? = null @@ -84,7 +82,7 @@ class BootstrapSharedViewModel @AssistedInject constructor( init { - when (args.setUpMode) { + when (initialState.setupMode) { SetupMode.PASSPHRASE_RESET, SetupMode.PASSPHRASE_AND_NEEDED_SECRETS_RESET, SetupMode.HARD_RESET -> { @@ -410,7 +408,7 @@ class BootstrapSharedViewModel @AssistedInject constructor( progressListener = progressListener, passphrase = state.passphrase, keySpec = state.migrationRecoveryKey?.let { extractCurveKeyFromRecoveryKey(it)?.let { RawBytesKeySpec(it) } }, - setupMode = args.setUpMode + setupMode = state.setupMode ) ) { bootstrapResult -> when (bootstrapResult) { @@ -516,7 +514,7 @@ class BootstrapSharedViewModel @AssistedInject constructor( BootstrapStep.CheckingMigration -> Unit is BootstrapStep.FirstForm -> { _viewEvents.post( - when (args.setUpMode) { + when (state.setupMode) { SetupMode.CROSS_SIGNING_ONLY, SetupMode.NORMAL -> BootstrapViewEvents.SkipBootstrap() else -> BootstrapViewEvents.Dismiss(success = false) @@ -547,18 +545,4 @@ class BootstrapSharedViewModel @AssistedInject constructor( else -> stringProvider.getString(R.string.unexpected_error) } } - - // ====================================== - // Companion, view model assisted creation - // ====================================== - - companion object : MavericksViewModelFactory { - - override fun create(viewModelContext: ViewModelContext, state: BootstrapViewState): BootstrapSharedViewModel? { - val fragment: BootstrapBottomSheet = (viewModelContext as FragmentViewModelContext).fragment() - val args: BootstrapBottomSheet.Args = fragment.arguments?.getParcelable(BootstrapBottomSheet.EXTRA_ARGS) - ?: BootstrapBottomSheet.Args(SetupMode.CROSS_SIGNING_ONLY) - return fragment.bootstrapViewModelFactory.create(state, args) - } - } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapViewState.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapViewState.kt index b8c9f10b49..9d5760cbf9 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapViewState.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapViewState.kt @@ -24,6 +24,7 @@ import im.vector.app.core.platform.WaitingViewData import org.matrix.android.sdk.api.session.securestorage.SsssKeyCreationInfo data class BootstrapViewState( + val setupMode: SetupMode, val step: BootstrapStep = BootstrapStep.CheckingMigration, val passphrase: String? = null, val migrationRecoveryKey: String? = null, @@ -34,4 +35,7 @@ data class BootstrapViewState( val recoveryKeyCreationInfo: SsssKeyCreationInfo? = null, val initializationWaitingViewData: WaitingViewData? = null, val recoverySaveFileProcess: Async = Uninitialized -) : MavericksState +) : MavericksState { + + constructor(args: BootstrapBottomSheet.Args) : this(setupMode = args.setUpMode) +} diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt index e6abf13f8d..4ef0109227 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt @@ -28,8 +28,8 @@ import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.commitTransaction import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.registerStartForActivityResult @@ -61,6 +61,7 @@ import timber.log.Timber import javax.inject.Inject import kotlin.reflect.KClass +@AndroidEntryPoint class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { @Parcelize @@ -75,18 +76,11 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment = Uninitialized, val pendingLocalId: String? = null, val sasTransactionState: VerificationTxState? = null, val qrTransactionState: VerificationTxState? = null, val transactionId: String? = null, - // true when we display the loading and we wait for the other (incoming request) - val selfVerificationMode: Boolean = false, val verifiedFromPrivateKeys: Boolean = false, val verifyingFrom4S: Boolean = false, val isMe: Boolean = false, @@ -79,29 +80,41 @@ data class VerificationBottomSheetViewState( val quadSContainsSecrets: Boolean = true, val quadSHasBeenReset: Boolean = false, val hasAnyOtherSession: Boolean = false -) : MavericksState +) : MavericksState { + + constructor(args: VerificationBottomSheet.VerificationArgs) : this( + otherUserId = args.otherUserId, + verificationId = args.verificationId, + roomId = args.roomId, + selfVerificationMode = args.selfVerificationMode + ) +} class VerificationBottomSheetViewModel @AssistedInject constructor( @Assisted initialState: VerificationBottomSheetViewState, - @Assisted val args: VerificationBottomSheet.VerificationArgs, private val session: Session, private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider, private val stringProvider: StringProvider) : - VectorViewModel(initialState), + VectorViewModel(initialState), VerificationService.Listener { + @AssistedFactory + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: VerificationBottomSheetViewState): VerificationBottomSheetViewModel + } + + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() + init { session.cryptoService().verificationService().addListener(this) - val userItem = session.getUser(args.otherUserId) - - val selfVerificationMode = args.selfVerificationMode + val userItem = session.getUser(initialState.otherUserId) var autoReady = false - val pr = if (selfVerificationMode) { + val pr = if (initialState.selfVerificationMode) { // See if active tx for this user and take it - session.cryptoService().verificationService().getExistingVerificationRequests(args.otherUserId) + session.cryptoService().verificationService().getExistingVerificationRequests(initialState.otherUserId) .lastOrNull { !it.isFinished } ?.also { verificationRequest -> if (verificationRequest.isIncoming && !verificationRequest.isReady) { @@ -110,15 +123,15 @@ class VerificationBottomSheetViewModel @AssistedInject constructor( } } } else { - session.cryptoService().verificationService().getExistingVerificationRequest(args.otherUserId, args.verificationId) + session.cryptoService().verificationService().getExistingVerificationRequest(initialState.otherUserId, initialState.verificationId) } - val sasTx = (pr?.transactionId ?: args.verificationId)?.let { - session.cryptoService().verificationService().getExistingTransaction(args.otherUserId, it) as? SasVerificationTransaction + val sasTx = (pr?.transactionId ?: initialState.verificationId)?.let { + session.cryptoService().verificationService().getExistingTransaction(initialState.otherUserId, it) as? SasVerificationTransaction } - val qrTx = (pr?.transactionId ?: args.verificationId)?.let { - session.cryptoService().verificationService().getExistingTransaction(args.otherUserId, it) as? QrCodeVerificationTransaction + val qrTx = (pr?.transactionId ?: initialState.verificationId)?.let { + session.cryptoService().verificationService().getExistingTransaction(initialState.otherUserId, it) as? QrCodeVerificationTransaction } val hasAnyOtherSession = session.cryptoService() @@ -132,11 +145,9 @@ class VerificationBottomSheetViewModel @AssistedInject constructor( otherUserMxItem = userItem?.toMatrixItem(), sasTransactionState = sasTx?.state, qrTransactionState = qrTx?.state, - transactionId = pr?.transactionId ?: args.verificationId, + transactionId = pr?.transactionId ?: initialState.verificationId, pendingRequest = if (pr != null) Success(pr) else Uninitialized, - selfVerificationMode = selfVerificationMode, - roomId = args.roomId, - isMe = args.otherUserId == session.myUserId, + isMe = initialState.otherUserId == session.myUserId, currentDeviceCanCrossSign = session.cryptoService().crossSigningService().canCrossSign(), quadSContainsSecrets = session.sharedSecretStorageService.isRecoverySetup(), hasAnyOtherSession = hasAnyOtherSession @@ -159,12 +170,6 @@ class VerificationBottomSheetViewModel @AssistedInject constructor( super.onCleared() } - @AssistedFactory - interface Factory { - fun create(initialState: VerificationBottomSheetViewState, - args: VerificationBottomSheet.VerificationArgs): VerificationBottomSheetViewModel - } - fun queryCancel() = withState { state -> if (state.userThinkItsNotHim) { setState { @@ -223,16 +228,6 @@ class VerificationBottomSheetViewModel @AssistedInject constructor( _viewEvents.post(VerificationBottomSheetViewEvents.GoToSettings) } - companion object : MavericksViewModelFactory { - - override fun create(viewModelContext: ViewModelContext, state: VerificationBottomSheetViewState): VerificationBottomSheetViewModel? { - val fragment: VerificationBottomSheet = (viewModelContext as FragmentViewModelContext).fragment() - val args: VerificationBottomSheet.VerificationArgs = viewModelContext.args() - - return fragment.verificationViewModelFactory.create(state, args) - } - } - override fun handle(action: VerificationAction) = withState { state -> val otherUserId = state.otherUserMxItem?.id ?: return@withState val roomId = state.roomId @@ -542,7 +537,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor( state.pendingRequest.invoke()?.transactionId == pr.transactionId) { setState { copy( - transactionId = args.verificationId ?: pr.transactionId, + transactionId = state.verificationId ?: pr.transactionId, pendingRequest = Success(pr) ) } diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodFragment.kt index 92faa7a0e7..31a7956db3 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodFragment.kt @@ -40,7 +40,6 @@ import timber.log.Timber import javax.inject.Inject class VerificationChooseMethodFragment @Inject constructor( - val verificationChooseMethodViewModelFactory: VerificationChooseMethodViewModel.Factory, val controller: VerificationChooseMethodController ) : VectorBaseFragment(), VerificationChooseMethodController.Listener { diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt index 990a204bc1..7696bb8f5b 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt @@ -15,14 +15,16 @@ */ package im.vector.app.features.crypto.verification.choose -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import im.vector.app.core.di.HasScreenInjector +import dagger.hilt.EntryPoints +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.SingletonEntryPoint +import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel @@ -50,6 +52,10 @@ class VerificationChooseMethodViewModel @AssistedInject constructor( private val session: Session ) : VectorViewModel(initialState), VerificationService.Listener { + init { + session.cryptoService().verificationService().addListener(this) + } + override fun transactionCreated(tx: VerificationTransaction) { transactionUpdated(tx) } @@ -81,28 +87,15 @@ class VerificationChooseMethodViewModel @AssistedInject constructor( } @AssistedFactory - interface Factory { - fun create(initialState: VerificationChooseMethodViewState): VerificationChooseMethodViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: VerificationChooseMethodViewState): VerificationChooseMethodViewModel } - init { - session.cryptoService().verificationService().addListener(this) - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() { - override fun onCleared() { - session.cryptoService().verificationService().removeListener(this) - super.onCleared() - } - - companion object : MavericksViewModelFactory { - override fun create(viewModelContext: ViewModelContext, state: VerificationChooseMethodViewState): VerificationChooseMethodViewModel? { - val fragment: VerificationChooseMethodFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.verificationChooseMethodViewModelFactory.create(state) - } - - override fun initialState(viewModelContext: ViewModelContext): VerificationChooseMethodViewState? { + override fun initialState(viewModelContext: ViewModelContext): VerificationChooseMethodViewState { val args: VerificationBottomSheet.VerificationArgs = viewModelContext.args() - val session = (viewModelContext.activity as HasScreenInjector).injector().activeSessionHolder().getActiveSession() + val session = EntryPoints.get(viewModelContext.app(), SingletonEntryPoint::class.java).activeSessionHolder().getActiveSession() val verificationService = session.cryptoService().verificationService() val pvr = verificationService.getExistingVerificationRequest(args.otherUserId, args.verificationId) @@ -121,5 +114,10 @@ class VerificationChooseMethodViewModel @AssistedInject constructor( } } + override fun onCleared() { + session.cryptoService().verificationService().removeListener(this) + super.onCleared() + } + override fun handle(action: EmptyAction) {} } diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeFragment.kt index 566307c05b..3f4eaf8ac9 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeFragment.kt @@ -31,7 +31,6 @@ import im.vector.app.features.crypto.verification.VerificationBottomSheetViewMod import javax.inject.Inject class VerificationEmojiCodeFragment @Inject constructor( - val viewModelFactory: VerificationEmojiCodeViewModel.Factory, val controller: VerificationEmojiCodeController ) : VectorBaseFragment(), VerificationEmojiCodeController.Listener { diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeViewModel.kt index f1e3d1b805..6f213adb7e 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeViewModel.kt @@ -17,7 +17,6 @@ package im.vector.app.features.crypto.verification.emoji import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksViewModelFactory @@ -27,7 +26,10 @@ import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import im.vector.app.core.di.HasScreenInjector +import dagger.hilt.EntryPoints +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.SingletonEntryPoint +import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel @@ -151,20 +153,15 @@ class VerificationEmojiCodeViewModel @AssistedInject constructor( } @AssistedFactory - interface Factory { - fun create(initialState: VerificationEmojiCodeViewState): VerificationEmojiCodeViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: VerificationEmojiCodeViewState): VerificationEmojiCodeViewModel } - companion object : MavericksViewModelFactory { - - override fun create(viewModelContext: ViewModelContext, state: VerificationEmojiCodeViewState): VerificationEmojiCodeViewModel? { - val factory = (viewModelContext as FragmentViewModelContext).fragment().viewModelFactory - return factory.create(state) - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() { override fun initialState(viewModelContext: ViewModelContext): VerificationEmojiCodeViewState? { val args = viewModelContext.args() - val session = (viewModelContext.activity as HasScreenInjector).injector().activeSessionHolder().getActiveSession() + val session = EntryPoints.get(viewModelContext.app(), SingletonEntryPoint::class.java).activeSessionHolder().getActiveSession() val matrixItem = session.getUser(args.otherUserId)?.toMatrixItem() return VerificationEmojiCodeViewState( diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolActivity.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolActivity.kt index 2686722c6e..772ef99931 100644 --- a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolActivity.kt +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolActivity.kt @@ -33,8 +33,8 @@ import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.withState import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.replaceFragment import im.vector.app.core.extensions.toMvRxBundle @@ -45,10 +45,9 @@ import kotlinx.parcelize.Parcelize import org.billcarsonfr.jsonviewer.JSonViewerFragment import javax.inject.Inject -class RoomDevToolActivity : SimpleFragmentActivity(), RoomDevToolViewModel.Factory, - FragmentManager.OnBackStackChangedListener { +@AndroidEntryPoint +class RoomDevToolActivity : SimpleFragmentActivity(), FragmentManager.OnBackStackChangedListener { - @Inject lateinit var viewModelFactory: RoomDevToolViewModel.Factory @Inject lateinit var colorProvider: ColorProvider // private lateinit var viewModel: RoomDevToolViewModel @@ -65,15 +64,6 @@ class RoomDevToolActivity : SimpleFragmentActivity(), RoomDevToolViewModel.Facto val roomId: String ) : Parcelable - override fun injectWith(injector: ScreenComponent) { - super.injectWith(injector) - injector.inject(this) - } - - override fun create(initialState: RoomDevToolViewState): RoomDevToolViewModel { - return viewModelFactory.create(initialState) - } - override fun initUiAndData() { super.initUiAndData() viewModel.subscribe(this) { diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt index 023d1976c9..04d90a63e7 100644 --- a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolViewModel.kt @@ -16,19 +16,17 @@ package im.vector.app.features.devtools -import androidx.lifecycle.viewModelScope -import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success -import com.airbnb.mvrx.ViewModelContext import com.squareup.moshi.Types import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.R +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider @@ -51,21 +49,11 @@ class RoomDevToolViewModel @AssistedInject constructor( ) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: RoomDevToolViewState): RoomDevToolViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: RoomDevToolViewState): RoomDevToolViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: RoomDevToolViewState): RoomDevToolViewModel { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() init { session.getRoom(initialState.roomId) diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt index 6de7c1fba5..7306146027 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt @@ -45,8 +45,7 @@ import org.matrix.android.sdk.api.session.terms.TermsService import javax.inject.Inject class DiscoverySettingsFragment @Inject constructor( - private val controller: DiscoverySettingsController, - val viewModelFactory: DiscoverySettingsViewModel.Factory + private val controller: DiscoverySettingsController ) : VectorBaseFragment(), DiscoverySettingsController.Listener { diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt index 66f38928a7..b02784dad9 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt @@ -17,16 +17,16 @@ package im.vector.app.features.discovery import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.R +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider @@ -49,18 +49,11 @@ class DiscoverySettingsViewModel @AssistedInject constructor( ) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: DiscoverySettingsState): DiscoverySettingsViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: DiscoverySettingsState): DiscoverySettingsViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: DiscoverySettingsState): DiscoverySettingsViewModel? { - val fragment: DiscoverySettingsFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.viewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() private val identityService = session.identityService() private val termsService: TermsService = session diff --git a/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerFragment.kt b/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerFragment.kt index dd4db36387..15e4e65d3b 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerFragment.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerFragment.kt @@ -41,7 +41,6 @@ import org.matrix.android.sdk.api.session.terms.TermsService import javax.inject.Inject class SetIdentityServerFragment @Inject constructor( - val viewModelFactory: SetIdentityServerViewModel.Factory, val colorProvider: ColorProvider ) : VectorBaseFragment() { diff --git a/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerViewModel.kt b/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerViewModel.kt index 8921f0691d..c258652b49 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerViewModel.kt @@ -15,15 +15,16 @@ */ package im.vector.app.features.discovery.change -import androidx.lifecycle.viewModelScope -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import dagger.hilt.EntryPoints import im.vector.app.R -import im.vector.app.core.di.HasScreenInjector +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.SingletonEntryPoint +import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider @@ -42,26 +43,19 @@ class SetIdentityServerViewModel @AssistedInject constructor( VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: SetIdentityServerState): SetIdentityServerViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: SetIdentityServerState): SetIdentityServerViewModel } - companion object : MavericksViewModelFactory { - - override fun initialState(viewModelContext: ViewModelContext): SetIdentityServerState? { - val session = (viewModelContext.activity as HasScreenInjector).injector().activeSessionHolder().getActiveSession() + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() { + override fun initialState(viewModelContext: ViewModelContext): SetIdentityServerState { + val session = EntryPoints.get(viewModelContext.app(), SingletonEntryPoint::class.java).activeSessionHolder().getActiveSession() return SetIdentityServerState( homeServerUrl = session.sessionParams.homeServerUrl, defaultIdentityServerUrl = session.identityService().getDefaultIdentityServer() ) } - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: SetIdentityServerState): SetIdentityServerViewModel? { - val fragment: SetIdentityServerFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.viewModelFactory.create(state) - } } var currentWantedUrl: String? = null 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 ff1154acc3..1ad08ac8e9 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 @@ -34,10 +34,10 @@ import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.AppStateHandler import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.extensions.registerStartForActivityResult @@ -72,7 +72,6 @@ import im.vector.app.features.spaces.invite.SpaceInviteBottomSheet import im.vector.app.features.spaces.share.ShareSpaceBottomSheet import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.workers.signout.ServerBackupStatusViewModel -import im.vector.app.features.workers.signout.ServerBackupStatusViewState import im.vector.app.push.fcm.FcmHelper import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize @@ -91,13 +90,10 @@ data class HomeActivityArgs( val inviteNotificationRoomId: String? = null ) : Parcelable +@AndroidEntryPoint class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, - UnknownDeviceDetectorSharedViewModel.Factory, - ServerBackupStatusViewModel.Factory, - UnreadMessagesSharedViewModel.Factory, - PromoteRestrictedViewModel.Factory, NavigationInterceptor, SpaceInviteBottomSheet.InteractionListener, MatrixToBottomSheet.InteractionListener { @@ -105,11 +101,8 @@ class HomeActivity : private lateinit var sharedActionViewModel: HomeSharedActionViewModel private val homeActivityViewModel: HomeActivityViewModel by viewModel() - @Inject lateinit var viewModelFactory: HomeActivityViewModel.Factory private val serverBackupStatusViewModel: ServerBackupStatusViewModel by viewModel() - @Inject lateinit var serverBackupviewModelFactory: ServerBackupStatusViewModel.Factory - @Inject lateinit var promoteRestrictedViewModelFactory: PromoteRestrictedViewModel.Factory private val promoteRestrictedViewModel: PromoteRestrictedViewModel by viewModel() @Inject lateinit var activeSessionHolder: ActiveSessionHolder @@ -119,8 +112,6 @@ class HomeActivity : @Inject lateinit var vectorPreferences: VectorPreferences @Inject lateinit var popupAlertManager: PopupAlertManager @Inject lateinit var shortcutsHandler: ShortcutsHandler - @Inject lateinit var unknownDeviceViewModelFactory: UnknownDeviceDetectorSharedViewModel.Factory - @Inject lateinit var unreadMessagesSharedViewModelFactory: UnreadMessagesSharedViewModel.Factory @Inject lateinit var permalinkHandler: PermalinkHandler @Inject lateinit var avatarRenderer: AvatarRenderer @Inject lateinit var initSyncStepFormatter: InitSyncStepFormatter @@ -175,22 +166,6 @@ class HomeActivity : override fun getBinding() = ActivityHomeBinding.inflate(layoutInflater) - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - - override fun create(initialState: UnknownDevicesState): UnknownDeviceDetectorSharedViewModel { - return unknownDeviceViewModelFactory.create(initialState) - } - - override fun create(initialState: ServerBackupStatusViewState): ServerBackupStatusViewModel { - return serverBackupviewModelFactory.create(initialState) - } - - override fun create(initialState: UnreadMessagesState): UnreadMessagesSharedViewModel { - return unreadMessagesSharedViewModelFactory.create(initialState) - } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, false) @@ -298,14 +273,13 @@ class HomeActivity : val resolvedLink = when { // Element custom scheme is not handled by the sdk, convert it to matrix.to link for compatibility deepLink.startsWith(MATRIX_TO_CUSTOM_SCHEME_URL_BASE) -> { - val let = when { + when { deepLink.startsWith(USER_LINK_PREFIX) -> deepLink.substring(USER_LINK_PREFIX.length) deepLink.startsWith(ROOM_LINK_PREFIX) -> deepLink.substring(ROOM_LINK_PREFIX.length) else -> null }?.let { permalinkId -> activeSessionHolder.getSafeActiveSession()?.permalinkService()?.createPermalink(permalinkId) } - let } else -> deepLink } @@ -590,8 +564,6 @@ class HomeActivity : } } - override fun create(initialState: ActiveSpaceViewState) = promoteRestrictedViewModelFactory.create(initialState) - override fun mxToBottomSheetNavigateToRoom(roomId: String) { navigator.openRoom(this, roomId) } 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 627ff4be12..59b9cafd6e 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 @@ -17,14 +17,13 @@ package im.vector.app.features.home import androidx.lifecycle.asFlow -import androidx.lifecycle.viewModelScope -import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.MavericksViewModelFactory -import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject 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.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.login.ReAuthHelper @@ -58,26 +57,17 @@ import kotlin.coroutines.resumeWithException class HomeActivityViewModel @AssistedInject constructor( @Assisted initialState: HomeActivityViewState, - @Assisted private val args: HomeActivityArgs, private val activeSessionHolder: ActiveSessionHolder, private val reAuthHelper: ReAuthHelper, private val vectorPreferences: VectorPreferences ) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: HomeActivityViewState, args: HomeActivityArgs): HomeActivityViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: HomeActivityViewState): HomeActivityViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: HomeActivityViewState): HomeActivityViewModel? { - val activity: HomeActivity = viewModelContext.activity() - val args: HomeActivityArgs? = activity.intent.getParcelableExtra(Mavericks.KEY_ARG) - return activity.viewModelFactory.create(state, args ?: HomeActivityArgs(clearNotification = false, accountCreation = false)) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() private var checkBootstrap = false private var onceTrusted = false diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt index df95bb9b14..80351a437e 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt @@ -57,15 +57,12 @@ import im.vector.app.features.settings.VectorSettingsActivity.Companion.EXTRA_DI import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.workers.signout.BannerState import im.vector.app.features.workers.signout.ServerBackupStatusViewModel -import im.vector.app.features.workers.signout.ServerBackupStatusViewState import org.matrix.android.sdk.api.session.group.model.GroupSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo import javax.inject.Inject class HomeDetailFragment @Inject constructor( - val homeDetailViewModelFactory: HomeDetailViewModel.Factory, - private val serverBackupStatusViewModelFactory: ServerBackupStatusViewModel.Factory, private val avatarRenderer: AvatarRenderer, private val colorProvider: ColorProvider, private val alertManager: PopupAlertManager, @@ -74,8 +71,7 @@ class HomeDetailFragment @Inject constructor( private val appStateHandler: AppStateHandler ) : VectorBaseFragment(), KeysBackupBanner.Delegate, - CurrentCallsView.Callback, - ServerBackupStatusViewModel.Factory { + CurrentCallsView.Callback { private val viewModel: HomeDetailViewModel by fragmentViewModel() private val unknownDeviceDetectorSharedViewModel: UnknownDeviceDetectorSharedViewModel by activityViewModel() @@ -503,8 +499,4 @@ class HomeDetailFragment @Inject constructor( } return this } - - override fun create(initialState: ServerBackupStatusViewState): ServerBackupStatusViewModel { - return serverBackupStatusViewModelFactory.create(initialState) - } } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt index d981a29fec..73e50ad5f1 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt @@ -17,7 +17,6 @@ package im.vector.app.features.home import androidx.lifecycle.asFlow -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted @@ -25,7 +24,9 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.AppStateHandler import im.vector.app.RoomGroupingMethod -import im.vector.app.core.di.HasScreenInjector +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory +import im.vector.app.core.extensions.singletonEntryPoint import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.call.dialpad.DialPadLookup import im.vector.app.features.call.lookup.CallProtocolsChecker @@ -69,24 +70,18 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho CallProtocolsChecker.Listener { @AssistedFactory - interface Factory { - fun create(initialState: HomeDetailViewState): HomeDetailViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: HomeDetailViewState): HomeDetailViewModel } - companion object : MavericksViewModelFactory { + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() { - override fun initialState(viewModelContext: ViewModelContext): HomeDetailViewState? { - val uiStateRepository = (viewModelContext.activity as HasScreenInjector).injector().uiStateRepository() + override fun initialState(viewModelContext: ViewModelContext): HomeDetailViewState { + val uiStateRepository = viewModelContext.activity.singletonEntryPoint().uiStateRepository() return HomeDetailViewState( currentTab = HomeTab.RoomList(uiStateRepository.getDisplayMode()) ) } - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: HomeDetailViewState): HomeDetailViewModel? { - val fragment: HomeDetailFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.homeDetailViewModelFactory.create(state) - } } init { diff --git a/vector/src/main/java/im/vector/app/features/home/HomeModule.kt b/vector/src/main/java/im/vector/app/features/home/HomeModule.kt index 5c34d0715d..0782bbb573 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeModule.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeModule.kt @@ -19,9 +19,11 @@ package im.vector.app.features.home import android.os.Handler import dagger.Module import dagger.Provides +import dagger.hilt.migration.DisableInstallInCheck import im.vector.app.features.home.room.detail.timeline.TimelineEventControllerHandler import im.vector.app.features.home.room.detail.timeline.helper.TimelineAsyncHelper +@DisableInstallInCheck @Module object HomeModule { diff --git a/vector/src/main/java/im/vector/app/features/home/PromoteRestrictedViewModel.kt b/vector/src/main/java/im/vector/app/features/home/PromoteRestrictedViewModel.kt index 0c8c9e480c..218574c03e 100644 --- a/vector/src/main/java/im/vector/app/features/home/PromoteRestrictedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/PromoteRestrictedViewModel.kt @@ -16,17 +16,16 @@ package im.vector.app.features.home -import com.airbnb.mvrx.ActivityViewModelContext -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksViewModelFactory -import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.AppStateHandler import im.vector.app.RoomGroupingMethod 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.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel @@ -72,21 +71,11 @@ class PromoteRestrictedViewModel @AssistedInject constructor( } @AssistedFactory - interface Factory { - fun create(initialState: ActiveSpaceViewState): PromoteRestrictedViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: ActiveSpaceViewState): PromoteRestrictedViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: ActiveSpaceViewState): PromoteRestrictedViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() override fun handle(action: EmptyAction) {} } diff --git a/vector/src/main/java/im/vector/app/features/home/ShortcutsHandler.kt b/vector/src/main/java/im/vector/app/features/home/ShortcutsHandler.kt index 7514d455aa..612e2dcf87 100644 --- a/vector/src/main/java/im/vector/app/features/home/ShortcutsHandler.kt +++ b/vector/src/main/java/im/vector/app/features/home/ShortcutsHandler.kt @@ -22,19 +22,28 @@ import android.os.Build import androidx.core.content.getSystemService import androidx.core.content.pm.ShortcutManagerCompat import im.vector.app.core.di.ActiveSessionHolder +import im.vector.app.features.pin.PinCodeStore +import im.vector.app.features.pin.PinCodeStoreListener import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposables import org.matrix.android.sdk.api.session.room.RoomSortOrder import org.matrix.android.sdk.api.session.room.model.Membership +import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.rx.asObservable +import timber.log.Timber import javax.inject.Inject class ShortcutsHandler @Inject constructor( private val context: Context, private val shortcutCreator: ShortcutCreator, - private val activeSessionHolder: ActiveSessionHolder -) { + private val activeSessionHolder: ActiveSessionHolder, + private val pinCodeStore: PinCodeStore +) : PinCodeStoreListener { + private val isRequestPinShortcutSupported = ShortcutManagerCompat.isRequestPinShortcutSupported(context) + + // Value will be set correctly if necessary + private var hasPinCode = true fun observeRoomsAndBuildShortcuts(): Disposable { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) { @@ -42,31 +51,56 @@ class ShortcutsHandler @Inject constructor( return Disposables.empty() } - return activeSessionHolder.getSafeActiveSession() - ?.getPagedRoomSummariesLive( - roomSummaryQueryParams { - memberships = listOf(Membership.JOIN) - }, - sortOrder = RoomSortOrder.PRIORITY_AND_ACTIVITY - ) - ?.asObservable() - ?.subscribe { rooms -> + hasPinCode = pinCodeStore.getEncodedPin() != null + + val session = activeSessionHolder.getSafeActiveSession() ?: return Disposables.empty() + return session.getRoomSummariesLive( + roomSummaryQueryParams { + memberships = listOf(Membership.JOIN) + }, + sortOrder = RoomSortOrder.PRIORITY_AND_ACTIVITY + ) + .asObservable() + .doOnSubscribe { pinCodeStore.addListener(this) } + .doFinally { pinCodeStore.removeListener(this) } + .subscribe { rooms -> // Remove dead shortcuts (i.e. deleted rooms) - val roomIds = rooms.map { it.roomId } - val deadShortcutIds = ShortcutManagerCompat.getShortcuts(context, ShortcutManagerCompat.FLAG_MATCH_DYNAMIC) - .map { it.id } - .filter { !roomIds.contains(it) } - ShortcutManagerCompat.removeLongLivedShortcuts(context, deadShortcutIds) + removeDeadShortcut(rooms.map { it.roomId }) - val shortcuts = rooms.mapIndexed { index, room -> - shortcutCreator.create(room, index) - } - - shortcuts.forEach { shortcut -> - ShortcutManagerCompat.pushDynamicShortcut(context, shortcut) - } + // Create shortcuts + createShortcuts(rooms) } - ?: Disposables.empty() + } + + private fun removeDeadShortcut(roomIds: List) { + val deadShortcutIds = ShortcutManagerCompat.getShortcuts(context, ShortcutManagerCompat.FLAG_MATCH_DYNAMIC) + .map { it.id } + .filter { !roomIds.contains(it) } + + if (deadShortcutIds.isNotEmpty()) { + Timber.d("Removing shortcut(s) $deadShortcutIds") + ShortcutManagerCompat.removeLongLivedShortcuts(context, deadShortcutIds) + if (isRequestPinShortcutSupported) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { + context.getSystemService()?.disableShortcuts(deadShortcutIds) + } + } + } + } + + private fun createShortcuts(rooms: List) { + if (hasPinCode) { + // No shortcut in this case (privacy) + ShortcutManagerCompat.removeAllDynamicShortcuts(context) + } else { + val shortcuts = rooms.mapIndexed { index, room -> + shortcutCreator.create(room, index) + } + + shortcuts.forEach { shortcut -> + ShortcutManagerCompat.pushDynamicShortcut(context, shortcut) + } + } } fun clearShortcuts() { @@ -82,7 +116,7 @@ class ShortcutsHandler @Inject constructor( ShortcutManagerCompat.removeLongLivedShortcuts(context, shortcuts) // We can only disabled pinned shortcuts with the API, but at least it will prevent the crash - if (ShortcutManagerCompat.isRequestPinShortcutSupported(context)) { + if (isRequestPinShortcutSupported) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { context.getSystemService() ?.let { @@ -91,4 +125,14 @@ class ShortcutsHandler @Inject constructor( } } } + + override fun onPinSetUpChange(isConfigured: Boolean) { + hasPinCode = isConfigured + if (isConfigured) { + // Remove shortcuts immediately + ShortcutManagerCompat.removeAllDynamicShortcuts(context) + } + // Else shortcut will be created next time any room summary is updated, or + // next time the app is started which is acceptable + } } diff --git a/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt index 36e31770a9..8a36a4c19e 100644 --- a/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt @@ -16,17 +16,16 @@ package im.vector.app.features.home -import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Async -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext 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.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModelAction @@ -66,21 +65,11 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted } @AssistedFactory - interface Factory { - fun create(initialState: UnknownDevicesState): UnknownDeviceDetectorSharedViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: UnknownDevicesState): UnknownDeviceDetectorSharedViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: UnknownDevicesState): UnknownDeviceDetectorSharedViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() private val ignoredDeviceList = ArrayList() diff --git a/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt index cc209cd72c..5bdbc95b48 100644 --- a/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt @@ -16,16 +16,15 @@ package im.vector.app.features.home -import com.airbnb.mvrx.ActivityViewModelContext -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksViewModelFactory -import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.AppStateHandler import im.vector.app.RoomGroupingMethod +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel @@ -61,21 +60,11 @@ class UnreadMessagesSharedViewModel @AssistedInject constructor(@Assisted initia VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: UnreadMessagesState): UnreadMessagesSharedViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: UnreadMessagesState): UnreadMessagesSharedViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: UnreadMessagesState): UnreadMessagesSharedViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() override fun handle(action: EmptyAction) {} diff --git a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsFragment.kt index 47a8256628..4d44ff775a 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsFragment.kt @@ -31,8 +31,7 @@ import im.vector.app.features.home.room.detail.RoomDetailSharedActionViewModel import javax.inject.Inject class BreadcrumbsFragment @Inject constructor( - private val breadcrumbsController: BreadcrumbsController, - val breadcrumbsViewModelFactory: BreadcrumbsViewModel.Factory + private val breadcrumbsController: BreadcrumbsController ) : VectorBaseFragment(), BreadcrumbsController.Listener { diff --git a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt index 8ed44053a1..112b7e8574 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt @@ -16,12 +16,12 @@ package im.vector.app.features.home.room.breadcrumbs -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory -import com.airbnb.mvrx.ViewModelContext 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.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel @@ -33,21 +33,14 @@ import org.matrix.android.sdk.flow.flow class BreadcrumbsViewModel @AssistedInject constructor(@Assisted initialState: BreadcrumbsViewState, private val session: Session) : - VectorViewModel(initialState) { + VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: BreadcrumbsViewState): BreadcrumbsViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: BreadcrumbsViewState): BreadcrumbsViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: BreadcrumbsViewState): BreadcrumbsViewModel? { - val fragment: BreadcrumbsFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.breadcrumbsViewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() init { observeBreadcrumbs() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/JoinReplacementRoomBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/JoinReplacementRoomBottomSheet.kt index 7159f7b33a..ba559677c9 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/JoinReplacementRoomBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/JoinReplacementRoomBottomSheet.kt @@ -25,8 +25,8 @@ import com.airbnb.mvrx.Loading import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.parentFragmentViewModel +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.platform.ButtonStateView @@ -34,6 +34,7 @@ import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.databinding.BottomSheetTombstoneJoinBinding import javax.inject.Inject +@AndroidEntryPoint class JoinReplacementRoomBottomSheet : VectorBaseBottomSheetDialogFragment() { @@ -43,10 +44,6 @@ class JoinReplacementRoomBottomSheet : @Inject lateinit var errorFormatter: ErrorFormatter - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - private val viewModel: RoomDetailViewModel by parentFragmentViewModel() override val showExpanded: Boolean diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt index 76c3816ce6..ba53f75eca 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt @@ -24,10 +24,11 @@ import androidx.core.view.GravityCompat import androidx.drawerlayout.widget.DrawerLayout import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager +import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import com.google.android.material.appbar.MaterialToolbar +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.extensions.replaceFragment import im.vector.app.core.platform.ToolbarConfigurable @@ -39,16 +40,11 @@ import im.vector.app.features.navigation.Navigator import im.vector.app.features.room.RequireActiveMembershipAction import im.vector.app.features.room.RequireActiveMembershipViewEvents import im.vector.app.features.room.RequireActiveMembershipViewModel -import im.vector.app.features.room.RequireActiveMembershipViewState -import im.vector.app.features.widgets.permissions.RoomWidgetPermissionViewModel -import im.vector.app.features.widgets.permissions.RoomWidgetPermissionViewState -import javax.inject.Inject +@AndroidEntryPoint class RoomDetailActivity : VectorBaseActivity(), ToolbarConfigurable, - RequireActiveMembershipViewModel.Factory, - RoomWidgetPermissionViewModel.Factory, MatrixToBottomSheet.InteractionListener { override fun getBinding(): ActivityRoomDetailBinding { @@ -77,24 +73,6 @@ class RoomDetailActivity : private lateinit var sharedActionViewModel: RoomDetailSharedActionViewModel private val requireActiveMembershipViewModel: RequireActiveMembershipViewModel by viewModel() - @Inject - lateinit var requireActiveMembershipViewModelFactory: RequireActiveMembershipViewModel.Factory - - override fun create(initialState: RequireActiveMembershipViewState): RequireActiveMembershipViewModel { - // Due to shortcut, we cannot use MvRx args. Pass the first roomId here - return requireActiveMembershipViewModelFactory.create(initialState.copy(roomId = currentRoomId ?: "")) - } - - @Inject - lateinit var permissionsViewModelFactory: RoomWidgetPermissionViewModel.Factory - override fun create(initialState: RoomWidgetPermissionViewState): RoomWidgetPermissionViewModel { - return permissionsViewModelFactory.create(initialState) - } - - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - // Simple filter var currentRoomId: String? = null @@ -108,6 +86,7 @@ class RoomDetailActivity : intent?.extras?.getParcelable(EXTRA_ROOM_DETAIL_ARGS) } if (roomDetailArgs == null) return + intent.putExtra(Mavericks.KEY_ARG, roomDetailArgs) currentRoomId = roomDetailArgs.roomId if (isFirstCreation()) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index fa0ca24289..75edee5d55 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -52,6 +52,7 @@ import androidx.core.view.forEach import androidx.core.view.isInvisible import androidx.core.view.isVisible import androidx.fragment.app.setFragmentResultListener +import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager @@ -237,7 +238,6 @@ class RoomDetailFragment @Inject constructor( private val permalinkHandler: PermalinkHandler, private val notificationDrawerManager: NotificationDrawerManager, val roomDetailViewModelFactory: RoomDetailViewModel.Factory, - val textComposerViewModelFactory: TextComposerViewModel.Factory, private val eventHtmlRenderer: EventHtmlRenderer, private val vectorPreferences: VectorPreferences, private val colorProvider: ColorProvider, @@ -640,17 +640,27 @@ class RoomDetailFragment @Inject constructor( setImageResource(R.drawable.ic_keyboard) } } - .setOnEmojiPopupDismissListener { - if (isAdded) { - views.composerLayout.views.composerEmojiButton.apply { - contentDescription = getString(R.string.a11y_open_emoji_picker) - setImageResource(R.drawable.ic_insert_emoji) - } + .setOnEmojiPopupDismissListenerLifecycleAware { + views.composerLayout.views.composerEmojiButton.apply { + contentDescription = getString(R.string.a11y_open_emoji_picker) + setImageResource(R.drawable.ic_insert_emoji) } } .build(views.composerLayout.views.composerEditText) } + /** + * Ensure dismiss actions only trigger when the fragment is in the started state + * EmojiPopup by default dismisses onViewDetachedFromWindow, this can cause race conditions with onDestroyView + */ + private fun EmojiPopup.Builder.setOnEmojiPopupDismissListenerLifecycleAware(action: () -> Unit): EmojiPopup.Builder { + return setOnEmojiPopupDismissListener { + if (lifecycle.currentState == Lifecycle.State.STARTED) { + action() + } + } + } + private val permissionVoiceMessageLauncher = registerForPermissionsResult { allGranted, deniedPermanently -> if (allGranted) { // In this case, let the user start again the gesture diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index f4f2def028..03bde7d4cc 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -147,14 +147,16 @@ class RoomDetailViewModel @AssistedInject constructor( fun create(initialState: RoomDetailViewState): RoomDetailViewModel } + /** + * Can't use the hiltMaverick here because some dependencies are injected here and in fragment but they don't share the graph. + */ companion object : MavericksViewModelFactory { const val PAGINATION_COUNT = 50 @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: RoomDetailViewState): RoomDetailViewModel? { + override fun create(viewModelContext: ViewModelContext, state: RoomDetailViewState): RoomDetailViewModel { val fragment: RoomDetailFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.roomDetailViewModelFactory.create(state) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewModel.kt index b635602189..e80f25de2f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewModel.kt @@ -16,20 +16,19 @@ package im.vector.app.features.home.room.detail.composer -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory -import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.R +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.features.command.CommandParser import im.vector.app.features.command.ParsedCommand import im.vector.app.features.home.room.detail.ChatEffect -import im.vector.app.features.home.room.detail.RoomDetailFragment import im.vector.app.features.home.room.detail.composer.rainbow.RainbowGenerator import im.vector.app.features.home.room.detail.toMessageType import im.vector.app.features.powerlevel.PowerLevelsFlowFactory @@ -710,16 +709,9 @@ class TextComposerViewModel @AssistedInject constructor( } @AssistedFactory - interface Factory { - fun create(initialState: TextComposerViewState): TextComposerViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: TextComposerViewState): TextComposerViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: TextComposerViewState): TextComposerViewModel { - val fragment: RoomDetailFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.textComposerViewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/readreceipts/DisplayReadReceiptsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/readreceipts/DisplayReadReceiptsBottomSheet.kt index 9f98d655cc..6c315a4e76 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/readreceipts/DisplayReadReceiptsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/readreceipts/DisplayReadReceiptsBottomSheet.kt @@ -23,8 +23,8 @@ import android.view.View import android.view.ViewGroup import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.args +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment @@ -43,6 +43,7 @@ data class DisplayReadReceiptArgs( /** * Bottom sheet displaying list of read receipts for a given event ordered by descending timestamp */ +@AndroidEntryPoint class DisplayReadReceiptsBottomSheet : VectorBaseBottomSheetDialogFragment(), DisplayReadReceiptsController.Listener { @@ -53,10 +54,6 @@ class DisplayReadReceiptsBottomSheet : private lateinit var sharedActionViewModel: MessageSharedActionViewModel - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetGenericListWithTitleBinding { return BottomSheetGenericListWithTitleBinding.inflate(inflater, container, false) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt index 88ed101252..eea62b0907 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt @@ -21,12 +21,13 @@ import android.content.Intent import android.os.Bundle import androidx.appcompat.widget.SearchView import com.airbnb.mvrx.Mavericks +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.addFragment import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivitySearchBinding +@AndroidEntryPoint class SearchActivity : VectorBaseActivity() { private val searchFragment: SearchFragment? @@ -38,10 +39,6 @@ class SearchActivity : VectorBaseActivity() { override fun getCoordinatorLayout() = views.coordinatorLayout - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) configureToolbar(views.searchToolbar) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt index 9f34cdd679..4a285da5f2 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchFragment.kt @@ -47,7 +47,6 @@ data class SearchArgs( ) : Parcelable class SearchFragment @Inject constructor( - val viewModelFactory: SearchViewModel.Factory, private val controller: SearchResultController ) : VectorBaseFragment(), StateView.EventCallback, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt index e4832b3876..a360b91085 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchViewModel.kt @@ -16,16 +16,15 @@ package im.vector.app.features.home.room.detail.search -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success -import com.airbnb.mvrx.ViewModelContext 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.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.CancellationException @@ -46,18 +45,11 @@ class SearchViewModel @AssistedInject constructor( private var nextBatch: String? = null @AssistedFactory - interface Factory { - fun create(initialState: SearchViewState): SearchViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: SearchViewState): SearchViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: SearchViewState): SearchViewModel? { - val fragment: SearchFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.viewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() override fun handle(action: SearchAction) { when (action) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt index 6de8864f10..5e0db19d9e 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt @@ -21,7 +21,7 @@ import android.view.View import android.view.ViewGroup import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState -import im.vector.app.core.di.ScreenComponent +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment @@ -32,11 +32,11 @@ import javax.inject.Inject /** * Bottom sheet fragment that shows a message preview with list of contextual actions */ +@AndroidEntryPoint class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), MessageActionsEpoxyController.MessageActionsEpoxyControllerListener { - @Inject lateinit var messageActionViewModelFactory: MessageActionsViewModel.Factory @Inject lateinit var messageActionsEpoxyController: MessageActionsEpoxyController private val viewModel: MessageActionsViewModel by fragmentViewModel(MessageActionsViewModel::class) @@ -45,10 +45,6 @@ class MessageActionsBottomSheet : private lateinit var sharedActionViewModel: MessageSharedActionViewModel - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetGenericListBinding { return BottomSheetGenericListBinding.inflate(inflater, container, false) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt index b4fff6eb3d..30c231131e 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt @@ -15,14 +15,14 @@ */ package im.vector.app.features.home.room.detail.timeline.action -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory -import com.airbnb.mvrx.ViewModelContext import dagger.Lazy import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.R +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.extensions.canReact import im.vector.app.core.platform.EmptyViewEvents @@ -85,17 +85,11 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted private val eventIdFlow = MutableStateFlow(initialState.eventId) @AssistedFactory - interface Factory { - fun create(initialState: MessageActionState): MessageActionsViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: MessageActionState): MessageActionsViewModel } - companion object : MavericksViewModelFactory { - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: MessageActionState): MessageActionsViewModel? { - val fragment: MessageActionsBottomSheet = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.messageActionViewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() init { observeEvent() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt index da3a7396fd..63140edd8b 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt @@ -22,8 +22,8 @@ import android.view.ViewGroup import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment @@ -35,18 +35,14 @@ import javax.inject.Inject /** * Bottom sheet displaying list of edits for a given event ordered by timestamp */ +@AndroidEntryPoint class ViewEditHistoryBottomSheet : VectorBaseBottomSheetDialogFragment() { private val viewModel: ViewEditHistoryViewModel by fragmentViewModel(ViewEditHistoryViewModel::class) - @Inject lateinit var viewEditHistoryViewModelFactory: ViewEditHistoryViewModel.Factory @Inject lateinit var epoxyController: ViewEditHistoryEpoxyController - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetGenericListWithTitleBinding { return BottomSheetGenericListWithTitleBinding.inflate(inflater, container, false) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewModel.kt index 699f3cf02d..9abc67e41f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewModel.kt @@ -15,16 +15,15 @@ */ package im.vector.app.features.home.room.detail.timeline.edithistory -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success -import com.airbnb.mvrx.ViewModelContext 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.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel @@ -47,18 +46,11 @@ class ViewEditHistoryViewModel @AssistedInject constructor( ?: throw IllegalStateException("Shouldn't use this ViewModel without a room") @AssistedFactory - interface Factory { - fun create(initialState: ViewEditHistoryViewState): ViewEditHistoryViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: ViewEditHistoryViewState): ViewEditHistoryViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: ViewEditHistoryViewState): ViewEditHistoryViewModel? { - val fragment: ViewEditHistoryBottomSheet = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.viewEditHistoryViewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() init { loadHistory() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/ContentDownloadStateTrackerBinder.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/ContentDownloadStateTrackerBinder.kt index abaaaf2152..caf0131144 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/ContentDownloadStateTrackerBinder.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/ContentDownloadStateTrackerBinder.kt @@ -19,16 +19,16 @@ package im.vector.app.features.home.room.detail.timeline.helper import android.graphics.drawable.Drawable import androidx.vectordrawable.graphics.drawable.Animatable2Compat import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat +import dagger.hilt.android.scopes.ActivityScoped import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder -import im.vector.app.core.di.ScreenScope import im.vector.app.core.error.ErrorFormatter import im.vector.app.features.home.room.detail.timeline.MessageColorProvider import im.vector.app.features.home.room.detail.timeline.item.MessageFileItem import org.matrix.android.sdk.api.session.file.ContentDownloadStateTracker import javax.inject.Inject -@ScreenScope +@ActivityScoped class ContentDownloadStateTrackerBinder @Inject constructor(private val activeSessionHolder: ActiveSessionHolder, private val messageColorProvider: MessageColorProvider, private val errorFormatter: ErrorFormatter) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt index 75570a67a0..0909cbe8de 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt @@ -22,9 +22,9 @@ import android.view.ViewGroup import android.widget.ProgressBar import android.widget.TextView import androidx.core.view.isVisible +import dagger.hilt.android.scopes.ActivityScoped import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder -import im.vector.app.core.di.ScreenScope import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.extensions.exhaustive import im.vector.app.core.utils.TextUtils @@ -33,7 +33,7 @@ import org.matrix.android.sdk.api.session.content.ContentUploadStateTracker import org.matrix.android.sdk.api.session.room.send.SendState import javax.inject.Inject -@ScreenScope +@ActivityScoped class ContentUploadStateTrackerBinder @Inject constructor(private val activeSessionHolder: ActiveSessionHolder, private val messageColorProvider: MessageColorProvider, private val errorFormatter: ErrorFormatter) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineMediaSizeProvider.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineMediaSizeProvider.kt index 52229151a0..9ec61e6054 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineMediaSizeProvider.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineMediaSizeProvider.kt @@ -17,11 +17,11 @@ package im.vector.app.features.home.room.detail.timeline.helper import androidx.recyclerview.widget.RecyclerView -import im.vector.app.core.di.ScreenScope +import dagger.hilt.android.scopes.ActivityScoped import javax.inject.Inject import kotlin.math.roundToInt -@ScreenScope +@ActivityScoped class TimelineMediaSizeProvider @Inject constructor() { var recyclerView: RecyclerView? = null diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/VoiceMessagePlaybackTracker.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/VoiceMessagePlaybackTracker.kt index 446d4161e3..2e8f6d9336 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/VoiceMessagePlaybackTracker.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/VoiceMessagePlaybackTracker.kt @@ -18,10 +18,10 @@ package im.vector.app.features.home.room.detail.timeline.helper import android.os.Handler import android.os.Looper -import im.vector.app.core.di.ScreenScope +import dagger.hilt.android.scopes.ActivityScoped import javax.inject.Inject -@ScreenScope +@ActivityScoped class VoiceMessagePlaybackTracker @Inject constructor() { private val mainHandler = Handler(Looper.getMainLooper()) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsBottomSheet.kt index 81ebd6d3de..8071ed8809 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsBottomSheet.kt @@ -23,8 +23,8 @@ import android.view.ViewGroup import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment @@ -38,21 +38,17 @@ import javax.inject.Inject /** * Bottom sheet displaying list of reactions for a given event ordered by timestamp */ +@AndroidEntryPoint class ViewReactionsBottomSheet : VectorBaseBottomSheetDialogFragment(), ViewReactionsEpoxyController.Listener { private val viewModel: ViewReactionsViewModel by fragmentViewModel(ViewReactionsViewModel::class) - @Inject lateinit var viewReactionsViewModelFactory: ViewReactionsViewModel.Factory private lateinit var sharedActionViewModel: MessageSharedActionViewModel @Inject lateinit var epoxyController: ViewReactionsEpoxyController - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetGenericListWithTitleBinding { return BottomSheetGenericListWithTitleBinding.inflate(inflater, container, false) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt index 5baab683cf..1f4d67db03 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt @@ -17,16 +17,16 @@ package im.vector.app.features.home.room.detail.timeline.reactions import com.airbnb.mvrx.Async -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.core.date.DateFormatKind import im.vector.app.core.date.VectorDateFormatter +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel @@ -70,18 +70,11 @@ class ViewReactionsViewModel @AssistedInject constructor(@Assisted ?: throw IllegalStateException("Shouldn't use this ViewModel without a room") @AssistedFactory - interface Factory { - fun create(initialState: DisplayReactionsViewState): ViewReactionsViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: DisplayReactionsViewState): ViewReactionsViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: DisplayReactionsViewState): ViewReactionsViewModel? { - val fragment: ViewReactionsBottomSheet = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.viewReactionsViewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() init { observeEventAnnotationSummaries() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/upgrade/MigrateRoomBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/upgrade/MigrateRoomBottomSheet.kt index 2fa210a748..03a0e64d9b 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/upgrade/MigrateRoomBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/upgrade/MigrateRoomBottomSheet.kt @@ -27,8 +27,8 @@ import com.airbnb.mvrx.Loading import com.airbnb.mvrx.Success import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment @@ -36,9 +36,9 @@ import im.vector.app.databinding.BottomSheetRoomUpgradeBinding import kotlinx.parcelize.Parcelize import javax.inject.Inject +@AndroidEntryPoint class MigrateRoomBottomSheet : - VectorBaseBottomSheetDialogFragment(), - MigrateRoomViewModel.Factory { + VectorBaseBottomSheetDialogFragment() { enum class MigrationReason { MANUAL, @@ -53,20 +53,12 @@ class MigrateRoomBottomSheet : val customDescription: CharSequence? = null ) : Parcelable - @Inject - lateinit var viewModelFactory: MigrateRoomViewModel.Factory - override val showExpanded = true - @Inject - lateinit var errorFormatter: ErrorFormatter + @Inject lateinit var errorFormatter: ErrorFormatter val viewModel: MigrateRoomViewModel by fragmentViewModel() - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - override fun invalidate() = withState(viewModel) { state -> views.headerText.setText(if (state.isPublic) R.string.upgrade_public_room else R.string.upgrade_private_room) @@ -152,10 +144,6 @@ class MigrateRoomBottomSheet : } } - override fun create(initialState: MigrateRoomViewState): MigrateRoomViewModel { - return viewModelFactory.create(initialState) - } - companion object { const val REQUEST_KEY = "MigrateRoomBottomSheetRequest" diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/upgrade/MigrateRoomViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/upgrade/MigrateRoomViewModel.kt index bb28836cd3..98be65c167 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/upgrade/MigrateRoomViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/upgrade/MigrateRoomViewModel.kt @@ -16,15 +16,14 @@ package im.vector.app.features.home.room.detail.upgrade -import com.airbnb.mvrx.ActivityViewModelContext -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success -import com.airbnb.mvrx.ViewModelContext 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.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.session.coroutineScope @@ -51,20 +50,11 @@ class MigrateRoomViewModel @AssistedInject constructor( } @AssistedFactory - interface Factory { - fun create(initialState: MigrateRoomViewState): MigrateRoomViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: MigrateRoomViewState): MigrateRoomViewModel } - companion object : MavericksViewModelFactory { - - override fun create(viewModelContext: ViewModelContext, state: MigrateRoomViewState): MigrateRoomViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() override fun handle(action: MigrateRoomAction) { when (action) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBottomSheet.kt index 42f613d60f..aa6966254f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBottomSheet.kt @@ -22,8 +22,8 @@ import android.view.View import android.view.ViewGroup import com.airbnb.mvrx.parentFragmentViewModel import com.airbnb.mvrx.withState +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment @@ -39,6 +39,7 @@ import javax.inject.Inject /** * Bottom sheet displaying active widgets in a room */ +@AndroidEntryPoint class RoomWidgetsBottomSheet : VectorBaseBottomSheetDialogFragment(), RoomWidgetsController.Listener { @@ -49,10 +50,6 @@ class RoomWidgetsBottomSheet : private val roomDetailViewModel: RoomDetailViewModel by parentFragmentViewModel() - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetGenericListWithTitleBinding { return BottomSheetGenericListWithTitleBinding.inflate(inflater, container, false) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/filtered/FilteredRoomsActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/filtered/FilteredRoomsActivity.kt index a4f6f5f7c4..18618099bd 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/filtered/FilteredRoomsActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/filtered/FilteredRoomsActivity.kt @@ -20,8 +20,8 @@ import android.content.Context import android.content.Intent import android.os.Bundle import androidx.appcompat.widget.SearchView +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.replaceFragment import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivityFilteredRoomsBinding @@ -29,6 +29,7 @@ import im.vector.app.features.home.RoomListDisplayMode import im.vector.app.features.home.room.list.RoomListFragment import im.vector.app.features.home.room.list.RoomListParams +@AndroidEntryPoint class FilteredRoomsActivity : VectorBaseActivity() { private val roomListFragment: RoomListFragment? @@ -40,10 +41,6 @@ class FilteredRoomsActivity : VectorBaseActivity() override fun getCoordinatorLayout() = views.coordinatorLayout - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) configureToolbar(views.filteredRoomsToolbar) diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt index c36836c87f..1c173e12e8 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt @@ -64,7 +64,6 @@ data class RoomListParams( class RoomListFragment @Inject constructor( private val pagedControllerFactory: RoomSummaryPagedControllerFactory, - val roomListViewModelFactory: RoomListViewModel.Factory, private val notificationDrawerManager: NotificationDrawerManager, private val footerController: RoomListFooterController, private val userPreferencesProvider: UserPreferencesProvider diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt index 345c33ec18..b38f2565b6 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt @@ -19,13 +19,16 @@ package im.vector.app.features.home.room.list import androidx.lifecycle.MutableLiveData import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success -import com.airbnb.mvrx.ViewModelContext +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.AppStateHandler import im.vector.app.RoomGroupingMethod +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider @@ -46,10 +49,9 @@ import org.matrix.android.sdk.api.session.room.state.isPublic import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.flow.flow import timber.log.Timber -import javax.inject.Inject -class RoomListViewModel @Inject constructor( - initialState: RoomListViewState, +class RoomListViewModel @AssistedInject constructor( + @Assisted initialState: RoomListViewState, private val session: Session, private val stringProvider: StringProvider, private val appStateHandler: AppStateHandler, @@ -57,8 +59,9 @@ class RoomListViewModel @Inject constructor( private val autoAcceptInvites: AutoAcceptInvites ) : VectorViewModel(initialState) { - interface Factory { - fun create(initialState: RoomListViewState): RoomListViewModel + @AssistedFactory + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: RoomListViewState): RoomListViewModel } private var updatableQuery: UpdatableLivePageResult? = null @@ -115,14 +118,7 @@ class RoomListViewModel @Inject constructor( } } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: RoomListViewState): RoomListViewModel { - val fragment: RoomListFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.roomListViewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() private val roomListSectionBuilder = if (appStateHandler.getCurrentRoomGroupingMethod() is RoomGroupingMethod.BySpace) { RoomListSectionBuilderSpace( @@ -195,7 +191,7 @@ class RoomListViewModel @Inject constructor( } updatableQuery?.updateQuery { it.copy( - displayName = QueryStringValue.Contains(action.filter, QueryStringValue.Case.INSENSITIVE) + displayName = QueryStringValue.Contains(action.filter, QueryStringValue.Case.NORMALIZED) ) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModelFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModelFactory.kt deleted file mode 100644 index e017a8fe08..0000000000 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModelFactory.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2019 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.app.features.home.room.list - -import im.vector.app.AppStateHandler -import im.vector.app.core.resources.StringProvider -import im.vector.app.features.invite.AutoAcceptInvites -import im.vector.app.features.settings.VectorPreferences -import org.matrix.android.sdk.api.session.Session -import javax.inject.Inject -import javax.inject.Provider - -class RoomListViewModelFactory @Inject constructor(private val session: Provider, - private val appStateHandler: AppStateHandler, - private val stringProvider: StringProvider, - private val vectorPreferences: VectorPreferences, - private val autoAcceptInvites: AutoAcceptInvites) : - RoomListViewModel.Factory { - - override fun create(initialState: RoomListViewState): RoomListViewModel { - return RoomListViewModel( - initialState = initialState, - session = session.get(), - stringProvider = stringProvider, - appStateHandler = appStateHandler, - vectorPreferences = vectorPreferences, - autoAcceptInvites = autoAcceptInvites - ) - } -} diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt index 8c1bdc086f..014ce14c95 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt @@ -26,8 +26,8 @@ import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith @@ -56,13 +56,13 @@ data class RoomListActionsArgs( /** * Bottom sheet fragment that shows room information with list of contextual actions */ +@AndroidEntryPoint class RoomListQuickActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), RoomListQuickActionsEpoxyController.Listener { private lateinit var sharedActionViewModel: RoomListQuickActionsSharedActionViewModel @Inject lateinit var sharedViewPool: RecyclerView.RecycledViewPool - @Inject lateinit var roomNotificationSettingsViewModelFactory: RoomNotificationSettingsViewModel.Factory @Inject lateinit var roomListActionsEpoxyController: RoomListQuickActionsEpoxyController @Inject lateinit var navigator: Navigator @Inject lateinit var errorFormatter: ErrorFormatter @@ -72,10 +72,6 @@ class RoomListQuickActionsBottomSheet : override val showExpanded = true - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetGenericListBinding { return BottomSheetGenericListBinding.inflate(inflater, container, false) } diff --git a/vector/src/main/java/im/vector/app/features/homeserver/HomeServerCapabilitiesViewModel.kt b/vector/src/main/java/im/vector/app/features/homeserver/HomeServerCapabilitiesViewModel.kt index deee0c4cf5..9223485eff 100644 --- a/vector/src/main/java/im/vector/app/features/homeserver/HomeServerCapabilitiesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/homeserver/HomeServerCapabilitiesViewModel.kt @@ -16,20 +16,20 @@ package im.vector.app.features.homeserver -import androidx.lifecycle.viewModelScope -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import im.vector.app.core.di.HasScreenInjector +import dagger.hilt.EntryPoints +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.SingletonEntryPoint +import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.raw.wellknown.getElementWellknown import im.vector.app.features.raw.wellknown.isE2EByDefault -import im.vector.app.features.userdirectory.UserListFragment import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.tryOrNull @@ -44,19 +44,14 @@ class HomeServerCapabilitiesViewModel @AssistedInject constructor( ) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: HomeServerCapabilitiesViewState): HomeServerCapabilitiesViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: HomeServerCapabilitiesViewState): HomeServerCapabilitiesViewModel } - companion object : MavericksViewModelFactory { - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: HomeServerCapabilitiesViewState): HomeServerCapabilitiesViewModel? { - val fragment: UserListFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.homeServerCapabilitiesViewModelFactory.create(state) - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() { - override fun initialState(viewModelContext: ViewModelContext): HomeServerCapabilitiesViewState? { - val session = (viewModelContext.activity as HasScreenInjector).injector().activeSessionHolder().getSafeActiveSession() + override fun initialState(viewModelContext: ViewModelContext): HomeServerCapabilitiesViewState { + val session = EntryPoints.get(viewModelContext.app(), SingletonEntryPoint::class.java).activeSessionHolder().getSafeActiveSession() return HomeServerCapabilitiesViewState( capabilities = session?.getHomeServerCapabilities() ?: HomeServerCapabilities() ) @@ -64,6 +59,7 @@ class HomeServerCapabilitiesViewModel @AssistedInject constructor( } init { + initAdminE2eByDefault() } diff --git a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomActivity.kt b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomActivity.kt index dd07319e9f..6f4aff0041 100644 --- a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomActivity.kt +++ b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomActivity.kt @@ -24,8 +24,8 @@ import android.view.View import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.extensions.addFragment import im.vector.app.core.extensions.addFragmentToBackstack @@ -37,14 +37,10 @@ import im.vector.app.core.utils.onPermissionDeniedSnackbar import im.vector.app.core.utils.registerForPermissionsResult import im.vector.app.core.utils.toast import im.vector.app.features.contactsbook.ContactsBookFragment -import im.vector.app.features.contactsbook.ContactsBookViewModel -import im.vector.app.features.contactsbook.ContactsBookViewState import im.vector.app.features.userdirectory.UserListFragment import im.vector.app.features.userdirectory.UserListFragmentArgs import im.vector.app.features.userdirectory.UserListSharedAction import im.vector.app.features.userdirectory.UserListSharedActionViewModel -import im.vector.app.features.userdirectory.UserListViewModel -import im.vector.app.features.userdirectory.UserListViewState import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.failure.Failure import java.net.HttpURLConnection @@ -53,26 +49,13 @@ import javax.inject.Inject @Parcelize data class InviteUsersToRoomArgs(val roomId: String) : Parcelable -class InviteUsersToRoomActivity : SimpleFragmentActivity(), UserListViewModel.Factory, ContactsBookViewModel.Factory, InviteUsersToRoomViewModel.Factory { +@AndroidEntryPoint +class InviteUsersToRoomActivity : SimpleFragmentActivity() { private val viewModel: InviteUsersToRoomViewModel by viewModel() private lateinit var sharedActionViewModel: UserListSharedActionViewModel - @Inject lateinit var userListViewModelFactory: UserListViewModel.Factory - @Inject lateinit var inviteUsersToRoomViewModelFactory: InviteUsersToRoomViewModel.Factory - @Inject lateinit var contactsBookViewModelFactory: ContactsBookViewModel.Factory @Inject lateinit var errorFormatter: ErrorFormatter - override fun injectWith(injector: ScreenComponent) { - super.injectWith(injector) - injector.inject(this) - } - - override fun create(initialState: UserListViewState) = userListViewModelFactory.create(initialState) - - override fun create(initialState: ContactsBookViewState) = contactsBookViewModelFactory.create(initialState) - - override fun create(initialState: InviteUsersToRoomViewState) = inviteUsersToRoomViewModelFactory.create(initialState) - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt index fd06614950..891194040e 100644 --- a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt @@ -16,14 +16,13 @@ package im.vector.app.features.invite -import com.airbnb.mvrx.ActivityViewModelContext -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory -import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.R +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.core.resources.StringProvider import im.vector.app.features.userdirectory.PendingSelection @@ -43,21 +42,11 @@ class InviteUsersToRoomViewModel @AssistedInject constructor(@Assisted private val room = session.getRoom(initialState.roomId)!! @AssistedFactory - interface Factory { - fun create(initialState: InviteUsersToRoomViewState): InviteUsersToRoomViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: InviteUsersToRoomViewState): InviteUsersToRoomViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: InviteUsersToRoomViewState): InviteUsersToRoomViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() override fun handle(action: InviteUsersToRoomAction) { when (action) { diff --git a/vector/src/main/java/im/vector/app/features/invite/VectorInviteView.kt b/vector/src/main/java/im/vector/app/features/invite/VectorInviteView.kt index 22f9e9a18c..d9f1ad343b 100644 --- a/vector/src/main/java/im/vector/app/features/invite/VectorInviteView.kt +++ b/vector/src/main/java/im/vector/app/features/invite/VectorInviteView.kt @@ -21,8 +21,8 @@ import android.util.AttributeSet import android.view.View import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.updateLayoutParams +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.HasScreenInjector import im.vector.app.databinding.VectorInviteViewBinding import im.vector.app.features.home.AvatarRenderer import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState @@ -30,6 +30,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject +@AndroidEntryPoint class VectorInviteView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : ConstraintLayout(context, attrs, defStyle) { @@ -49,9 +50,6 @@ class VectorInviteView @JvmOverloads constructor(context: Context, attrs: Attrib var callback: Callback? = null init { - if (context is HasScreenInjector) { - context.injector().inject(this) - } inflate(context, R.layout.vector_invite_view, this) views = VectorInviteViewBinding.bind(this) views.inviteAcceptView.commonClicked = { callback?.onAcceptInvite() } diff --git a/vector/src/main/java/im/vector/app/features/link/LinkHandlerActivity.kt b/vector/src/main/java/im/vector/app/features/link/LinkHandlerActivity.kt index 39105185b1..c22f8eb779 100644 --- a/vector/src/main/java/im/vector/app/features/link/LinkHandlerActivity.kt +++ b/vector/src/main/java/im/vector/app/features/link/LinkHandlerActivity.kt @@ -20,9 +20,9 @@ import android.content.Intent import android.net.Uri import androidx.lifecycle.lifecycleScope import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.utils.toast @@ -38,6 +38,7 @@ import javax.inject.Inject /** * Dummy activity used to dispatch the vector URL links. */ +@AndroidEntryPoint class LinkHandlerActivity : VectorBaseActivity() { @Inject lateinit var sessionHolder: ActiveSessionHolder @@ -46,10 +47,6 @@ class LinkHandlerActivity : VectorBaseActivity() { override fun getBinding() = ActivityProgressBinding.inflate(layoutInflater) - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - override fun initUiAndData() { handleIntent() } diff --git a/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt index a01e02f4e0..0ca076ccd7 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt @@ -20,7 +20,6 @@ import android.content.Context import android.content.Intent import android.view.View import android.view.ViewGroup -import androidx.annotation.CallSuper import androidx.core.view.ViewCompat import androidx.core.view.children import androidx.core.view.isVisible @@ -31,8 +30,8 @@ import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.withState import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.POP_BACK_STACK_EXCLUSIVE import im.vector.app.core.extensions.addFragment import im.vector.app.core.extensions.addFragmentToBackstack @@ -48,22 +47,15 @@ import im.vector.app.features.pin.UnlockedActivity import org.matrix.android.sdk.api.auth.registration.FlowResult import org.matrix.android.sdk.api.auth.registration.Stage import org.matrix.android.sdk.api.extensions.tryOrNull -import javax.inject.Inject /** * The LoginActivity manages the fragment navigation and also display the loading View */ +@AndroidEntryPoint open class LoginActivity : VectorBaseActivity(), ToolbarConfigurable, UnlockedActivity { private val loginViewModel: LoginViewModel by viewModel() - @Inject lateinit var loginViewModelFactory: LoginViewModel.Factory - - @CallSuper - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - private val enterAnim = R.anim.enter_fade_in private val exitAnim = R.anim.exit_fade_out diff --git a/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt index c613cf93d5..60f02cb2c6 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt @@ -193,7 +193,7 @@ class LoginFragment @Inject constructor() : AbstractSSOLoginFragment { views.loginSignupSigninSignInSocialLoginContainer.isVisible = true - views.loginSignupSigninSocialLoginButtons.ssoIdentityProviders = state.loginMode.ssoIdentityProviders() + views.loginSignupSigninSocialLoginButtons.ssoIdentityProviders = state.loginMode.ssoIdentityProviders()?.sorted() views.loginSignupSigninSocialLoginButtons.listener = object : SocialLoginButtonsView.InteractionListener { override fun onProviderSelected(id: String?) { loginViewModel.getSsoUrl( diff --git a/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt index ca126570ff..bfa924c155 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt @@ -18,25 +18,23 @@ package im.vector.app.features.login import android.content.Context import android.net.Uri -import androidx.fragment.app.FragmentActivity -import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.R 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.extensions.configureAndStart import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.ensureTrailingSlash -import im.vector.app.features.signout.soft.SoftLogoutActivity import kotlinx.coroutines.Job import kotlinx.coroutines.launch import org.matrix.android.sdk.api.MatrixPatterns.getDomain @@ -71,10 +69,12 @@ class LoginViewModel @AssistedInject constructor( ) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: LoginViewState): LoginViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: LoginViewState): LoginViewModel } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() + init { getKnownCustomHomeServersUrls() } @@ -85,18 +85,6 @@ class LoginViewModel @AssistedInject constructor( } } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: LoginViewState): LoginViewModel? { - return when (val activity: FragmentActivity = (viewModelContext as ActivityViewModelContext).activity()) { - is LoginActivity -> activity.loginViewModelFactory.create(state) - is SoftLogoutActivity -> activity.loginViewModelFactory.create(state) - else -> error("Invalid Activity") - } - } - } - // Store the last action, to redo it after user has trusted the untrusted certificate private var lastAction: LoginAction? = null private var currentHomeServerConnectionConfig: HomeServerConnectionConfig? = null diff --git a/vector/src/main/java/im/vector/app/features/login2/LoginActivity2.kt b/vector/src/main/java/im/vector/app/features/login2/LoginActivity2.kt index a640f02279..40dd1d2872 100644 --- a/vector/src/main/java/im/vector/app/features/login2/LoginActivity2.kt +++ b/vector/src/main/java/im/vector/app/features/login2/LoginActivity2.kt @@ -20,7 +20,6 @@ import android.content.Context import android.content.Intent import android.view.View import android.view.ViewGroup -import androidx.annotation.CallSuper import androidx.core.view.ViewCompat import androidx.core.view.children import androidx.core.view.isVisible @@ -30,8 +29,8 @@ import androidx.fragment.app.FragmentTransaction import com.airbnb.mvrx.viewModel import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.POP_BACK_STACK_EXCLUSIVE import im.vector.app.core.extensions.addFragment import im.vector.app.core.extensions.addFragmentToBackstack @@ -55,22 +54,15 @@ import im.vector.app.features.pin.UnlockedActivity import org.matrix.android.sdk.api.auth.registration.FlowResult import org.matrix.android.sdk.api.auth.registration.Stage import org.matrix.android.sdk.api.extensions.tryOrNull -import javax.inject.Inject /** * The LoginActivity manages the fragment navigation and also display the loading View */ +@AndroidEntryPoint open class LoginActivity2 : VectorBaseActivity(), ToolbarConfigurable, UnlockedActivity { private val loginViewModel: LoginViewModel2 by viewModel() - @Inject lateinit var loginViewModelFactory: LoginViewModel2.Factory - - @CallSuper - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - private val enterAnim = R.anim.enter_fade_in private val exitAnim = R.anim.exit_fade_out diff --git a/vector/src/main/java/im/vector/app/features/login2/LoginViewModel2.kt b/vector/src/main/java/im/vector/app/features/login2/LoginViewModel2.kt index 09ca979c6c..b73988126b 100644 --- a/vector/src/main/java/im/vector/app/features/login2/LoginViewModel2.kt +++ b/vector/src/main/java/im/vector/app/features/login2/LoginViewModel2.kt @@ -18,17 +18,16 @@ package im.vector.app.features.login2 import android.content.Context import android.net.Uri -import androidx.fragment.app.FragmentActivity import androidx.lifecycle.viewModelScope -import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory -import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.R 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.extensions.configureAndStart import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.tryAsync @@ -72,10 +71,12 @@ class LoginViewModel2 @AssistedInject constructor( ) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: LoginViewState2): LoginViewModel2 + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: LoginViewState2): LoginViewModel2 } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() + init { getKnownCustomHomeServersUrls() } @@ -86,18 +87,6 @@ class LoginViewModel2 @AssistedInject constructor( } } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: LoginViewState2): LoginViewModel2? { - return when (val activity: FragmentActivity = (viewModelContext as ActivityViewModelContext).activity()) { - is LoginActivity2 -> activity.loginViewModelFactory.create(state) - // TODO is SoftLogoutActivity -> activity.loginViewModelFactory.create(state) - else -> error("Invalid Activity") - } - } - } - // Store the last action, to redo it after user has trusted the untrusted certificate private var lastAction: LoginAction2? = null private var currentHomeServerConnectionConfig: HomeServerConnectionConfig? = null @@ -558,7 +547,7 @@ class LoginViewModel2 @AssistedInject constructor( safeLoginWizard.login( login = login, password = password, - deviceName = stringProvider.getString(R.string.login_default_session_public_name) + initialDeviceName = stringProvider.getString(R.string.login_default_session_public_name) ) } catch (failure: Throwable) { _viewEvents.post(LoginViewEvents2.Failure(failure)) diff --git a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedFragment.kt b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedFragment.kt index 5668214b50..efa4bd29c6 100644 --- a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedFragment.kt @@ -49,7 +49,6 @@ import javax.inject.Inject * - the account has been created and we propose the user to set an avatar and a display name */ class AccountCreatedFragment @Inject constructor( - val accountCreatedViewModelFactory: AccountCreatedViewModel.Factory, private val avatarRenderer: AvatarRenderer, private val dateFormatter: VectorDateFormatter, private val matrixItemColorProvider: MatrixItemColorProvider, diff --git a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewModel.kt b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewModel.kt index 34957dd47b..568cdab119 100644 --- a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedViewModel.kt @@ -16,13 +16,12 @@ package im.vector.app.features.login2.created -import androidx.lifecycle.viewModelScope -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory -import com.airbnb.mvrx.ViewModelContext 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.VectorViewModel import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch @@ -40,18 +39,11 @@ class AccountCreatedViewModel @AssistedInject constructor( ) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: AccountCreatedViewState): AccountCreatedViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: AccountCreatedViewState): AccountCreatedViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: AccountCreatedViewState): AccountCreatedViewModel? { - val fragment: AccountCreatedFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.accountCreatedViewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() init { setState { diff --git a/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheet.kt b/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheet.kt index aadabcb1b4..029234a66d 100644 --- a/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheet.kt @@ -28,8 +28,8 @@ import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.commitTransaction import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.databinding.BottomSheetMatrixToCardBinding @@ -39,6 +39,7 @@ import org.matrix.android.sdk.api.session.permalinks.PermalinkData import javax.inject.Inject import kotlin.reflect.KClass +@AndroidEntryPoint class MatrixToBottomSheet : VectorBaseBottomSheetDialogFragment() { @@ -49,13 +50,6 @@ class MatrixToBottomSheet : @Inject lateinit var avatarRenderer: AvatarRenderer - @Inject - lateinit var matrixToBottomSheetViewModelFactory: MatrixToBottomSheetViewModel.Factory - - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - var interactionListener: InteractionListener? = null override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetMatrixToCardBinding { diff --git a/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheetViewModel.kt index 327485a3b0..e741f6fb39 100644 --- a/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheetViewModel.kt @@ -16,18 +16,17 @@ package im.vector.app.features.matrixto -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.R +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel @@ -54,10 +53,12 @@ class MatrixToBottomSheetViewModel @AssistedInject constructor( VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: MatrixToBottomSheetState): MatrixToBottomSheetViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: MatrixToBottomSheetState): MatrixToBottomSheetViewModel } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() + init { when (initialState.linkType) { is PermalinkData.RoomLink -> { @@ -245,14 +246,6 @@ class MatrixToBottomSheetViewModel @AssistedInject constructor( return session.peekRoom(roomIdOrAlias) } - companion object : MavericksViewModelFactory { - override fun create(viewModelContext: ViewModelContext, state: MatrixToBottomSheetState): MatrixToBottomSheetViewModel? { - val fragment: MatrixToBottomSheet = (viewModelContext as FragmentViewModelContext).fragment() - - return fragment.matrixToBottomSheetViewModelFactory.create(state) - } - } - override fun handle(action: MatrixToAction) { when (action) { is MatrixToAction.StartChattingWithUser -> handleStartChatting(action) diff --git a/vector/src/main/java/im/vector/app/features/media/BigImageViewerActivity.kt b/vector/src/main/java/im/vector/app/features/media/BigImageViewerActivity.kt index 28d16adcbb..84454ee509 100644 --- a/vector/src/main/java/im/vector/app/features/media/BigImageViewerActivity.kt +++ b/vector/src/main/java/im/vector/app/features/media/BigImageViewerActivity.kt @@ -20,8 +20,8 @@ import android.content.Context import android.content.Intent import android.os.Bundle import androidx.core.net.toUri +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.core.di.ActiveSessionHolder -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivityBigImageViewerBinding import javax.inject.Inject @@ -29,15 +29,12 @@ import javax.inject.Inject /** * Simple Activity to display an avatar in fullscreen */ +@AndroidEntryPoint class BigImageViewerActivity : VectorBaseActivity() { @Inject lateinit var sessionHolder: ActiveSessionHolder override fun getBinding() = ActivityBigImageViewerBinding.inflate(layoutInflater) - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt b/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt index bc3acf3eec..103511bad5 100644 --- a/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt +++ b/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt @@ -30,12 +30,9 @@ import androidx.core.view.isInvisible import androidx.core.view.isVisible import androidx.lifecycle.lifecycleScope import androidx.transition.Transition +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder -import im.vector.app.core.di.DaggerScreenComponent -import im.vector.app.core.di.HasVectorInjector -import im.vector.app.core.di.ScreenComponent -import im.vector.app.core.di.VectorComponent import im.vector.app.core.intent.getMimeTypeFromUri import im.vector.app.core.utils.shareMedia import im.vector.app.features.themes.ActivityOtherThemes @@ -48,8 +45,8 @@ import kotlinx.coroutines.withContext import kotlinx.parcelize.Parcelize import timber.log.Timber import javax.inject.Inject -import kotlin.system.measureTimeMillis +@AndroidEntryPoint class VectorAttachmentViewerActivity : AttachmentViewerActivity(), BaseAttachmentProvider.InteractionListener { @Parcelize @@ -61,15 +58,11 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), BaseAttachmen @Inject lateinit var sessionHolder: ActiveSessionHolder - @Inject lateinit var dataSourceFactory: AttachmentProviderFactory - @Inject lateinit var imageContentRenderer: ImageContentRenderer - private lateinit var screenComponent: ScreenComponent - private var initialIndex = 0 private var isAnimatingOut = false @@ -78,12 +71,6 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), BaseAttachmen override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Timber.i("onCreate Activity ${javaClass.simpleName}") - val vectorComponent = getVectorComponent() - screenComponent = DaggerScreenComponent.factory().create(vectorComponent, this) - val timeForInjection = measureTimeMillis { - screenComponent.inject(this) - } - Timber.v("Injecting dependencies into ${javaClass.simpleName} took $timeForInjection ms") ThemeUtils.setActivityTheme(this, getOtherThemes()) val args = args() ?: throw IllegalArgumentException("Missing arguments") @@ -220,10 +207,6 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), BaseAttachmen private fun args() = intent.getParcelableExtra(EXTRA_ARGS) - private fun getVectorComponent(): VectorComponent { - return (application as HasVectorInjector).injector() - } - private fun scheduleStartPostponedTransition(sharedElement: View) { sharedElement.viewTreeObserver.addOnPreDrawListener( object : ViewTreeObserver.OnPreDrawListener { diff --git a/vector/src/main/java/im/vector/app/features/notifications/InviteNotifiableEvent.kt b/vector/src/main/java/im/vector/app/features/notifications/InviteNotifiableEvent.kt index 61fd5c677a..832f97bc4e 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/InviteNotifiableEvent.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/InviteNotifiableEvent.kt @@ -15,22 +15,18 @@ */ package im.vector.app.features.notifications -import androidx.core.app.NotificationCompat - data class InviteNotifiableEvent( - override var matrixID: String?, + val matrixID: String?, override val eventId: String, override val editedEventId: String?, - var roomId: String, - override var noisy: Boolean, - override val title: String, - override val description: String, - override val type: String?, - override val timestamp: Long, - override var soundName: String?, - override var isPushGatewayEvent: Boolean = false) : NotifiableEvent { - - override var hasBeenDisplayed: Boolean = false - override var isRedacted: Boolean = false - override var lockScreenVisibility = NotificationCompat.VISIBILITY_PUBLIC -} + override val canBeReplaced: Boolean, + val roomId: String, + val roomName: String?, + val noisy: Boolean, + val title: String, + val description: String, + val type: String?, + val timestamp: Long, + val soundName: String?, + override val isRedacted: Boolean = false +) : NotifiableEvent diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEvent.kt b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEvent.kt index a4f099b905..52d8119cbb 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEvent.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEvent.kt @@ -20,24 +20,11 @@ import java.io.Serializable /** * Parent interface for all events which can be displayed as a Notification */ -interface NotifiableEvent : Serializable { - var matrixID: String? +sealed interface NotifiableEvent : Serializable { val eventId: String val editedEventId: String? - var noisy: Boolean - val title: String - val description: String? - val type: String? - val timestamp: Long - - // NotificationCompat.VISIBILITY_PUBLIC , VISIBILITY_PRIVATE , VISIBILITY_SECRET - var lockScreenVisibility: Int - - // Compat: Only for android <7, for newer version the sound is defined in the channel - var soundName: String? - var hasBeenDisplayed: Boolean - var isRedacted: Boolean // Used to know if event should be replaced with the one coming from eventstream - var isPushGatewayEvent: Boolean + val canBeReplaced: Boolean + val isRedacted: Boolean } diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventProcessor.kt b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventProcessor.kt new file mode 100644 index 0000000000..3d10d74fe3 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventProcessor.kt @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.notifications + +import im.vector.app.features.invite.AutoAcceptInvites +import im.vector.app.features.notifications.ProcessedEvent.Type.KEEP +import im.vector.app.features.notifications.ProcessedEvent.Type.REMOVE +import org.matrix.android.sdk.api.session.events.model.EventType +import javax.inject.Inject + +private typealias ProcessedEvents = List> + +class NotifiableEventProcessor @Inject constructor( + private val outdatedDetector: OutdatedEventDetector, + private val autoAcceptInvites: AutoAcceptInvites +) { + + fun process(queuedEvents: List, currentRoomId: String?, renderedEvents: ProcessedEvents): ProcessedEvents { + val processedEvents = queuedEvents.map { + val type = when (it) { + is InviteNotifiableEvent -> if (autoAcceptInvites.hideInvites) REMOVE else KEEP + is NotifiableMessageEvent -> if (shouldIgnoreMessageEventInRoom(currentRoomId, it.roomId) || outdatedDetector.isMessageOutdated(it)) { + REMOVE + } else KEEP + is SimpleNotifiableEvent -> when (it.type) { + EventType.REDACTION -> REMOVE + else -> KEEP + } + } + ProcessedEvent(type, it) + } + + val removedEventsDiff = renderedEvents.filter { renderedEvent -> + queuedEvents.none { it.eventId == renderedEvent.event.eventId } + }.map { ProcessedEvent(REMOVE, it.event) } + + return removedEventsDiff + processedEvents + } + + private fun shouldIgnoreMessageEventInRoom(currentRoomId: String?, roomId: String?): Boolean { + return currentRoomId != null && roomId == currentRoomId + } +} diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventResolver.kt b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventResolver.kt index 63c296f418..d2db73af3d 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventResolver.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventResolver.kt @@ -15,7 +15,6 @@ */ package im.vector.app.features.notifications -import androidx.core.app.NotificationCompat import im.vector.app.BuildConfig import im.vector.app.R import im.vector.app.core.resources.StringProvider @@ -54,21 +53,19 @@ class NotifiableEventResolver @Inject constructor( // private val eventDisplay = RiotEventDisplay(context) - fun resolveEvent(event: Event/*, roomState: RoomState?, bingRule: PushRule?*/, session: Session): NotifiableEvent? { + fun resolveEvent(event: Event/*, roomState: RoomState?, bingRule: PushRule?*/, session: Session, isNoisy: Boolean): NotifiableEvent? { val roomID = event.roomId ?: return null val eventId = event.eventId ?: return null if (event.getClearType() == EventType.STATE_ROOM_MEMBER) { - return resolveStateRoomEvent(event, session) + return resolveStateRoomEvent(event, session, canBeReplaced = false, isNoisy = isNoisy) } val timelineEvent = session.getRoom(roomID)?.getTimeLineEvent(eventId) ?: return null when (event.getClearType()) { EventType.MESSAGE -> { - return resolveMessageEvent(timelineEvent, session) + return resolveMessageEvent(timelineEvent, session, canBeReplaced = false, isNoisy = isNoisy) } EventType.ENCRYPTED -> { - val messageEvent = resolveMessageEvent(timelineEvent, session) - messageEvent?.lockScreenVisibility = NotificationCompat.VISIBILITY_PRIVATE - return messageEvent + return resolveMessageEvent(timelineEvent, session, canBeReplaced = false, isNoisy = isNoisy) } else -> { // If the event can be displayed, display it as is @@ -85,12 +82,14 @@ class NotifiableEventResolver @Inject constructor( description = bodyPreview, title = stringProvider.getString(R.string.notification_unknown_new_event), soundName = null, - type = event.type) + type = event.type, + canBeReplaced = false + ) } } } - fun resolveInMemoryEvent(session: Session, event: Event): NotifiableEvent? { + fun resolveInMemoryEvent(session: Session, event: Event, canBeReplaced: Boolean): NotifiableEvent? { if (event.getClearType() != EventType.MESSAGE) return null // Ignore message edition @@ -114,24 +113,14 @@ class NotifiableEventResolver @Inject constructor( avatarUrl = user.avatarUrl ) ) - - val notifiableEvent = resolveMessageEvent(timelineEvent, session) - - if (notifiableEvent == null) { - Timber.d("## Failed to resolve event") - // TODO - null - } else { - notifiableEvent.noisy = !notificationAction.soundName.isNullOrBlank() - notifiableEvent - } + resolveMessageEvent(timelineEvent, session, canBeReplaced = canBeReplaced, isNoisy = !notificationAction.soundName.isNullOrBlank()) } else { Timber.d("Matched push rule is set to not notify") null } } - private fun resolveMessageEvent(event: TimelineEvent, session: Session): NotifiableEvent? { + private fun resolveMessageEvent(event: TimelineEvent, session: Session, canBeReplaced: Boolean, isNoisy: Boolean): NotifiableEvent { // The event only contains an eventId, and roomId (type is m.room.*) , we need to get the displayable content (names, avatar, text, etc...) val room = session.getRoom(event.root.roomId!! /*roomID cannot be null*/) @@ -142,19 +131,19 @@ class NotifiableEventResolver @Inject constructor( val roomName = stringProvider.getString(R.string.notification_unknown_room_name) val senderDisplayName = event.senderInfo.disambiguatedDisplayName - val notifiableEvent = NotifiableMessageEvent( + return NotifiableMessageEvent( eventId = event.root.eventId!!, editedEventId = event.getEditedEventId(), + canBeReplaced = canBeReplaced, timestamp = event.root.originServerTs ?: 0, - noisy = false, // will be updated + noisy = isNoisy, senderName = senderDisplayName, senderId = event.root.senderId, body = body.toString(), roomId = event.root.roomId!!, - roomName = roomName) - - notifiableEvent.matrixID = session.myUserId - return notifiableEvent + roomName = roomName, + matrixID = session.myUserId + ) } else { if (event.root.isEncrypted() && event.root.mxDecryptionResult == null) { // TODO use a global event decryptor? attache to session and that listen to new sessionId? @@ -175,57 +164,56 @@ class NotifiableEventResolver @Inject constructor( val roomName = room.roomSummary()?.displayName ?: "" val senderDisplayName = event.senderInfo.disambiguatedDisplayName - val notifiableEvent = NotifiableMessageEvent( + return NotifiableMessageEvent( eventId = event.root.eventId!!, editedEventId = event.getEditedEventId(), + canBeReplaced = canBeReplaced, timestamp = event.root.originServerTs ?: 0, - noisy = false, // will be updated + noisy = isNoisy, senderName = senderDisplayName, senderId = event.root.senderId, body = body, roomId = event.root.roomId!!, roomName = roomName, - roomIsDirect = room.roomSummary()?.isDirect ?: false) - - notifiableEvent.matrixID = session.myUserId - notifiableEvent.soundName = null - - // Get the avatars URL - notifiableEvent.roomAvatarPath = session.contentUrlResolver() - .resolveThumbnail(room.roomSummary()?.avatarUrl, - 250, - 250, - ContentUrlResolver.ThumbnailMethod.SCALE) - - notifiableEvent.senderAvatarPath = session.contentUrlResolver() - .resolveThumbnail(event.senderInfo.avatarUrl, - 250, - 250, - ContentUrlResolver.ThumbnailMethod.SCALE) - - return notifiableEvent + roomIsDirect = room.roomSummary()?.isDirect ?: false, + roomAvatarPath = session.contentUrlResolver() + .resolveThumbnail(room.roomSummary()?.avatarUrl, + 250, + 250, + ContentUrlResolver.ThumbnailMethod.SCALE), + senderAvatarPath = session.contentUrlResolver() + .resolveThumbnail(event.senderInfo.avatarUrl, + 250, + 250, + ContentUrlResolver.ThumbnailMethod.SCALE), + matrixID = session.myUserId, + soundName = null + ) } } - private fun resolveStateRoomEvent(event: Event, session: Session): NotifiableEvent? { + private fun resolveStateRoomEvent(event: Event, session: Session, canBeReplaced: Boolean, isNoisy: Boolean): NotifiableEvent? { val content = event.content?.toModel() ?: return null val roomId = event.roomId ?: return null val dName = event.senderId?.let { session.getRoomMember(it, roomId)?.displayName } if (Membership.INVITE == content.membership) { - val body = noticeEventFormatter.format(event, dName, isDm = session.getRoomSummary(roomId)?.isDirect.orFalse()) + val roomSummary = session.getRoomSummary(roomId) + val body = noticeEventFormatter.format(event, dName, isDm = roomSummary?.isDirect.orFalse()) ?: stringProvider.getString(R.string.notification_new_invitation) return InviteNotifiableEvent( session.myUserId, eventId = event.eventId!!, editedEventId = null, + canBeReplaced = canBeReplaced, roomId = roomId, + roomName = roomSummary?.displayName, timestamp = event.originServerTs ?: 0, - noisy = false, // will be set later + noisy = isNoisy, title = stringProvider.getString(R.string.notification_new_invitation), description = body.toString(), soundName = null, // will be set later - type = event.getClearType(), - isPushGatewayEvent = false) + type = event.getClearType() + ) } else { Timber.e("## unsupported notifiable event for eventId [${event.eventId}]") if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) { diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotifiableMessageEvent.kt b/vector/src/main/java/im/vector/app/features/notifications/NotifiableMessageEvent.kt index fb9ca8d23c..161c9f74a6 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotifiableMessageEvent.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotifiableMessageEvent.kt @@ -15,43 +15,31 @@ */ package im.vector.app.features.notifications -import androidx.core.app.NotificationCompat import org.matrix.android.sdk.api.session.events.model.EventType data class NotifiableMessageEvent( override val eventId: String, override val editedEventId: String?, - override var noisy: Boolean, - override val timestamp: Long, - var senderName: String?, - var senderId: String?, - var body: String?, - var roomId: String, - var roomName: String?, - var roomIsDirect: Boolean = false + override val canBeReplaced: Boolean, + val noisy: Boolean, + val timestamp: Long, + val senderName: String?, + val senderId: String?, + val body: String?, + val roomId: String, + val roomName: String?, + val roomIsDirect: Boolean = false, + val roomAvatarPath: String? = null, + val senderAvatarPath: String? = null, + val matrixID: String? = null, + val soundName: String? = null, + // This is used for >N notification, as the result of a smart reply + val outGoingMessage: Boolean = false, + val outGoingMessageFailed: Boolean = false, + override val isRedacted: Boolean = false ) : NotifiableEvent { - override var matrixID: String? = null - override var soundName: String? = null - override var lockScreenVisibility = NotificationCompat.VISIBILITY_PUBLIC - override var hasBeenDisplayed: Boolean = false - override var isRedacted: Boolean = false - - var roomAvatarPath: String? = null - var senderAvatarPath: String? = null - - override var isPushGatewayEvent: Boolean = false - - override val type: String - get() = EventType.MESSAGE - - override val description: String? - get() = body ?: "" - - override val title: String - get() = senderName ?: "" - - // This is used for >N notification, as the result of a smart reply - var outGoingMessage = false - var outGoingMessageFailed = false + val type: String = EventType.MESSAGE + val description: String = body ?: "" + val title: String = senderName ?: "" } diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationBroadcastReceiver.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationBroadcastReceiver.kt index 2c4cdab25e..33e43cd7e4 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationBroadcastReceiver.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationBroadcastReceiver.kt @@ -20,9 +20,9 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import androidx.core.app.RemoteInput +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder -import im.vector.app.core.extensions.vectorComponent import im.vector.app.features.session.coroutineScope import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.tryOrNull @@ -36,6 +36,7 @@ import javax.inject.Inject /** * Receives actions broadcast by notification (on click, on dismiss, inline replies, etc.) */ +@AndroidEntryPoint class NotificationBroadcastReceiver : BroadcastReceiver() { @Inject lateinit var notificationDrawerManager: NotificationDrawerManager @@ -44,7 +45,6 @@ class NotificationBroadcastReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { if (intent == null || context == null) return Timber.v("NotificationBroadcastReceiver received : $intent") - context.vectorComponent().inject(this) when (intent.action) { NotificationUtils.SMART_REPLY_ACTION -> handleSmartReply(intent, context) @@ -130,19 +130,20 @@ class NotificationBroadcastReceiver : BroadcastReceiver() { val notifiableMessageEvent = NotifiableMessageEvent( // Generate a Fake event id - UUID.randomUUID().toString(), - null, - false, - System.currentTimeMillis(), - session.getRoomMember(session.myUserId, room.roomId)?.displayName + eventId = UUID.randomUUID().toString(), + editedEventId = null, + noisy = false, + timestamp = System.currentTimeMillis(), + senderName = session.getRoomMember(session.myUserId, room.roomId)?.displayName ?: context?.getString(R.string.notification_sender_me), - session.myUserId, - message, - room.roomId, - room.roomSummary()?.displayName ?: room.roomId, - room.roomSummary()?.isDirect == true + senderId = session.myUserId, + body = message, + roomId = room.roomId, + roomName = room.roomSummary()?.displayName ?: room.roomId, + roomIsDirect = room.roomSummary()?.isDirect == true, + outGoingMessage = true, + canBeReplaced = false ) - notifiableMessageEvent.outGoingMessage = true notificationDrawerManager.onNotifiableEventReceived(notifiableMessageEvent) notificationDrawerManager.refreshNotificationDrawer() diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationDisplayer.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationDisplayer.kt new file mode 100644 index 0000000000..680ff32a52 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationDisplayer.kt @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.notifications + +import android.app.Notification +import android.content.Context +import androidx.core.app.NotificationManagerCompat +import timber.log.Timber +import javax.inject.Inject + +class NotificationDisplayer @Inject constructor(context: Context) { + + private val notificationManager = NotificationManagerCompat.from(context) + + fun showNotificationMessage(tag: String?, id: Int, notification: Notification) { + notificationManager.notify(tag, id, notification) + } + + fun cancelNotificationMessage(tag: String?, id: Int) { + notificationManager.cancel(tag, id) + } + + fun cancelAllNotifications() { + // Keep this try catch (reported by GA) + try { + notificationManager.cancelAll() + } catch (e: Exception) { + Timber.e(e, "## cancelAllNotifications() failed") + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt index e4b2ead93d..c052de650e 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt @@ -16,26 +16,15 @@ package im.vector.app.features.notifications import android.content.Context -import android.graphics.Bitmap -import android.os.Build import android.os.Handler import android.os.HandlerThread import androidx.annotation.WorkerThread -import androidx.core.app.NotificationCompat -import androidx.core.app.Person -import androidx.core.content.pm.ShortcutInfoCompat -import androidx.core.content.pm.ShortcutManagerCompat -import androidx.core.graphics.drawable.IconCompat import im.vector.app.ActiveSessionDataSource import im.vector.app.BuildConfig import im.vector.app.R -import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.FirstThrottler import im.vector.app.features.displayname.getBestName -import im.vector.app.features.home.room.detail.RoomDetailActivity -import im.vector.app.features.invite.AutoAcceptInvites import im.vector.app.features.settings.VectorPreferences -import me.gujun.android.span.span import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.content.ContentUrlResolver import org.matrix.android.sdk.api.util.toMatrixItem @@ -52,14 +41,11 @@ import javax.inject.Singleton */ @Singleton class NotificationDrawerManager @Inject constructor(private val context: Context, - private val notificationUtils: NotificationUtils, + private val notificationDisplayer: NotificationDisplayer, private val vectorPreferences: VectorPreferences, - private val stringProvider: StringProvider, private val activeSessionDataSource: ActiveSessionDataSource, - private val iconLoader: IconLoader, - private val bitmapLoader: BitmapLoader, - private val outdatedDetector: OutdatedEventDetector?, - private val autoAcceptInvites: AutoAcceptInvites) { + private val notifiableEventProcessor: NotifiableEventProcessor, + private val notificationRenderer: NotificationRenderer) { private val handlerThread: HandlerThread = HandlerThread("NotificationDrawerManager", Thread.MIN_PRIORITY) private var backgroundHandler: Handler @@ -69,13 +55,23 @@ class NotificationDrawerManager @Inject constructor(private val context: Context backgroundHandler = Handler(handlerThread.looper) } - // The first time the notification drawer is refreshed, we force re-render of all notifications - private var firstTime = true - - private val eventList = loadEventInfo() + /** + * The notifiable events to render + * this is our source of truth for notifications, any changes to this list will be rendered as notifications + * when events are removed the previously rendered notifications will be cancelled + * when adding or updating, the notifications will be notified + * + * Events are unique by their properties, we should be careful not to insert multiple events with the same event-id + */ + private val queuedEvents = loadEventInfo() + /** + * The last known rendered notifiable events + * we keep track of them in order to know which events have been removed from the eventList + * allowing us to cancel any notifications previous displayed by now removed events + */ + private var renderedEvents = emptyList>() private val avatarSize = context.resources.getDimensionPixelSize(R.dimen.profile_avatar_size) - private var currentRoomId: String? = null // TODO Multi-session: this will have to be improved @@ -107,12 +103,12 @@ class NotificationDrawerManager @Inject constructor(private val context: Context if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) { Timber.d("onNotifiableEventReceived(): $notifiableEvent") } else { - Timber.d("onNotifiableEventReceived(): is push: ${notifiableEvent.isPushGatewayEvent}") + Timber.d("onNotifiableEventReceived(): is push: ${notifiableEvent.canBeReplaced}") } - synchronized(eventList) { - val existing = eventList.firstOrNull { it.eventId == notifiableEvent.eventId } + synchronized(queuedEvents) { + val existing = queuedEvents.firstOrNull { it.eventId == notifiableEvent.eventId } if (existing != null) { - if (existing.isPushGatewayEvent) { + if (existing.canBeReplaced) { // Use the event coming from the event stream as it may contains more info than // the fcm one (like type/content/clear text) (e.g when an encrypted message from // FCM should be update with clear text after a sync) @@ -121,9 +117,8 @@ class NotificationDrawerManager @Inject constructor(private val context: Context // Use setOnlyAlertOnce to ensure update notification does not interfere with sound // from first notify invocation as outlined in: // https://developer.android.com/training/notify-user/build-notification#Updating - notifiableEvent.hasBeenDisplayed = false - eventList.remove(existing) - eventList.add(notifiableEvent) + queuedEvents.remove(existing) + queuedEvents.add(notifiableEvent) } else { // keep the existing one, do not replace } @@ -131,7 +126,7 @@ class NotificationDrawerManager @Inject constructor(private val context: Context // Check if this is an edit if (notifiableEvent.editedEventId != null) { // This is an edition - val eventBeforeEdition = eventList.firstOrNull { + val eventBeforeEdition = queuedEvents.firstOrNull { // Edition of an event it.eventId == notifiableEvent.editedEventId || // or edition of an edition @@ -140,9 +135,9 @@ class NotificationDrawerManager @Inject constructor(private val context: Context if (eventBeforeEdition != null) { // Replace the existing notification with the new content - eventList.remove(eventBeforeEdition) + queuedEvents.remove(eventBeforeEdition) - eventList.add(notifiableEvent) + queuedEvents.add(notifiableEvent) } else { // Ignore an edit of a not displayed event in the notification drawer } @@ -153,7 +148,7 @@ class NotificationDrawerManager @Inject constructor(private val context: Context Timber.d("onNotifiableEventReceived(): skipping event, already seen") } else { seenEventIds.put(notifiableEvent.eventId) - eventList.add(notifiableEvent) + queuedEvents.add(notifiableEvent) } } } @@ -161,10 +156,13 @@ class NotificationDrawerManager @Inject constructor(private val context: Context } fun onEventRedacted(eventId: String) { - synchronized(eventList) { - eventList.find { it.eventId == eventId }?.apply { - isRedacted = true - hasBeenDisplayed = false + synchronized(queuedEvents) { + queuedEvents.replace(eventId) { + when (it) { + is InviteNotifiableEvent -> it.copy(isRedacted = true) + is NotifiableMessageEvent -> it.copy(isRedacted = true) + is SimpleNotifiableEvent -> it.copy(isRedacted = true) + } } } } @@ -173,8 +171,8 @@ class NotificationDrawerManager @Inject constructor(private val context: Context * Clear all known events and refresh the notification drawer */ fun clearAllEvents() { - synchronized(eventList) { - eventList.clear() + synchronized(queuedEvents) { + queuedEvents.clear() } refreshNotificationDrawer() } @@ -183,14 +181,8 @@ class NotificationDrawerManager @Inject constructor(private val context: Context fun clearMessageEventOfRoom(roomId: String?) { Timber.v("clearMessageEventOfRoom $roomId") if (roomId != null) { - var shouldUpdate = false - synchronized(eventList) { - shouldUpdate = eventList.removeAll { e -> - e is NotifiableMessageEvent && e.roomId == roomId - } - } + val shouldUpdate = removeAll { it is NotifiableMessageEvent && it.roomId == roomId } if (shouldUpdate) { - notificationUtils.cancelNotificationMessage(roomId, ROOM_MESSAGES_NOTIFICATION_ID) refreshNotificationDrawer() } } @@ -202,7 +194,7 @@ class NotificationDrawerManager @Inject constructor(private val context: Context */ fun setCurrentRoom(roomId: String?) { var hasChanged: Boolean - synchronized(eventList) { + synchronized(queuedEvents) { hasChanged = roomId != currentRoomId currentRoomId = roomId } @@ -212,12 +204,16 @@ class NotificationDrawerManager @Inject constructor(private val context: Context } fun clearMemberShipNotificationForRoom(roomId: String) { - synchronized(eventList) { - eventList.removeAll { e -> - e is InviteNotifiableEvent && e.roomId == roomId - } + val shouldUpdate = removeAll { it is InviteNotifiableEvent && it.roomId == roomId } + if (shouldUpdate) { + refreshNotificationDrawerBg() + } + } + + private fun removeAll(predicate: (NotifiableEvent) -> Boolean): Boolean { + return synchronized(queuedEvents) { + queuedEvents.removeAll(predicate) } - notificationUtils.cancelNotificationMessage(roomId, ROOM_INVITATION_NOTIFICATION_ID) } private var firstThrottler = FirstThrottler(200) @@ -244,359 +240,36 @@ class NotificationDrawerManager @Inject constructor(private val context: Context private fun refreshNotificationDrawerBg() { Timber.v("refreshNotificationDrawerBg()") - val session = currentSession ?: return - - val user = session.getUser(session.myUserId) - // myUserDisplayName cannot be empty else NotificationCompat.MessagingStyle() will crash - val myUserDisplayName = user?.toMatrixItem()?.getBestName() ?: session.myUserId - val myUserAvatarUrl = session.contentUrlResolver().resolveThumbnail(user?.avatarUrl, avatarSize, avatarSize, ContentUrlResolver.ThumbnailMethod.SCALE) - synchronized(eventList) { - Timber.v("%%%%%%%% REFRESH NOTIFICATION DRAWER ") - // TMP code - var hasNewEvent = false - var summaryIsNoisy = false - val summaryInboxStyle = NotificationCompat.InboxStyle() - - // group events by room to create a single MessagingStyle notif - val roomIdToEventMap: MutableMap> = LinkedHashMap() - val simpleEvents: MutableList = ArrayList() - val invitationEvents: MutableList = ArrayList() - - val eventIterator = eventList.listIterator() - while (eventIterator.hasNext()) { - when (val event = eventIterator.next()) { - is NotifiableMessageEvent -> { - val roomId = event.roomId - val roomEvents = roomIdToEventMap.getOrPut(roomId) { ArrayList() } - - if (shouldIgnoreMessageEventInRoom(roomId) || outdatedDetector?.isMessageOutdated(event) == true) { - // forget this event - eventIterator.remove() - } else { - roomEvents.add(event) - } - } - is InviteNotifiableEvent -> { - if (autoAcceptInvites.hideInvites) { - // Forget this event - eventIterator.remove() - } else { - invitationEvents.add(event) - } - } - is SimpleNotifiableEvent -> simpleEvents.add(event) - else -> Timber.w("Type not handled") - } - } - - Timber.v("%%%%%%%% REFRESH NOTIFICATION DRAWER ${roomIdToEventMap.size} room groups") - - var globalLastMessageTimestamp = 0L - - val newSettings = vectorPreferences.useCompleteNotificationFormat() - if (newSettings != useCompleteNotificationFormat) { - // Settings has changed, remove all current notifications - notificationUtils.cancelAllNotifications() - useCompleteNotificationFormat = newSettings - } - - var simpleNotificationRoomCounter = 0 - var simpleNotificationMessageCounter = 0 - - // events have been grouped by roomId - for ((roomId, events) in roomIdToEventMap) { - // Build the notification for the room - if (events.isEmpty() || events.all { it.isRedacted }) { - // Just clear this notification - Timber.v("%%%%%%%% REFRESH NOTIFICATION DRAWER $roomId has no more events") - notificationUtils.cancelNotificationMessage(roomId, ROOM_MESSAGES_NOTIFICATION_ID) - continue - } - - simpleNotificationRoomCounter++ - val roomName = events[0].roomName ?: events[0].senderName ?: "" - - val roomEventGroupInfo = RoomEventGroupInfo( - roomId = roomId, - isDirect = events[0].roomIsDirect, - roomDisplayName = roomName) - - val style = NotificationCompat.MessagingStyle(Person.Builder() - .setName(myUserDisplayName) - .setIcon(iconLoader.getUserIcon(myUserAvatarUrl)) - .setKey(events[0].matrixID) - .build()) - - style.isGroupConversation = !roomEventGroupInfo.isDirect - - if (!roomEventGroupInfo.isDirect) { - style.conversationTitle = roomEventGroupInfo.roomDisplayName - } - - val largeBitmap = getRoomBitmap(events) - - for (event in events) { - // if all events in this room have already been displayed there is no need to update it - if (!event.hasBeenDisplayed && !event.isRedacted) { - roomEventGroupInfo.shouldBing = roomEventGroupInfo.shouldBing || event.noisy - roomEventGroupInfo.customSound = event.soundName - } - roomEventGroupInfo.hasNewEvent = roomEventGroupInfo.hasNewEvent || !event.hasBeenDisplayed - - val senderPerson = if (event.outGoingMessage) { - null - } else { - Person.Builder() - .setName(event.senderName) - .setIcon(iconLoader.getUserIcon(event.senderAvatarPath)) - .setKey(event.senderId) - .build() - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - val openRoomIntent = RoomDetailActivity.shortcutIntent(context, roomId) - - val shortcut = ShortcutInfoCompat.Builder(context, roomId) - .setLongLived(true) - .setIntent(openRoomIntent) - .setShortLabel(roomName) - .setIcon(largeBitmap?.let { IconCompat.createWithAdaptiveBitmap(it) } ?: iconLoader.getUserIcon(event.senderAvatarPath)) - .build() - - ShortcutManagerCompat.pushDynamicShortcut(context, shortcut) - } - - if (event.outGoingMessage && event.outGoingMessageFailed) { - style.addMessage(stringProvider.getString(R.string.notification_inline_reply_failed), event.timestamp, senderPerson) - roomEventGroupInfo.hasSmartReplyError = true - } else { - if (!event.isRedacted) { - simpleNotificationMessageCounter++ - style.addMessage(event.body, event.timestamp, senderPerson) - } - } - event.hasBeenDisplayed = true // we can consider it as displayed - - // It is possible that this event was previously shown as an 'anonymous' simple notif. - // And now it will be merged in a single MessageStyle notif, so we can clean to be sure - notificationUtils.cancelNotificationMessage(event.eventId, ROOM_EVENT_NOTIFICATION_ID) - } - - try { - if (events.size == 1) { - val event = events[0] - if (roomEventGroupInfo.isDirect) { - val line = span { - span { - textStyle = "bold" - +String.format("%s: ", event.senderName) - } - +(event.description ?: "") - } - summaryInboxStyle.addLine(line) - } else { - val line = span { - span { - textStyle = "bold" - +String.format("%s: %s ", roomName, event.senderName) - } - +(event.description ?: "") - } - summaryInboxStyle.addLine(line) - } - } else { - val summaryLine = stringProvider.getQuantityString( - R.plurals.notification_compat_summary_line_for_room, events.size, roomName, events.size) - summaryInboxStyle.addLine(summaryLine) - } - } catch (e: Throwable) { - // String not found or bad format - Timber.v("%%%%%%%% REFRESH NOTIFICATION DRAWER failed to resolve string") - summaryInboxStyle.addLine(roomName) - } - - if (firstTime || roomEventGroupInfo.hasNewEvent) { - // Should update displayed notification - Timber.v("%%%%%%%% REFRESH NOTIFICATION DRAWER $roomId need refresh") - val lastMessageTimestamp = events.last().timestamp - - if (globalLastMessageTimestamp < lastMessageTimestamp) { - globalLastMessageTimestamp = lastMessageTimestamp - } - - val tickerText = if (roomEventGroupInfo.isDirect) { - stringProvider.getString(R.string.notification_ticker_text_dm, events.last().senderName, events.last().description) - } else { - stringProvider.getString(R.string.notification_ticker_text_group, roomName, events.last().senderName, events.last().description) - } - - if (useCompleteNotificationFormat) { - val notification = notificationUtils.buildMessagesListNotification( - style, - roomEventGroupInfo, - largeBitmap, - lastMessageTimestamp, - myUserDisplayName, - tickerText) - - // is there an id for this room? - notificationUtils.showNotificationMessage(roomId, ROOM_MESSAGES_NOTIFICATION_ID, notification) - } - - hasNewEvent = true - summaryIsNoisy = summaryIsNoisy || roomEventGroupInfo.shouldBing - } else { - Timber.v("%%%%%%%% REFRESH NOTIFICATION DRAWER $roomId is up to date") - } - } - - // Handle invitation events - for (event in invitationEvents) { - // We build a invitation notification - if (firstTime || !event.hasBeenDisplayed) { - if (useCompleteNotificationFormat) { - val notification = notificationUtils.buildRoomInvitationNotification(event, session.myUserId) - notificationUtils.showNotificationMessage(event.roomId, ROOM_INVITATION_NOTIFICATION_ID, notification) - } - event.hasBeenDisplayed = true // we can consider it as displayed - hasNewEvent = true - summaryIsNoisy = summaryIsNoisy || event.noisy - summaryInboxStyle.addLine(event.description) - } - } - - // Handle simple events - for (event in simpleEvents) { - // We build a simple notification - if (firstTime || !event.hasBeenDisplayed) { - if (useCompleteNotificationFormat) { - val notification = notificationUtils.buildSimpleEventNotification(event, session.myUserId) - notificationUtils.showNotificationMessage(event.eventId, ROOM_EVENT_NOTIFICATION_ID, notification) - } - event.hasBeenDisplayed = true // we can consider it as displayed - hasNewEvent = true - summaryIsNoisy = summaryIsNoisy || event.noisy - summaryInboxStyle.addLine(event.description) - } - } - - // ======== Build summary notification ========= - // On Android 7.0 (API level 24) and higher, the system automatically builds a summary for - // your group using snippets of text from each notification. The user can expand this - // notification to see each separate notification. - // To support older versions, which cannot show a nested group of notifications, - // you must create an extra notification that acts as the summary. - // This appears as the only notification and the system hides all the others. - // So this summary should include a snippet from all the other notifications, - // which the user can tap to open your app. - // The behavior of the group summary may vary on some device types such as wearables. - // To ensure the best experience on all devices and versions, always include a group summary when you create a group - // https://developer.android.com/training/notify-user/group - - if (eventList.isEmpty() || eventList.all { it.isRedacted }) { - notificationUtils.cancelNotificationMessage(null, SUMMARY_NOTIFICATION_ID) - } else if (hasNewEvent) { - // FIXME roomIdToEventMap.size is not correct, this is the number of rooms - val nbEvents = roomIdToEventMap.size + simpleEvents.size - val sumTitle = stringProvider.getQuantityString(R.plurals.notification_compat_summary_title, nbEvents, nbEvents) - summaryInboxStyle.setBigContentTitle(sumTitle) - // TODO get latest event? - .setSummaryText(stringProvider.getQuantityString(R.plurals.notification_unread_notified_messages, nbEvents, nbEvents)) - - if (useCompleteNotificationFormat) { - val notification = notificationUtils.buildSummaryListNotification( - summaryInboxStyle, - sumTitle, - noisy = hasNewEvent && summaryIsNoisy, - lastMessageTimestamp = globalLastMessageTimestamp) - - notificationUtils.showNotificationMessage(null, SUMMARY_NOTIFICATION_ID, notification) - } else { - // Add the simple events as message (?) - simpleNotificationMessageCounter += simpleEvents.size - val numberOfInvitations = invitationEvents.size - - val privacyTitle = if (numberOfInvitations > 0) { - val invitationsStr = stringProvider.getQuantityString(R.plurals.notification_invitations, numberOfInvitations, numberOfInvitations) - if (simpleNotificationMessageCounter > 0) { - // Invitation and message - val messageStr = stringProvider.getQuantityString(R.plurals.room_new_messages_notification, - simpleNotificationMessageCounter, simpleNotificationMessageCounter) - if (simpleNotificationRoomCounter > 1) { - // In several rooms - val roomStr = stringProvider.getQuantityString(R.plurals.notification_unread_notified_messages_in_room_rooms, - simpleNotificationRoomCounter, simpleNotificationRoomCounter) - stringProvider.getString( - R.string.notification_unread_notified_messages_in_room_and_invitation, - messageStr, - roomStr, - invitationsStr - ) - } else { - // In one room - stringProvider.getString( - R.string.notification_unread_notified_messages_and_invitation, - messageStr, - invitationsStr - ) - } - } else { - // Only invitation - invitationsStr - } - } else { - // No invitation, only messages - val messageStr = stringProvider.getQuantityString(R.plurals.room_new_messages_notification, - simpleNotificationMessageCounter, simpleNotificationMessageCounter) - if (simpleNotificationRoomCounter > 1) { - // In several rooms - val roomStr = stringProvider.getQuantityString(R.plurals.notification_unread_notified_messages_in_room_rooms, - simpleNotificationRoomCounter, simpleNotificationRoomCounter) - stringProvider.getString(R.string.notification_unread_notified_messages_in_room, messageStr, roomStr) - } else { - // In one room - messageStr - } - } - val notification = notificationUtils.buildSummaryListNotification( - style = null, - compatSummary = privacyTitle, - noisy = hasNewEvent && summaryIsNoisy, - lastMessageTimestamp = globalLastMessageTimestamp) - - notificationUtils.showNotificationMessage(null, SUMMARY_NOTIFICATION_ID, notification) - } - - if (hasNewEvent && summaryIsNoisy) { - try { - // turn the screen on for 3 seconds - /* - TODO - if (Matrix.getInstance(VectorApp.getInstance())!!.pushManager.isScreenTurnedOn) { - val pm = VectorApp.getInstance().getSystemService()!! - val wl = pm.newWakeLock(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON or PowerManager.ACQUIRE_CAUSES_WAKEUP, - NotificationDrawerManager::class.java.name) - wl.acquire(3000) - wl.release() - } - */ - } catch (e: Throwable) { - Timber.e(e, "## Failed to turn screen on") - } - } - } - // notice that we can get bit out of sync with actual display but not a big issue - firstTime = false + val newSettings = vectorPreferences.useCompleteNotificationFormat() + if (newSettings != useCompleteNotificationFormat) { + // Settings has changed, remove all current notifications + notificationDisplayer.cancelAllNotifications() + useCompleteNotificationFormat = newSettings } - } - private fun getRoomBitmap(events: List): Bitmap? { - if (events.isEmpty()) return null + val eventsToRender = synchronized(queuedEvents) { + notifiableEventProcessor.process(queuedEvents, currentRoomId, renderedEvents).also { + queuedEvents.clear() + queuedEvents.addAll(it.onlyKeptEvents()) + } + } - // Use the last event (most recent?) - val roomAvatarPath = events.last().roomAvatarPath ?: events.last().senderAvatarPath - - return bitmapLoader.getRoomBitmap(roomAvatarPath) + if (renderedEvents == eventsToRender) { + Timber.d("Skipping notification update due to event list not changing") + } else { + renderedEvents = eventsToRender + val session = currentSession ?: return + val user = session.getUser(session.myUserId) + // myUserDisplayName cannot be empty else NotificationCompat.MessagingStyle() will crash + val myUserDisplayName = user?.toMatrixItem()?.getBestName() ?: session.myUserId + val myUserAvatarUrl = session.contentUrlResolver().resolveThumbnail( + contentUrl = user?.avatarUrl, + width = avatarSize, + height = avatarSize, + method = ContentUrlResolver.ThumbnailMethod.SCALE + ) + notificationRenderer.render(session.myUserId, myUserDisplayName, myUserAvatarUrl, useCompleteNotificationFormat, eventsToRender) + } } fun shouldIgnoreMessageEventInRoom(roomId: String?): Boolean { @@ -604,8 +277,8 @@ class NotificationDrawerManager @Inject constructor(private val context: Context } fun persistInfo() { - synchronized(eventList) { - if (eventList.isEmpty()) { + synchronized(queuedEvents) { + if (queuedEvents.isEmpty()) { deleteCachedRoomNotifications() return } @@ -613,7 +286,7 @@ class NotificationDrawerManager @Inject constructor(private val context: Context val file = File(context.applicationContext.cacheDir, ROOMS_NOTIFICATIONS_FILE_NAME) if (!file.exists()) file.createNewFile() FileOutputStream(file).use { - currentSession?.securelyStoreObject(eventList, KEY_ALIAS_SECRET_STORAGE, it) + currentSession?.securelyStoreObject(queuedEvents, KEY_ALIAS_SECRET_STORAGE, it) } } catch (e: Throwable) { Timber.e(e, "## Failed to save cached notification info") @@ -645,15 +318,11 @@ class NotificationDrawerManager @Inject constructor(private val context: Context } } - fun displayDiagnosticNotification() { - notificationUtils.displayDiagnosticNotification() - } - companion object { - private const val SUMMARY_NOTIFICATION_ID = 0 - private const val ROOM_MESSAGES_NOTIFICATION_ID = 1 - private const val ROOM_EVENT_NOTIFICATION_ID = 2 - private const val ROOM_INVITATION_NOTIFICATION_ID = 3 + const val SUMMARY_NOTIFICATION_ID = 0 + const val ROOM_MESSAGES_NOTIFICATION_ID = 1 + const val ROOM_EVENT_NOTIFICATION_ID = 2 + const val ROOM_INVITATION_NOTIFICATION_ID = 3 // TODO Mutliaccount private const val ROOMS_NOTIFICATIONS_FILE_NAME = "im.vector.notifications.cache" @@ -661,3 +330,11 @@ class NotificationDrawerManager @Inject constructor(private val context: Context private const val KEY_ALIAS_SECRET_STORAGE = "notificationMgr" } } + +private fun MutableList.replace(eventId: String, block: (NotifiableEvent) -> NotifiableEvent) { + val indexToReplace = indexOfFirst { it.eventId == eventId } + if (indexToReplace == -1) { + return + } + set(indexToReplace, block(get(indexToReplace))) +} diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationFactory.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationFactory.kt new file mode 100644 index 0000000000..adc4e44bcc --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationFactory.kt @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.notifications + +import android.app.Notification +import androidx.core.content.pm.ShortcutInfoCompat +import javax.inject.Inject + +private typealias ProcessedMessageEvents = List> + +class NotificationFactory @Inject constructor( + private val notificationUtils: NotificationUtils, + private val roomGroupMessageCreator: RoomGroupMessageCreator, + private val summaryGroupMessageCreator: SummaryGroupMessageCreator +) { + + fun Map.toNotifications(myUserDisplayName: String, myUserAvatarUrl: String?): List { + return map { (roomId, events) -> + when { + events.hasNoEventsToDisplay() -> RoomNotification.Removed(roomId) + else -> { + val messageEvents = events.onlyKeptEvents().filterNot { it.isRedacted } + roomGroupMessageCreator.createRoomMessage(messageEvents, roomId, myUserDisplayName, myUserAvatarUrl) + } + } + } + } + + private fun ProcessedMessageEvents.hasNoEventsToDisplay() = isEmpty() || all { + it.type == ProcessedEvent.Type.REMOVE || it.event.canNotBeDisplayed() + } + + private fun NotifiableMessageEvent.canNotBeDisplayed() = isRedacted + + @JvmName("toNotificationsInviteNotifiableEvent") + fun List>.toNotifications(myUserId: String): List { + return map { (processed, event) -> + when (processed) { + ProcessedEvent.Type.REMOVE -> OneShotNotification.Removed(key = event.roomId) + ProcessedEvent.Type.KEEP -> OneShotNotification.Append( + notificationUtils.buildRoomInvitationNotification(event, myUserId), + OneShotNotification.Append.Meta( + key = event.roomId, + summaryLine = event.description, + isNoisy = event.noisy, + timestamp = event.timestamp + ) + ) + } + } + } + + @JvmName("toNotificationsSimpleNotifiableEvent") + fun List>.toNotifications(myUserId: String): List { + return map { (processed, event) -> + when (processed) { + ProcessedEvent.Type.REMOVE -> OneShotNotification.Removed(key = event.eventId) + ProcessedEvent.Type.KEEP -> OneShotNotification.Append( + notificationUtils.buildSimpleEventNotification(event, myUserId), + OneShotNotification.Append.Meta( + key = event.eventId, + summaryLine = event.description, + isNoisy = event.noisy, + timestamp = event.timestamp + ) + ) + } + } + } + + fun createSummaryNotification(roomNotifications: List, + invitationNotifications: List, + simpleNotifications: List, + useCompleteNotificationFormat: Boolean): SummaryNotification { + val roomMeta = roomNotifications.filterIsInstance().map { it.meta } + val invitationMeta = invitationNotifications.filterIsInstance().map { it.meta } + val simpleMeta = simpleNotifications.filterIsInstance().map { it.meta } + return when { + roomMeta.isEmpty() && invitationMeta.isEmpty() && simpleMeta.isEmpty() -> SummaryNotification.Removed + else -> SummaryNotification.Update( + summaryGroupMessageCreator.createSummaryNotification( + roomNotifications = roomMeta, + invitationNotifications = invitationMeta, + simpleNotifications = simpleMeta, + useCompleteNotificationFormat = useCompleteNotificationFormat + )) + } + } +} + +sealed interface RoomNotification { + data class Removed(val roomId: String) : RoomNotification + data class Message(val notification: Notification, val shortcutInfo: ShortcutInfoCompat?, val meta: Meta) : RoomNotification { + data class Meta( + val summaryLine: CharSequence, + val messageCount: Int, + val latestTimestamp: Long, + val roomId: String, + val shouldBing: Boolean + ) + } +} + +sealed interface OneShotNotification { + data class Removed(val key: String) : OneShotNotification + data class Append(val notification: Notification, val meta: Meta) : OneShotNotification { + data class Meta( + val key: String, + val summaryLine: CharSequence, + val isNoisy: Boolean, + val timestamp: Long, + ) + } +} + +sealed interface SummaryNotification { + object Removed : SummaryNotification + data class Update(val notification: Notification) : SummaryNotification +} diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationRenderer.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationRenderer.kt new file mode 100644 index 0000000000..5afff89402 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationRenderer.kt @@ -0,0 +1,135 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.app.features.notifications + +import android.content.Context +import androidx.annotation.WorkerThread +import androidx.core.content.pm.ShortcutManagerCompat +import im.vector.app.features.notifications.NotificationDrawerManager.Companion.ROOM_EVENT_NOTIFICATION_ID +import im.vector.app.features.notifications.NotificationDrawerManager.Companion.ROOM_INVITATION_NOTIFICATION_ID +import im.vector.app.features.notifications.NotificationDrawerManager.Companion.ROOM_MESSAGES_NOTIFICATION_ID +import im.vector.app.features.notifications.NotificationDrawerManager.Companion.SUMMARY_NOTIFICATION_ID +import timber.log.Timber +import javax.inject.Inject + +class NotificationRenderer @Inject constructor(private val notificationDisplayer: NotificationDisplayer, + private val notificationFactory: NotificationFactory, + private val appContext: Context) { + + @WorkerThread + fun render(myUserId: String, + myUserDisplayName: String, + myUserAvatarUrl: String?, + useCompleteNotificationFormat: Boolean, + eventsToProcess: List>) { + val (roomEvents, simpleEvents, invitationEvents) = eventsToProcess.groupByType() + with(notificationFactory) { + val roomNotifications = roomEvents.toNotifications(myUserDisplayName, myUserAvatarUrl) + val invitationNotifications = invitationEvents.toNotifications(myUserId) + val simpleNotifications = simpleEvents.toNotifications(myUserId) + val summaryNotification = createSummaryNotification( + roomNotifications = roomNotifications, + invitationNotifications = invitationNotifications, + simpleNotifications = simpleNotifications, + useCompleteNotificationFormat = useCompleteNotificationFormat + ) + + // Remove summary first to avoid briefly displaying it after dismissing the last notification + when (summaryNotification) { + SummaryNotification.Removed -> { + Timber.d("Removing summary notification") + notificationDisplayer.cancelNotificationMessage(null, SUMMARY_NOTIFICATION_ID) + } + } + + roomNotifications.forEach { wrapper -> + when (wrapper) { + is RoomNotification.Removed -> { + Timber.d("Removing room messages notification ${wrapper.roomId}") + notificationDisplayer.cancelNotificationMessage(wrapper.roomId, ROOM_MESSAGES_NOTIFICATION_ID) + } + is RoomNotification.Message -> if (useCompleteNotificationFormat) { + Timber.d("Updating room messages notification ${wrapper.meta.roomId}") + wrapper.shortcutInfo?.let { + ShortcutManagerCompat.pushDynamicShortcut(appContext, it) + } + notificationDisplayer.showNotificationMessage(wrapper.meta.roomId, ROOM_MESSAGES_NOTIFICATION_ID, wrapper.notification) + } + } + } + + invitationNotifications.forEach { wrapper -> + when (wrapper) { + is OneShotNotification.Removed -> { + Timber.d("Removing invitation notification ${wrapper.key}") + notificationDisplayer.cancelNotificationMessage(wrapper.key, ROOM_INVITATION_NOTIFICATION_ID) + } + is OneShotNotification.Append -> if (useCompleteNotificationFormat) { + Timber.d("Updating invitation notification ${wrapper.meta.key}") + notificationDisplayer.showNotificationMessage(wrapper.meta.key, ROOM_INVITATION_NOTIFICATION_ID, wrapper.notification) + } + } + } + + simpleNotifications.forEach { wrapper -> + when (wrapper) { + is OneShotNotification.Removed -> { + Timber.d("Removing simple notification ${wrapper.key}") + notificationDisplayer.cancelNotificationMessage(wrapper.key, ROOM_EVENT_NOTIFICATION_ID) + } + is OneShotNotification.Append -> if (useCompleteNotificationFormat) { + Timber.d("Updating simple notification ${wrapper.meta.key}") + notificationDisplayer.showNotificationMessage(wrapper.meta.key, ROOM_EVENT_NOTIFICATION_ID, wrapper.notification) + } + } + } + + // Update summary last to avoid briefly displaying it before other notifications + when (summaryNotification) { + is SummaryNotification.Update -> { + Timber.d("Updating summary notification") + notificationDisplayer.showNotificationMessage(null, SUMMARY_NOTIFICATION_ID, summaryNotification.notification) + } + } + } + } +} + +private fun List>.groupByType(): GroupedNotificationEvents { + val roomIdToEventMap: MutableMap>> = LinkedHashMap() + val simpleEvents: MutableList> = ArrayList() + val invitationEvents: MutableList> = ArrayList() + forEach { + when (val event = it.event) { + is InviteNotifiableEvent -> invitationEvents.add(it.castedToEventType()) + is NotifiableMessageEvent -> { + val roomEvents = roomIdToEventMap.getOrPut(event.roomId) { ArrayList() } + roomEvents.add(it.castedToEventType()) + } + is SimpleNotifiableEvent -> simpleEvents.add(it.castedToEventType()) + } + } + return GroupedNotificationEvents(roomIdToEventMap, simpleEvents, invitationEvents) +} + +@Suppress("UNCHECKED_CAST") +private fun ProcessedEvent.castedToEventType(): ProcessedEvent = this as ProcessedEvent + +data class GroupedNotificationEvents( + val roomEvents: Map>>, + val simpleEvents: List>, + val invitationEvents: List> +) diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt index f3b34e1269..491302a225 100755 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt @@ -642,7 +642,7 @@ class NotificationUtils @Inject constructor(private val context: Context, return NotificationCompat.Builder(context, channelID) .setOnlyAlertOnce(true) - .setContentTitle(stringProvider.getString(R.string.app_name)) + .setContentTitle(inviteNotifiableEvent.roomName ?: stringProvider.getString(R.string.app_name)) .setContentText(inviteNotifiableEvent.description) .setGroup(stringProvider.getString(R.string.app_name)) .setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL) diff --git a/vector/src/main/java/im/vector/app/features/notifications/ProcessedEvent.kt b/vector/src/main/java/im/vector/app/features/notifications/ProcessedEvent.kt new file mode 100644 index 0000000000..8bd9819ca9 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/notifications/ProcessedEvent.kt @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.notifications + +data class ProcessedEvent( + val type: Type, + val event: T +) { + + enum class Type { + KEEP, + REMOVE + } +} + +fun List>.onlyKeptEvents() = mapNotNull { processedEvent -> + processedEvent.event.takeIf { processedEvent.type == ProcessedEvent.Type.KEEP } +} diff --git a/vector/src/main/java/im/vector/app/features/notifications/PushRuleTriggerListener.kt b/vector/src/main/java/im/vector/app/features/notifications/PushRuleTriggerListener.kt index 791803fa49..abbbd47f95 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/PushRuleTriggerListener.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/PushRuleTriggerListener.kt @@ -40,12 +40,11 @@ class PushRuleTriggerListener @Inject constructor( val notificationAction = actions.toNotificationAction() if (notificationAction.shouldNotify) { - val notifiableEvent = resolver.resolveEvent(event, safeSession) + val notifiableEvent = resolver.resolveEvent(event, safeSession, isNoisy = !notificationAction.soundName.isNullOrBlank()) if (notifiableEvent == null) { Timber.v("## Failed to resolve event") // TODO } else { - notifiableEvent.noisy = !notificationAction.soundName.isNullOrBlank() Timber.v("New event to notify") notificationDrawerManager.onNotifiableEventReceived(notifiableEvent) } diff --git a/vector/src/main/java/im/vector/app/features/notifications/RoomGroupMessageCreator.kt b/vector/src/main/java/im/vector/app/features/notifications/RoomGroupMessageCreator.kt new file mode 100644 index 0000000000..bdd7d026f9 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/notifications/RoomGroupMessageCreator.kt @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.notifications + +import android.content.Context +import android.graphics.Bitmap +import android.os.Build +import androidx.core.app.NotificationCompat +import androidx.core.app.Person +import androidx.core.content.pm.ShortcutInfoCompat +import androidx.core.graphics.drawable.IconCompat +import im.vector.app.R +import im.vector.app.core.resources.StringProvider +import im.vector.app.features.home.room.detail.RoomDetailActivity +import me.gujun.android.span.Span +import me.gujun.android.span.span +import timber.log.Timber +import javax.inject.Inject + +class RoomGroupMessageCreator @Inject constructor( + private val iconLoader: IconLoader, + private val bitmapLoader: BitmapLoader, + private val stringProvider: StringProvider, + private val notificationUtils: NotificationUtils, + private val appContext: Context +) { + + fun createRoomMessage(events: List, roomId: String, userDisplayName: String, userAvatarUrl: String?): RoomNotification.Message { + val firstKnownRoomEvent = events[0] + val roomName = firstKnownRoomEvent.roomName ?: firstKnownRoomEvent.senderName ?: "" + val roomIsGroup = !firstKnownRoomEvent.roomIsDirect + val style = NotificationCompat.MessagingStyle(Person.Builder() + .setName(userDisplayName) + .setIcon(iconLoader.getUserIcon(userAvatarUrl)) + .setKey(firstKnownRoomEvent.matrixID) + .build() + ).also { + it.conversationTitle = roomName.takeIf { roomIsGroup } + it.isGroupConversation = roomIsGroup + it.addMessagesFromEvents(events) + } + + val tickerText = if (roomIsGroup) { + stringProvider.getString(R.string.notification_ticker_text_group, roomName, events.last().senderName, events.last().description) + } else { + stringProvider.getString(R.string.notification_ticker_text_dm, events.last().senderName, events.last().description) + } + + val largeBitmap = getRoomBitmap(events) + val shortcutInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + val openRoomIntent = RoomDetailActivity.shortcutIntent(appContext, roomId) + ShortcutInfoCompat.Builder(appContext, roomId) + .setLongLived(true) + .setIntent(openRoomIntent) + .setShortLabel(roomName) + .setIcon(largeBitmap?.let { IconCompat.createWithAdaptiveBitmap(it) } ?: iconLoader.getUserIcon(events.last().senderAvatarPath)) + .build() + } else { + null + } + + val lastMessageTimestamp = events.last().timestamp + val smartReplyErrors = events.filter { it.isSmartReplyError() } + val messageCount = (events.size - smartReplyErrors.size) + val meta = RoomNotification.Message.Meta( + summaryLine = createRoomMessagesGroupSummaryLine(events, roomName, roomIsDirect = !roomIsGroup), + messageCount = messageCount, + latestTimestamp = lastMessageTimestamp, + roomId = roomId, + shouldBing = events.any { it.noisy } + ) + return RoomNotification.Message( + notificationUtils.buildMessagesListNotification( + style, + RoomEventGroupInfo(roomId, roomName, isDirect = !roomIsGroup).also { + it.hasSmartReplyError = smartReplyErrors.isNotEmpty() + it.shouldBing = meta.shouldBing + it.customSound = events.last().soundName + }, + largeIcon = largeBitmap, + lastMessageTimestamp, + userDisplayName, + tickerText + ), + shortcutInfo, + meta + ) + } + + private fun NotificationCompat.MessagingStyle.addMessagesFromEvents(events: List) { + events.forEach { event -> + val senderPerson = if (event.outGoingMessage) { + null + } else { + Person.Builder() + .setName(event.senderName) + .setIcon(iconLoader.getUserIcon(event.senderAvatarPath)) + .setKey(event.senderId) + .build() + } + when { + event.isSmartReplyError() -> addMessage(stringProvider.getString(R.string.notification_inline_reply_failed), event.timestamp, senderPerson) + else -> addMessage(event.body, event.timestamp, senderPerson) + } + } + } + + private fun createRoomMessagesGroupSummaryLine(events: List, roomName: String, roomIsDirect: Boolean): CharSequence { + return try { + when (events.size) { + 1 -> createFirstMessageSummaryLine(events.first(), roomName, roomIsDirect) + else -> { + stringProvider.getQuantityString( + R.plurals.notification_compat_summary_line_for_room, + events.size, + roomName, + events.size + ) + } + } + } catch (e: Throwable) { + // String not found or bad format + Timber.v("%%%%%%%% REFRESH NOTIFICATION DRAWER failed to resolve string") + roomName + } + } + + private fun createFirstMessageSummaryLine(event: NotifiableMessageEvent, roomName: String, roomIsDirect: Boolean): Span { + return if (roomIsDirect) { + span { + span { + textStyle = "bold" + +String.format("%s: ", event.senderName) + } + +(event.description) + } + } else { + span { + span { + textStyle = "bold" + +String.format("%s: %s ", roomName, event.senderName) + } + +(event.description) + } + } + } + + private fun getRoomBitmap(events: List): Bitmap? { + // Use the last event (most recent?) + return events.lastOrNull() + ?.roomAvatarPath + ?.let { bitmapLoader.getRoomBitmap(it) } + } +} + +private fun NotifiableMessageEvent.isSmartReplyError() = outGoingMessage && outGoingMessageFailed diff --git a/vector/src/main/java/im/vector/app/features/notifications/SimpleNotifiableEvent.kt b/vector/src/main/java/im/vector/app/features/notifications/SimpleNotifiableEvent.kt index 2f74737ba2..8c72372204 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/SimpleNotifiableEvent.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/SimpleNotifiableEvent.kt @@ -15,21 +15,16 @@ */ package im.vector.app.features.notifications -import androidx.core.app.NotificationCompat - data class SimpleNotifiableEvent( - override var matrixID: String?, + val matrixID: String?, override val eventId: String, override val editedEventId: String?, - override var noisy: Boolean, - override val title: String, - override val description: String, - override val type: String?, - override val timestamp: Long, - override var soundName: String?, - override var isPushGatewayEvent: Boolean = false) : NotifiableEvent { - - override var hasBeenDisplayed: Boolean = false - override var isRedacted: Boolean = false - override var lockScreenVisibility = NotificationCompat.VISIBILITY_PUBLIC -} + val noisy: Boolean, + val title: String, + val description: String, + val type: String?, + val timestamp: Long, + val soundName: String?, + override var canBeReplaced: Boolean, + override val isRedacted: Boolean = false +) : NotifiableEvent diff --git a/vector/src/main/java/im/vector/app/features/notifications/SummaryGroupMessageCreator.kt b/vector/src/main/java/im/vector/app/features/notifications/SummaryGroupMessageCreator.kt new file mode 100644 index 0000000000..91163434c2 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/notifications/SummaryGroupMessageCreator.kt @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.notifications + +import android.app.Notification +import androidx.core.app.NotificationCompat +import im.vector.app.R +import im.vector.app.core.resources.StringProvider +import javax.inject.Inject + +/** + * ======== Build summary notification ========= + * On Android 7.0 (API level 24) and higher, the system automatically builds a summary for + * your group using snippets of text from each notification. The user can expand this + * notification to see each separate notification. + * To support older versions, which cannot show a nested group of notifications, + * you must create an extra notification that acts as the summary. + * This appears as the only notification and the system hides all the others. + * So this summary should include a snippet from all the other notifications, + * which the user can tap to open your app. + * The behavior of the group summary may vary on some device types such as wearables. + * To ensure the best experience on all devices and versions, always include a group summary when you create a group + * https://developer.android.com/training/notify-user/group + */ +class SummaryGroupMessageCreator @Inject constructor( + private val stringProvider: StringProvider, + private val notificationUtils: NotificationUtils +) { + + fun createSummaryNotification(roomNotifications: List, + invitationNotifications: List, + simpleNotifications: List, + useCompleteNotificationFormat: Boolean): Notification { + val summaryInboxStyle = NotificationCompat.InboxStyle().also { style -> + roomNotifications.forEach { style.addLine(it.summaryLine) } + invitationNotifications.forEach { style.addLine(it.summaryLine) } + simpleNotifications.forEach { style.addLine(it.summaryLine) } + } + + val summaryIsNoisy = roomNotifications.any { it.shouldBing } || + invitationNotifications.any { it.isNoisy } || + simpleNotifications.any { it.isNoisy } + + val messageCount = roomNotifications.fold(initial = 0) { acc, current -> acc + current.messageCount } + + val lastMessageTimestamp = roomNotifications.lastOrNull()?.latestTimestamp + ?: invitationNotifications.lastOrNull()?.timestamp + ?: simpleNotifications.last().timestamp + + // FIXME roomIdToEventMap.size is not correct, this is the number of rooms + val nbEvents = roomNotifications.size + simpleNotifications.size + val sumTitle = stringProvider.getQuantityString(R.plurals.notification_compat_summary_title, nbEvents, nbEvents) + summaryInboxStyle.setBigContentTitle(sumTitle) + // TODO get latest event? + .setSummaryText(stringProvider.getQuantityString(R.plurals.notification_unread_notified_messages, nbEvents, nbEvents)) + return if (useCompleteNotificationFormat) { + notificationUtils.buildSummaryListNotification( + summaryInboxStyle, + sumTitle, + noisy = summaryIsNoisy, + lastMessageTimestamp = lastMessageTimestamp + ) + } else { + processSimpleGroupSummary( + summaryIsNoisy, + messageCount, + simpleNotifications.size, + invitationNotifications.size, + roomNotifications.size, + lastMessageTimestamp + ) + } + } + + private fun processSimpleGroupSummary(summaryIsNoisy: Boolean, + messageEventsCount: Int, + simpleEventsCount: Int, + invitationEventsCount: Int, + roomCount: Int, + lastMessageTimestamp: Long): Notification { + // Add the simple events as message (?) + val messageNotificationCount = messageEventsCount + simpleEventsCount + + val privacyTitle = if (invitationEventsCount > 0) { + val invitationsStr = stringProvider.getQuantityString(R.plurals.notification_invitations, invitationEventsCount, invitationEventsCount) + if (messageNotificationCount > 0) { + // Invitation and message + val messageStr = stringProvider.getQuantityString(R.plurals.room_new_messages_notification, + messageNotificationCount, messageNotificationCount) + if (roomCount > 1) { + // In several rooms + val roomStr = stringProvider.getQuantityString(R.plurals.notification_unread_notified_messages_in_room_rooms, + roomCount, roomCount) + stringProvider.getString( + R.string.notification_unread_notified_messages_in_room_and_invitation, + messageStr, + roomStr, + invitationsStr + ) + } else { + // In one room + stringProvider.getString( + R.string.notification_unread_notified_messages_and_invitation, + messageStr, + invitationsStr + ) + } + } else { + // Only invitation + invitationsStr + } + } else { + // No invitation, only messages + val messageStr = stringProvider.getQuantityString(R.plurals.room_new_messages_notification, + messageNotificationCount, messageNotificationCount) + if (roomCount > 1) { + // In several rooms + val roomStr = stringProvider.getQuantityString(R.plurals.notification_unread_notified_messages_in_room_rooms, roomCount, roomCount) + stringProvider.getString(R.string.notification_unread_notified_messages_in_room, messageStr, roomStr) + } else { + // In one room + messageStr + } + } + return notificationUtils.buildSummaryListNotification( + style = null, + compatSummary = privacyTitle, + noisy = summaryIsNoisy, + lastMessageTimestamp = lastMessageTimestamp + ) + } +} diff --git a/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt b/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt index 90bcee3d04..40cc0b3e13 100644 --- a/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt +++ b/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt @@ -18,6 +18,7 @@ package im.vector.app.features.permalink import android.content.Context import android.net.Uri +import androidx.core.net.toUri import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.extensions.isIgnored @@ -79,15 +80,14 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti return when (permalinkData) { is PermalinkData.RoomLink -> { val roomId = permalinkData.getRoomId() - if (navigationInterceptor?.navToRoom(roomId, permalinkData.eventId, rawLink) != true) { - openRoom( - context = context, - roomId = roomId, - permalinkData = permalinkData, - rawLink = rawLink, - buildTask = buildTask - ) - } + openRoom( + navigationInterceptor, + context = context, + roomId = roomId, + permalinkData = permalinkData, + rawLink = rawLink, + buildTask = buildTask + ) true } is PermalinkData.GroupLink -> { @@ -119,9 +119,8 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti private fun isPermalinkSupported(context: Context, url: String): Boolean { return url.startsWith(PermalinkService.MATRIX_TO_URL_BASE) || - context.resources.getStringArray(R.array.permalink_supported_hosts).any { - url.startsWith(it) - } + context.resources.getStringArray(R.array.permalink_supported_hosts) + .any { url.toUri().host == it } } private suspend fun PermalinkData.RoomLink.getRoomId(): String? { @@ -146,6 +145,7 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti * Open room either joined, or not */ private fun openRoom( + navigationInterceptor: NavigationInterceptor?, context: Context, roomId: String?, permalinkData: PermalinkData.RoomLink, @@ -167,7 +167,7 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti membership?.isActive().orFalse() -> { if (!isSpace && membership == Membership.JOIN) { // If it's a room you're in, let's just open it, you can tap back if needed - navigator.openRoom(context, roomId, eventId, buildTask) + navigationInterceptor.openJoinedRoomScreen(buildTask, roomId, eventId, rawLink, context) } else { // maybe open space preview navigator.openSpacePreview(context, roomId)? if already joined? navigator.openMatrixToBottomSheet(context, rawLink.toString()) @@ -180,6 +180,12 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti } } + private fun NavigationInterceptor?.openJoinedRoomScreen(buildTask: Boolean, roomId: String, eventId: String?, rawLink: Uri, context: Context) { + if (this?.navToRoom(roomId, eventId, rawLink) != true) { + navigator.openRoom(context, roomId, eventId, buildTask) + } + } + companion object { const val MATRIX_TO_CUSTOM_SCHEME_URL_BASE = "element://" const val ROOM_LINK_PREFIX = "${MATRIX_TO_CUSTOM_SCHEME_URL_BASE}room/" diff --git a/vector/src/main/java/im/vector/app/features/pin/PinActivity.kt b/vector/src/main/java/im/vector/app/features/pin/PinActivity.kt index 430be6bc1f..0978f0d5b5 100644 --- a/vector/src/main/java/im/vector/app/features/pin/PinActivity.kt +++ b/vector/src/main/java/im/vector/app/features/pin/PinActivity.kt @@ -20,12 +20,14 @@ import android.content.Context import android.content.Intent import com.airbnb.mvrx.Mavericks import com.google.android.material.appbar.MaterialToolbar +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R import im.vector.app.core.extensions.addFragment import im.vector.app.core.platform.ToolbarConfigurable import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivitySimpleBinding +@AndroidEntryPoint class PinActivity : VectorBaseActivity(), ToolbarConfigurable, UnlockedActivity { companion object { diff --git a/vector/src/main/java/im/vector/app/features/pin/PinCodeStore.kt b/vector/src/main/java/im/vector/app/features/pin/PinCodeStore.kt index fb7c6897e2..d3632edbca 100644 --- a/vector/src/main/java/im/vector/app/features/pin/PinCodeStore.kt +++ b/vector/src/main/java/im/vector/app/features/pin/PinCodeStore.kt @@ -25,6 +25,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.extensions.orFalse import javax.inject.Inject +import javax.inject.Singleton import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine @@ -56,26 +57,40 @@ interface PinCodeStore { * Will reset the counters */ fun resetCounters() + + fun addListener(listener: PinCodeStoreListener) + fun removeListener(listener: PinCodeStoreListener) } -class SharedPrefPinCodeStore @Inject constructor(private val sharedPreferences: SharedPreferences) : PinCodeStore { +interface PinCodeStoreListener { + fun onPinSetUpChange(isConfigured: Boolean) +} - override suspend fun storeEncodedPin(encodePin: String) = withContext(Dispatchers.IO) { - sharedPreferences.edit { - putString(ENCODED_PIN_CODE_KEY, encodePin) +@Singleton +class SharedPrefPinCodeStore @Inject constructor(private val sharedPreferences: SharedPreferences) : PinCodeStore { + private val listeners = mutableSetOf() + + override suspend fun storeEncodedPin(encodePin: String) { + withContext(Dispatchers.IO) { + sharedPreferences.edit { + putString(ENCODED_PIN_CODE_KEY, encodePin) + } } + listeners.forEach { it.onPinSetUpChange(isConfigured = true) } } - override suspend fun deleteEncodedPin() = withContext(Dispatchers.IO) { - // Also reset the counters - resetCounters() - sharedPreferences.edit { - remove(ENCODED_PIN_CODE_KEY) + override suspend fun deleteEncodedPin() { + withContext(Dispatchers.IO) { + // Also reset the counters + resetCounters() + sharedPreferences.edit { + remove(ENCODED_PIN_CODE_KEY) + } + awaitPinCodeCallback { + PFSecurityManager.getInstance().pinCodeHelper.delete(it) + } } - awaitPinCodeCallback { - PFSecurityManager.getInstance().pinCodeHelper.delete(it) - } - return@withContext + listeners.forEach { it.onPinSetUpChange(isConfigured = false) } } override fun getEncodedPin(): String? { @@ -124,6 +139,14 @@ class SharedPrefPinCodeStore @Inject constructor(private val sharedPreferences: } } + override fun addListener(listener: PinCodeStoreListener) { + listeners.add(listener) + } + + override fun removeListener(listener: PinCodeStoreListener) { + listeners.remove(listener) + } + private suspend inline fun awaitPinCodeCallback(crossinline callback: (PFPinCodeHelperCallback) -> Unit) = suspendCoroutine> { cont -> callback(PFPinCodeHelperCallback { result -> cont.resume(result) }) } diff --git a/vector/src/main/java/im/vector/app/features/qrcode/QrCodeScannerActivity.kt b/vector/src/main/java/im/vector/app/features/qrcode/QrCodeScannerActivity.kt index 13f989994a..7fb2f1f254 100644 --- a/vector/src/main/java/im/vector/app/features/qrcode/QrCodeScannerActivity.kt +++ b/vector/src/main/java/im/vector/app/features/qrcode/QrCodeScannerActivity.kt @@ -23,22 +23,19 @@ import androidx.activity.result.ActivityResultLauncher import com.google.zxing.BarcodeFormat import com.google.zxing.Result import com.google.zxing.ResultMetadataType +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.replaceFragment import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivitySimpleBinding +@AndroidEntryPoint class QrCodeScannerActivity : VectorBaseActivity() { override fun getBinding() = ActivitySimpleBinding.inflate(layoutInflater) override fun getCoordinatorLayout() = views.coordinatorLayout - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if (isFirstCreation()) { diff --git a/vector/src/main/java/im/vector/app/features/rageshake/BugReportActivity.kt b/vector/src/main/java/im/vector/app/features/rageshake/BugReportActivity.kt index 89aa307dc4..b27c2e9818 100755 --- a/vector/src/main/java/im/vector/app/features/rageshake/BugReportActivity.kt +++ b/vector/src/main/java/im/vector/app/features/rageshake/BugReportActivity.kt @@ -25,27 +25,21 @@ import androidx.core.view.isVisible import androidx.core.widget.doOnTextChanged import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.withState +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivityBugReportBinding import org.matrix.android.sdk.api.extensions.tryOrNull import timber.log.Timber -import javax.inject.Inject /** * Form to send a bug report */ +@AndroidEntryPoint class BugReportActivity : VectorBaseActivity() { - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - override fun getBinding() = ActivityBugReportBinding.inflate(layoutInflater) - @Inject lateinit var bugReportViewModelFactory: BugReportViewModel.Factory - private val viewModel: BugReportViewModel by viewModel() private var reportType: ReportType = ReportType.BUG_REPORT diff --git a/vector/src/main/java/im/vector/app/features/rageshake/BugReportViewModel.kt b/vector/src/main/java/im/vector/app/features/rageshake/BugReportViewModel.kt index a1ec6b2e76..d0a1280868 100644 --- a/vector/src/main/java/im/vector/app/features/rageshake/BugReportViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/rageshake/BugReportViewModel.kt @@ -16,14 +16,13 @@ package im.vector.app.features.rageshake -import androidx.lifecycle.viewModelScope -import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory -import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject 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.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel @@ -36,18 +35,11 @@ class BugReportViewModel @AssistedInject constructor( ) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: BugReportState): BugReportViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: BugReportState): BugReportViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: BugReportState): BugReportViewModel? { - val activity: BugReportActivity = (viewModelContext as ActivityViewModelContext).activity() - return activity.bugReportViewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() init { fetchHomeserverVersion() diff --git a/vector/src/main/java/im/vector/app/features/rageshake/RageShake.kt b/vector/src/main/java/im/vector/app/features/rageshake/RageShake.kt index 48a6e8a679..93c72ea69e 100644 --- a/vector/src/main/java/im/vector/app/features/rageshake/RageShake.kt +++ b/vector/src/main/java/im/vector/app/features/rageshake/RageShake.kt @@ -19,8 +19,8 @@ package im.vector.app.features.rageshake import android.content.Context import android.hardware.Sensor import android.hardware.SensorManager -import androidx.appcompat.app.AppCompatActivity import androidx.core.content.getSystemService +import androidx.fragment.app.FragmentActivity import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.squareup.seismic.ShakeDetector import im.vector.app.R @@ -30,7 +30,7 @@ import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorSettingsActivity import javax.inject.Inject -class RageShake @Inject constructor(private val activity: AppCompatActivity, +class RageShake @Inject constructor(private val activity: FragmentActivity, private val bugReporter: BugReporter, private val navigator: Navigator, private val vectorPreferences: VectorPreferences) : ShakeDetector.Listener { diff --git a/vector/src/main/java/im/vector/app/features/reactions/EmojiReactionPickerActivity.kt b/vector/src/main/java/im/vector/app/features/reactions/EmojiReactionPickerActivity.kt index e371aae096..5675af6960 100644 --- a/vector/src/main/java/im/vector/app/features/reactions/EmojiReactionPickerActivity.kt +++ b/vector/src/main/java/im/vector/app/features/reactions/EmojiReactionPickerActivity.kt @@ -29,9 +29,9 @@ import androidx.lifecycle.lifecycleScope import com.airbnb.mvrx.viewModel import com.google.android.material.tabs.TabLayout import com.jakewharton.rxbinding3.widget.queryTextChanges +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.EmojiCompatFontProvider import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.observeEvent import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivityEmojiReactionPickerBinding @@ -47,6 +47,7 @@ import javax.inject.Inject * TODO: Loading indicator while getting emoji data source? * TODO: Finish Refactor to vector base activity */ +@AndroidEntryPoint class EmojiReactionPickerActivity : VectorBaseActivity(), EmojiCompatFontProvider.FontProviderListener { @@ -60,7 +61,6 @@ class EmojiReactionPickerActivity : VectorBaseActivity(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: EmojiSearchResultViewState): EmojiSearchResultViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: EmojiSearchResultViewState): EmojiSearchResultViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: EmojiSearchResultViewState): EmojiSearchResultViewModel? { - val activity: EmojiReactionPickerActivity = (viewModelContext as ActivityViewModelContext).activity() - return activity.emojiSearchResultViewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() override fun handle(action: EmojiSearchAction) { when (action) { diff --git a/vector/src/main/java/im/vector/app/features/reactions/widget/ReactionButton.kt b/vector/src/main/java/im/vector/app/features/reactions/widget/ReactionButton.kt index c342d6519d..2b4e9ee5ab 100644 --- a/vector/src/main/java/im/vector/app/features/reactions/widget/ReactionButton.kt +++ b/vector/src/main/java/im/vector/app/features/reactions/widget/ReactionButton.kt @@ -23,9 +23,9 @@ import android.view.View import android.widget.LinearLayout import androidx.core.content.ContextCompat import androidx.core.content.withStyledAttributes +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.EmojiCompatWrapper import im.vector.app.R -import im.vector.app.core.di.HasScreenInjector import im.vector.app.core.utils.DimensionConverter import im.vector.app.core.utils.TextUtils import im.vector.app.databinding.ReactionButtonBinding @@ -35,17 +35,12 @@ import javax.inject.Inject * An animated reaction button. * Displays a String reaction (emoji), with a count, and that can be selected or not (toggle) */ +@AndroidEntryPoint class ReactionButton @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : LinearLayout(context, attrs, defStyleAttr), View.OnClickListener, View.OnLongClickListener { - init { - if (context is HasScreenInjector) { - context.injector().inject(this) - } - } - @Inject lateinit var emojiCompatWrapper: EmojiCompatWrapper private val views: ReactionButtonBinding diff --git a/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt b/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt index 44a6963a5d..6ad93abe0c 100644 --- a/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt @@ -16,14 +16,13 @@ package im.vector.app.features.room -import com.airbnb.mvrx.ActivityViewModelContext -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory -import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.R +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider @@ -55,21 +54,11 @@ class RequireActiveMembershipViewModel @AssistedInject constructor( VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: RequireActiveMembershipViewState): RequireActiveMembershipViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: RequireActiveMembershipViewState): RequireActiveMembershipViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: RequireActiveMembershipViewState): RequireActiveMembershipViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() private val roomIdFlow = MutableStateFlow(Optional.from(initialState.roomId)) diff --git a/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewState.kt b/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewState.kt index dbf399bdf2..7a5363100f 100644 --- a/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewState.kt +++ b/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewState.kt @@ -17,6 +17,7 @@ package im.vector.app.features.room import com.airbnb.mvrx.MavericksState +import im.vector.app.features.home.room.detail.RoomDetailArgs import im.vector.app.features.roommemberprofile.RoomMemberProfileArgs import im.vector.app.features.roomprofile.RoomProfileArgs @@ -24,7 +25,7 @@ data class RequireActiveMembershipViewState( val roomId: String? = null ) : MavericksState { - // No constructor for RoomDetailArgs because of intent for Shortcut + constructor(args: RoomDetailArgs) : this(roomId = args.roomId) constructor(args: RoomProfileArgs) : this(roomId = args.roomId) diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryActivity.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryActivity.kt index 02bae82e03..7ad8d0ce49 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryActivity.kt @@ -21,19 +21,22 @@ import android.content.Intent import android.os.Bundle import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.withState +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.addFragment import im.vector.app.core.extensions.addFragmentToBackstack import im.vector.app.core.extensions.popBackstack import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivitySimpleBinding +import im.vector.app.features.matrixto.MatrixToBottomSheet +import im.vector.app.features.navigation.Navigator import im.vector.app.features.roomdirectory.createroom.CreateRoomArgs import im.vector.app.features.roomdirectory.createroom.CreateRoomFragment import im.vector.app.features.roomdirectory.picker.RoomDirectoryPickerFragment import javax.inject.Inject -class RoomDirectoryActivity : VectorBaseActivity() { +@AndroidEntryPoint +class RoomDirectoryActivity : VectorBaseActivity(), MatrixToBottomSheet.InteractionListener { @Inject lateinit var roomDirectoryViewModelFactory: RoomDirectoryViewModel.Factory private val roomDirectoryViewModel: RoomDirectoryViewModel by viewModel() @@ -43,10 +46,6 @@ class RoomDirectoryActivity : VectorBaseActivity() { override fun getCoordinatorLayout() = views.coordinatorLayout - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) sharedActionViewModel = viewModelProvider.get(RoomDirectorySharedActionViewModel::class.java) @@ -84,6 +83,14 @@ class RoomDirectoryActivity : VectorBaseActivity() { } } + override fun mxToBottomSheetNavigateToRoom(roomId: String) { + navigator.openRoom(this, roomId) + } + + override fun mxToBottomSheetSwitchToSpace(spaceId: String) { + navigator.switchToSpace(this, spaceId, Navigator.PostSwitchSpaceAction.None) + } + companion object { private const val INITIAL_FILTER = "INITIAL_FILTER" diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt index a2089e6cd5..844266e668 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryViewModel.kt @@ -16,16 +16,16 @@ package im.vector.app.features.roomdirectory -import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success -import com.airbnb.mvrx.ViewModelContext import com.airbnb.mvrx.appendAt 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.VectorViewModel import im.vector.app.features.settings.VectorPreferences import kotlinx.coroutines.CancellationException @@ -49,18 +49,12 @@ class RoomDirectoryViewModel @AssistedInject constructor( ) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: PublicRoomsViewState): RoomDirectoryViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: PublicRoomsViewState): RoomDirectoryViewModel } - companion object : MavericksViewModelFactory { + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() { private const val PUBLIC_ROOMS_LIMIT = 20 - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: PublicRoomsViewState): RoomDirectoryViewModel? { - val activity: RoomDirectoryActivity = (viewModelContext as ActivityViewModelContext).activity() - return activity.roomDirectoryViewModelFactory.create(state) - } } private val showAllRooms = vectorPreferences.showAllPublicRooms() diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomActivity.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomActivity.kt index 4afda8a0e9..eeb7d217c0 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomActivity.kt @@ -20,8 +20,8 @@ import android.content.Context import android.content.Intent import android.os.Bundle import com.google.android.material.appbar.MaterialToolbar +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.addFragment import im.vector.app.core.platform.ToolbarConfigurable import im.vector.app.core.platform.VectorBaseActivity @@ -32,6 +32,7 @@ import im.vector.app.features.roomdirectory.RoomDirectorySharedActionViewModel /** * Simple container for [CreateRoomFragment] */ +@AndroidEntryPoint class CreateRoomActivity : VectorBaseActivity(), ToolbarConfigurable { private lateinit var sharedActionViewModel: RoomDirectorySharedActionViewModel @@ -44,10 +45,6 @@ class CreateRoomActivity : VectorBaseActivity(), ToolbarC configureToolbar(toolbar) } - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - override fun initUiAndData() { if (isFirstCreation()) { addFragment( diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomFragment.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomFragment.kt index ac4c9db89f..c61da211a4 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomFragment.kt @@ -59,7 +59,6 @@ data class CreateRoomArgs( class CreateRoomFragment @Inject constructor( private val createRoomController: CreateRoomController, private val createSpaceController: CreateSubSpaceController, - val createRoomViewModelFactory: CreateRoomViewModel.Factory, colorProvider: ColorProvider ) : VectorBaseFragment(), CreateRoomController.Listener, diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt index e0a542dd68..e0ffdc7a52 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt @@ -17,17 +17,16 @@ package im.vector.app.features.roomdirectory.createroom import androidx.core.net.toFile -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext 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.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.raw.wellknown.getElementWellknown @@ -60,10 +59,12 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted private val init ) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: CreateRoomViewState): CreateRoomViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: CreateRoomViewState): CreateRoomViewModel } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() + init { initHomeServerName() initAdminE2eByDefault() @@ -122,16 +123,6 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted private val init } } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: CreateRoomViewState): CreateRoomViewModel? { - val fragment: CreateRoomFragment = (viewModelContext as FragmentViewModelContext).fragment() - - return fragment.createRoomViewModelFactory.create(state) - } - } - override fun handle(action: CreateRoomAction) { when (action) { is CreateRoomAction.SetAvatar -> setAvatar(action) diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt index a32a3a897f..2707b87c1f 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerFragment.kt @@ -39,8 +39,7 @@ import im.vector.app.features.roomdirectory.RoomDirectoryViewModel import timber.log.Timber import javax.inject.Inject -class RoomDirectoryPickerFragment @Inject constructor(val roomDirectoryPickerViewModelFactory: RoomDirectoryPickerViewModel.Factory, - private val roomDirectoryPickerController: RoomDirectoryPickerController +class RoomDirectoryPickerFragment @Inject constructor(private val roomDirectoryPickerController: RoomDirectoryPickerController ) : VectorBaseFragment(), OnBackPressed, RoomDirectoryPickerController.Callback { diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerViewModel.kt index 3f73b80bc6..a5673e78a2 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerViewModel.kt @@ -16,18 +16,17 @@ package im.vector.app.features.roomdirectory.picker -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.R +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel @@ -46,18 +45,11 @@ class RoomDirectoryPickerViewModel @AssistedInject constructor( ) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: RoomDirectoryPickerViewState): RoomDirectoryPickerViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: RoomDirectoryPickerViewState): RoomDirectoryPickerViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: RoomDirectoryPickerViewState): RoomDirectoryPickerViewModel? { - val fragment: RoomDirectoryPickerFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.roomDirectoryPickerViewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() init { observeAndCompute() diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt index 0121d5d795..86bfdb79cd 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt @@ -20,6 +20,7 @@ import android.content.Context import android.content.Intent import android.os.Parcelable import com.google.android.material.appbar.MaterialToolbar +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R import im.vector.app.core.extensions.addFragment import im.vector.app.core.platform.ToolbarConfigurable @@ -51,6 +52,7 @@ data class RoomPreviewData( get() = MatrixItem.RoomItem(roomId, roomName ?: roomAlias, avatarUrl) } +@AndroidEntryPoint class RoomPreviewActivity : VectorBaseActivity(), ToolbarConfigurable { companion object { diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt index ef70a31a00..52617e2f1d 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewNoPreviewFragment.kt @@ -51,7 +51,6 @@ import javax.inject.Inject * Note: this Fragment is also used for world readable room for the moment */ class RoomPreviewNoPreviewFragment @Inject constructor( - val roomPreviewViewModelFactory: RoomPreviewViewModel.Factory, private val avatarRenderer: AvatarRenderer ) : VectorBaseFragment() { diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt index 1df070d3d9..7b012f4fac 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt @@ -16,15 +16,14 @@ package im.vector.app.features.roomdirectory.roompreview -import androidx.lifecycle.viewModelScope -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success -import com.airbnb.mvrx.ViewModelContext 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.extensions.exhaustive import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel @@ -50,18 +49,11 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted private val ini VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: RoomPreviewViewState): RoomPreviewViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: RoomPreviewViewState): RoomPreviewViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: RoomPreviewViewState): RoomPreviewViewModel? { - val fragment: RoomPreviewNoPreviewFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.roomPreviewViewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() init { // Observe joined room (from the sync) diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileActivity.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileActivity.kt index 8c166e7715..c563f6b855 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileActivity.kt @@ -23,21 +23,19 @@ import android.widget.Toast import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import com.google.android.material.appbar.MaterialToolbar +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.addFragment import im.vector.app.core.platform.ToolbarConfigurable import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivitySimpleBinding import im.vector.app.features.room.RequireActiveMembershipViewEvents import im.vector.app.features.room.RequireActiveMembershipViewModel -import im.vector.app.features.room.RequireActiveMembershipViewState -import javax.inject.Inject +@AndroidEntryPoint class RoomMemberProfileActivity : VectorBaseActivity(), - ToolbarConfigurable, - RequireActiveMembershipViewModel.Factory { + ToolbarConfigurable { companion object { fun newIntent(context: Context, args: RoomMemberProfileArgs): Intent { @@ -49,17 +47,6 @@ class RoomMemberProfileActivity : private val requireActiveMembershipViewModel: RequireActiveMembershipViewModel by viewModel() - @Inject - lateinit var requireActiveMembershipViewModelFactory: RequireActiveMembershipViewModel.Factory - - override fun create(initialState: RequireActiveMembershipViewState): RequireActiveMembershipViewModel { - return requireActiveMembershipViewModelFactory.create(initialState) - } - - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - override fun getBinding(): ActivitySimpleBinding { return ActivitySimpleBinding.inflate(layoutInflater) } diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt index d8e967fd27..48823714f5 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -66,7 +66,6 @@ data class RoomMemberProfileArgs( ) : Parcelable class RoomMemberProfileFragment @Inject constructor( - val viewModelFactory: RoomMemberProfileViewModel.Factory, private val roomMemberProfileController: RoomMemberProfileController, private val avatarRenderer: AvatarRenderer, private val roomDetailPendingActionStore: RoomDetailPendingActionStore diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt index e99c8dde64..5b07b101e7 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -18,16 +18,16 @@ package im.vector.app.features.roommemberprofile import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.R +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.mvrx.runCatchingToAsync import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider @@ -60,21 +60,14 @@ import org.matrix.android.sdk.flow.unwrap class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private val initialState: RoomMemberProfileViewState, private val stringProvider: StringProvider, private val session: Session) : - VectorViewModel(initialState) { + VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: RoomMemberProfileViewState): RoomMemberProfileViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: RoomMemberProfileViewState): RoomMemberProfileViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: RoomMemberProfileViewState): RoomMemberProfileViewModel? { - val fragment: RoomMemberProfileFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.viewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() private val room = if (initialState.roomId != null) { session.getRoom(initialState.roomId) diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheet.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheet.kt index 05ccc57b10..f83ac8f19d 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheet.kt @@ -27,17 +27,17 @@ import androidx.fragment.app.Fragment import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.commitTransaction import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.databinding.BottomSheetWithFragmentsBinding import im.vector.app.features.crypto.verification.VerificationBottomSheet import kotlinx.parcelize.Parcelize -import javax.inject.Inject import kotlin.reflect.KClass +@AndroidEntryPoint class DeviceListBottomSheet : VectorBaseBottomSheetDialogFragment() { @@ -47,12 +47,6 @@ class DeviceListBottomSheet : private val viewModel: DeviceListBottomSheetViewModel by fragmentViewModel(DeviceListBottomSheetViewModel::class) - @Inject lateinit var viewModelFactory: DeviceListBottomSheetViewModel.Factory - - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewModel.observeViewEvents { diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt index 063f7b6188..d2491237ca 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt @@ -17,7 +17,6 @@ package im.vector.app.features.roommemberprofile.devices import com.airbnb.mvrx.Async -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksViewModelFactory @@ -25,7 +24,10 @@ import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import im.vector.app.core.di.HasScreenInjector +import dagger.hilt.EntryPoints +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.SingletonEntryPoint +import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import org.matrix.android.sdk.api.session.Session @@ -37,6 +39,8 @@ import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo data class DeviceListViewState( + val userId: String, + val allowDeviceAction: Boolean, val userItem: MatrixItem? = null, val isMine: Boolean = false, val memberCrossSigningKey: MXCrossSigningInfo? = null, @@ -45,24 +49,41 @@ data class DeviceListViewState( ) : MavericksState class DeviceListBottomSheetViewModel @AssistedInject constructor(@Assisted private val initialState: DeviceListViewState, - @Assisted private val args: DeviceListBottomSheet.Args, private val session: Session) : - VectorViewModel(initialState) { + VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: DeviceListViewState, args: DeviceListBottomSheet.Args): DeviceListBottomSheetViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: DeviceListViewState): DeviceListBottomSheetViewModel + } + + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() { + + override fun initialState(viewModelContext: ViewModelContext): DeviceListViewState? { + val args = viewModelContext.args() + val userId = args.userId + val session = EntryPoints.get(viewModelContext.app(), SingletonEntryPoint::class.java).activeSessionHolder().getActiveSession() + return session.getUser(userId)?.toMatrixItem()?.let { + DeviceListViewState( + userId = userId, + allowDeviceAction = args.allowDeviceAction, + userItem = it, + isMine = userId == session.myUserId + ) + } ?: return super.initialState(viewModelContext) + } } init { - session.flow().liveUserCryptoDevices(args.userId) + + session.flow().liveUserCryptoDevices(initialState.userId) .execute { copy(cryptoDevices = it).also { refreshSelectedId() } } - session.flow().liveCrossSigningInfo(args.userId) + session.flow().liveCrossSigningInfo(initialState.userId) .execute { copy(memberCrossSigningKey = it.invoke()?.getOrNull()) } @@ -89,7 +110,7 @@ class DeviceListBottomSheetViewModel @AssistedInject constructor(@Assisted priva } private fun selectDevice(action: DeviceListAction.SelectDevice) { - if (!args.allowDeviceAction) return + if (!initialState.allowDeviceAction) return setState { copy(selectedDevice = action.device) } @@ -102,29 +123,9 @@ class DeviceListBottomSheetViewModel @AssistedInject constructor(@Assisted priva } private fun manuallyVerify(action: DeviceListAction.ManuallyVerify) { - if (!args.allowDeviceAction) return - session.cryptoService().verificationService().beginKeyVerification(VerificationMethod.SAS, args.userId, action.deviceId, null)?.let { txID -> - _viewEvents.post(DeviceListBottomSheetViewEvents.Verify(args.userId, txID)) - } - } - - companion object : MavericksViewModelFactory { - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: DeviceListViewState): DeviceListBottomSheetViewModel? { - val fragment: DeviceListBottomSheet = (viewModelContext as FragmentViewModelContext).fragment() - val args = viewModelContext.args() - return fragment.viewModelFactory.create(state, args) - } - - override fun initialState(viewModelContext: ViewModelContext): DeviceListViewState? { - val userId = viewModelContext.args().userId - val session = (viewModelContext.activity as HasScreenInjector).injector().activeSessionHolder().getActiveSession() - return session.getUser(userId)?.toMatrixItem()?.let { - DeviceListViewState( - userItem = it, - isMine = userId == session.myUserId - ) - } ?: return super.initialState(viewModelContext) + if (!initialState.allowDeviceAction) return + session.cryptoService().verificationService().beginKeyVerification(VerificationMethod.SAS, initialState.userId, action.deviceId, null)?.let { txID -> + _viewEvents.post(DeviceListBottomSheetViewEvents.Verify(initialState.userId, txID)) } } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt index f9ba2f6810..fdb639e7d6 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt @@ -23,8 +23,8 @@ import android.widget.Toast import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import com.google.android.material.appbar.MaterialToolbar +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.addFragment import im.vector.app.core.extensions.addFragmentToBackstack import im.vector.app.core.extensions.exhaustive @@ -34,7 +34,6 @@ import im.vector.app.databinding.ActivitySimpleBinding import im.vector.app.features.home.room.detail.RoomDetailPendingActionStore import im.vector.app.features.room.RequireActiveMembershipViewEvents import im.vector.app.features.room.RequireActiveMembershipViewModel -import im.vector.app.features.room.RequireActiveMembershipViewState import im.vector.app.features.roomprofile.alias.RoomAliasFragment import im.vector.app.features.roomprofile.banned.RoomBannedMemberListFragment import im.vector.app.features.roomprofile.members.RoomMemberListFragment @@ -44,10 +43,10 @@ import im.vector.app.features.roomprofile.settings.RoomSettingsFragment import im.vector.app.features.roomprofile.uploads.RoomUploadsFragment import javax.inject.Inject +@AndroidEntryPoint class RoomProfileActivity : VectorBaseActivity(), - ToolbarConfigurable, - RequireActiveMembershipViewModel.Factory { + ToolbarConfigurable { companion object { @@ -71,20 +70,9 @@ class RoomProfileActivity : private val requireActiveMembershipViewModel: RequireActiveMembershipViewModel by viewModel() - @Inject - lateinit var requireActiveMembershipViewModelFactory: RequireActiveMembershipViewModel.Factory - @Inject lateinit var roomDetailPendingActionStore: RoomDetailPendingActionStore - override fun create(initialState: RequireActiveMembershipViewState): RequireActiveMembershipViewModel { - return requireActiveMembershipViewModelFactory.create(initialState) - } - - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - override fun getBinding(): ActivitySimpleBinding { return ActivitySimpleBinding.inflate(layoutInflater) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt index e07746af85..23234f8bbd 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt @@ -67,7 +67,6 @@ class RoomProfileFragment @Inject constructor( private val roomProfileController: RoomProfileController, private val avatarRenderer: AvatarRenderer, private val roomDetailPendingActionStore: RoomDetailPendingActionStore, - val roomProfileViewModelFactory: RoomProfileViewModel.Factory ) : VectorBaseFragment(), RoomProfileController.Callback { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt index c4fc2bc7bb..472ddfc6b9 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileViewModel.kt @@ -17,13 +17,13 @@ package im.vector.app.features.roomprofile -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory -import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.R +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider @@ -53,18 +53,11 @@ class RoomProfileViewModel @AssistedInject constructor( ) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: RoomProfileViewState): RoomProfileViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: RoomProfileViewState): RoomProfileViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: RoomProfileViewState): RoomProfileViewModel? { - val fragment: RoomProfileFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.roomProfileViewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() private val room = session.getRoom(initialState.roomId)!! diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt index 0b144bea9f..e281c0f84d 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt @@ -44,7 +44,6 @@ import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject class RoomAliasFragment @Inject constructor( - val viewModelFactory: RoomAliasViewModel.Factory, private val controller: RoomAliasController, private val avatarRenderer: AvatarRenderer ) : diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt index 68cbfc6170..19f600e5de 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -17,15 +17,15 @@ package im.vector.app.features.roomprofile.alias import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext 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.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.powerlevel.PowerLevelsFlowFactory @@ -45,21 +45,14 @@ import org.matrix.android.sdk.flow.unwrap class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: RoomAliasViewState, private val session: Session) : - VectorViewModel(initialState) { + VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: RoomAliasViewState): RoomAliasViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: RoomAliasViewState): RoomAliasViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: RoomAliasViewState): RoomAliasViewModel? { - val fragment: RoomAliasFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.viewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() private val room = session.getRoom(initialState.roomId)!! diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheet.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheet.kt index e65efd4936..56dbcbfba4 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheet.kt @@ -24,7 +24,7 @@ import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState -import im.vector.app.core.di.ScreenComponent +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment @@ -44,23 +44,19 @@ data class RoomAliasBottomSheetArgs( /** * Bottom sheet fragment that shows room alias information with list of contextual actions */ +@AndroidEntryPoint class RoomAliasBottomSheet : VectorBaseBottomSheetDialogFragment(), RoomAliasBottomSheetController.Listener { private lateinit var sharedActionViewModel: RoomAliasBottomSheetSharedActionViewModel @Inject lateinit var sharedViewPool: RecyclerView.RecycledViewPool - @Inject lateinit var roomAliasBottomSheetViewModelFactory: RoomAliasBottomSheetViewModel.Factory @Inject lateinit var controller: RoomAliasBottomSheetController private val viewModel: RoomAliasBottomSheetViewModel by fragmentViewModel(RoomAliasBottomSheetViewModel::class) override val showExpanded = true - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetGenericListBinding { return BottomSheetGenericListBinding.inflate(inflater, container, false) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetViewModel.kt index bc249fc746..0efef6ad8c 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetViewModel.kt @@ -15,12 +15,12 @@ */ package im.vector.app.features.roomprofile.alias.detail -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory -import com.airbnb.mvrx.ViewModelContext 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.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel @@ -32,18 +32,11 @@ class RoomAliasBottomSheetViewModel @AssistedInject constructor( ) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: RoomAliasBottomSheetState): RoomAliasBottomSheetViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: RoomAliasBottomSheetState): RoomAliasBottomSheetViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: RoomAliasBottomSheetState): RoomAliasBottomSheetViewModel? { - val fragment: RoomAliasBottomSheet = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.roomAliasBottomSheetViewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() init { setState { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListFragment.kt index 72f43639ed..c9fc889242 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListFragment.kt @@ -39,7 +39,6 @@ import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject class RoomBannedMemberListFragment @Inject constructor( - val viewModelFactory: RoomBannedMemberListViewModel.Factory, private val roomMemberListController: RoomBannedMemberListController, private val avatarRenderer: AvatarRenderer ) : VectorBaseFragment(), diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt index e3132c3cc5..d7efc2fb79 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt @@ -16,13 +16,13 @@ package im.vector.app.features.roomprofile.banned -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory -import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.R +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider @@ -44,11 +44,11 @@ import org.matrix.android.sdk.flow.unwrap class RoomBannedMemberListViewModel @AssistedInject constructor(@Assisted initialState: RoomBannedMemberListViewState, private val stringProvider: StringProvider, private val session: Session) : - VectorViewModel(initialState) { + VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: RoomBannedMemberListViewState): RoomBannedMemberListViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: RoomBannedMemberListViewState): RoomBannedMemberListViewModel } private val room = session.getRoom(initialState.roomId)!! @@ -77,14 +77,7 @@ class RoomBannedMemberListViewModel @AssistedInject constructor(@Assisted initia } } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: RoomBannedMemberListViewState): RoomBannedMemberListViewModel? { - val fragment: RoomBannedMemberListFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.viewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() override fun handle(action: RoomBannedMemberListAction) { when (action) { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt index ed0b88c8d3..c2c938a271 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListController.kt @@ -61,7 +61,7 @@ class RoomMemberListController @Inject constructor( roomMemberSummaryFilter.filter = data.filter val roomMembersByPowerLevel = data.roomMemberSummaries.invoke() ?: return - val threePidInvites = data.threePidInvites() + val filteredThreePidInvites = data.threePidInvites() ?.filter { event -> event.content.toModel() ?.takeIf { @@ -69,7 +69,7 @@ class RoomMemberListController @Inject constructor( } != null } .orEmpty() - var threePidInvitesDone = threePidInvites.isEmpty() + var threePidInvitesDone = filteredThreePidInvites.isEmpty() for ((powerLevelCategory, roomMemberList) in roomMembersByPowerLevel) { val filteredRoomMemberList = roomMemberList.filter { roomMemberSummaryFilter.test(it) } @@ -83,7 +83,7 @@ class RoomMemberListController @Inject constructor( stringProvider.getString(RoomMemberListCategories.INVITE.titleRes) ) - buildThreePidInvites(data) + buildThreePidInvites(filteredThreePidInvites, data.actionsPermissions.canRevokeThreePidInvite) threePidInvitesDone = true } @@ -93,7 +93,7 @@ class RoomMemberListController @Inject constructor( filteredRoomMemberList.join( each = { _, roomMember -> - buildPresence(roomMember, powerLevelCategory, host, data, roomMember.userPresence) + buildRoomMember(roomMember, powerLevelCategory, host, data) }, between = { _, roomMemberBefore -> dividerItem { @@ -107,7 +107,7 @@ class RoomMemberListController @Inject constructor( id("divider_threepidinvites") } - buildThreePidInvites(data) + buildThreePidInvites(filteredThreePidInvites, data.actionsPermissions.canRevokeThreePidInvite) threePidInvitesDone = true } } @@ -118,16 +118,14 @@ class RoomMemberListController @Inject constructor( stringProvider.getString(RoomMemberListCategories.INVITE.titleRes) ) - buildThreePidInvites(data) + buildThreePidInvites(filteredThreePidInvites, data.actionsPermissions.canRevokeThreePidInvite) } } - private fun buildPresence(roomMember: RoomMemberSummary, - powerLevelCategory: RoomMemberListCategories, - host: RoomMemberListController, - data: RoomMemberListViewState, - userPresence: UserPresence? - ) { + private fun buildRoomMember(roomMember: RoomMemberSummary, + powerLevelCategory: RoomMemberListCategories, + host: RoomMemberListController, + data: RoomMemberListViewState) { val powerLabel = stringProvider.getString(powerLevelCategory.titleRes) profileMatrixItemWithPowerLevelWithPresence { @@ -138,7 +136,7 @@ class RoomMemberListController @Inject constructor( clickListener { host.callback?.onRoomMemberClicked(roomMember) } - userPresence(userPresence) + userPresence(roomMember.userPresence) powerLevelLabel( span { span(powerLabel) { @@ -149,11 +147,10 @@ class RoomMemberListController @Inject constructor( } } - private fun buildThreePidInvites(data: RoomMemberListViewState) { + private fun buildThreePidInvites(filteredThreePidInvites: List, canRevokeThreePidInvite: Boolean) { val host = this - data.threePidInvites() - ?.filter { it.content.toModel() != null } - ?.join( + filteredThreePidInvites + .join( each = { idx, event -> event.content.toModel() ?.let { content -> @@ -161,7 +158,7 @@ class RoomMemberListController @Inject constructor( id("3pid_$idx") matrixItem(MatrixItem.UserItem("@", displayName = content.displayName)) avatarRenderer(host.avatarRenderer) - editable(data.actionsPermissions.canRevokeThreePidInvite) + editable(canRevokeThreePidInvite) clickListener { host.callback?.onThreePidInviteClicked(event) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListFragment.kt index 4337e8767a..8840f61600 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListFragment.kt @@ -42,20 +42,14 @@ import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject class RoomMemberListFragment @Inject constructor( - val viewModelFactory: RoomMemberListViewModel.Factory, private val roomMemberListController: RoomMemberListController, private val avatarRenderer: AvatarRenderer ) : VectorBaseFragment(), - RoomMemberListController.Callback, - RoomMemberListViewModel.Factory { + RoomMemberListController.Callback { private val viewModel: RoomMemberListViewModel by fragmentViewModel() private val roomProfileArgs: RoomProfileArgs by args() - override fun create(initialState: RoomMemberListViewState): RoomMemberListViewModel { - return viewModelFactory.create(initialState) - } - override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRoomMemberListBinding { return FragmentRoomMemberListBinding.inflate(inflater, container, false) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt index f16353353c..adf5a31f2a 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt @@ -17,13 +17,12 @@ package im.vector.app.features.roomprofile.members import androidx.lifecycle.asFlow -import com.airbnb.mvrx.ActivityViewModelContext -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory -import com.airbnb.mvrx.ViewModelContext 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.extensions.exhaustive import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel @@ -57,24 +56,14 @@ import timber.log.Timber class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState: RoomMemberListViewState, private val roomMemberSummaryComparator: RoomMemberSummaryComparator, private val session: Session) : - VectorViewModel(initialState) { + VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: RoomMemberListViewState): RoomMemberListViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: RoomMemberListViewState): RoomMemberListViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: RoomMemberListViewState): RoomMemberListViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() private val room = session.getRoom(initialState.roomId)!! diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt index f91b482b13..26db6b001e 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsViewModel.kt @@ -16,16 +16,14 @@ package im.vector.app.features.roomprofile.notifications -import androidx.lifecycle.viewModelScope -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success -import com.airbnb.mvrx.ViewModelContext 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.VectorViewModel -import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.flow.flow @@ -37,24 +35,11 @@ class RoomNotificationSettingsViewModel @AssistedInject constructor( ) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: RoomNotificationSettingsViewState): RoomNotificationSettingsViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: RoomNotificationSettingsViewState): RoomNotificationSettingsViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: RoomNotificationSettingsViewState): RoomNotificationSettingsViewModel { - val fragmentModelContext = (viewModelContext as FragmentViewModelContext) - return if (fragmentModelContext.fragment is RoomNotificationSettingsFragment) { - val fragment: RoomNotificationSettingsFragment = fragmentModelContext.fragment() - fragment.viewModelFactory.create(state) - } else { - val fragment: RoomListQuickActionsBottomSheet = fragmentModelContext.fragment() - fragment.roomNotificationSettingsViewModelFactory.create(state) - } - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() private val room = session.getRoom(initialState.roomId)!! diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsFragment.kt index a538c9269b..acf01321c9 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsFragment.kt @@ -39,7 +39,6 @@ import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject class RoomPermissionsFragment @Inject constructor( - val viewModelFactory: RoomPermissionsViewModel.Factory, private val controller: RoomPermissionsController, private val avatarRenderer: AvatarRenderer ) : diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt index bf2f2134d6..011c4ea8ae 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/permissions/RoomPermissionsViewModel.kt @@ -16,13 +16,13 @@ package im.vector.app.features.roomprofile.permissions -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success -import com.airbnb.mvrx.ViewModelContext 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.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.powerlevel.PowerLevelsFlowFactory @@ -42,18 +42,11 @@ class RoomPermissionsViewModel @AssistedInject constructor(@Assisted initialStat VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: RoomPermissionsViewState): RoomPermissionsViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: RoomPermissionsViewState): RoomPermissionsViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: RoomPermissionsViewState): RoomPermissionsViewModel? { - val fragment: RoomPermissionsFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.viewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() private val room = session.getRoom(initialState.roomId)!! diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt index b7821c056c..ce059881b8 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt @@ -52,7 +52,6 @@ import java.util.UUID import javax.inject.Inject class RoomSettingsFragment @Inject constructor( - val viewModelFactory: RoomSettingsViewModel.Factory, private val controller: RoomSettingsController, colorProvider: ColorProvider, private val avatarRenderer: AvatarRenderer @@ -60,8 +59,7 @@ class RoomSettingsFragment @Inject constructor( VectorBaseFragment(), RoomSettingsController.Callback, OnBackPressed, - GalleryOrCameraDialogHelper.Listener, - RoomSettingsViewModel.Factory { + GalleryOrCameraDialogHelper.Listener { private val viewModel: RoomSettingsViewModel by fragmentViewModel() private lateinit var roomProfileSharedActionViewModel: RoomProfileSharedActionViewModel @@ -77,10 +75,6 @@ class RoomSettingsFragment @Inject constructor( override fun getMenuRes() = R.menu.vector_room_settings - override fun create(initialState: RoomSettingsViewState): RoomSettingsViewModel { - return viewModelFactory.create(initialState) - } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) roomProfileSharedActionViewModel = activityViewModelProvider.get(RoomProfileSharedActionViewModel::class.java) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt index c3c8ca7e2f..1e3cd053b1 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt @@ -17,13 +17,12 @@ package im.vector.app.features.roomprofile.settings import androidx.core.net.toFile -import com.airbnb.mvrx.ActivityViewModelContext -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory -import com.airbnb.mvrx.ViewModelContext 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.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.powerlevel.PowerLevelsFlowFactory @@ -50,24 +49,14 @@ import org.matrix.android.sdk.flow.unwrap class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: RoomSettingsViewState, private val vectorPreferences: VectorPreferences, private val session: Session) : - VectorViewModel(initialState) { + VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: RoomSettingsViewState): RoomSettingsViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: RoomSettingsViewState): RoomSettingsViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: RoomSettingsViewState): RoomSettingsViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() private val room = session.getRoom(initialState.roomId)!! @@ -150,7 +139,7 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: canChangeJoinRule = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_JOIN_RULES) && powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, - EventType.STATE_ROOM_GUEST_ACCESS), + EventType.STATE_ROOM_GUEST_ACCESS), canAddChildren = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_SPACE_CHILD) ) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityBottomSheet.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityBottomSheet.kt index 4089139b78..c63cf918c8 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityBottomSheet.kt @@ -21,7 +21,7 @@ import android.os.Parcelable import android.view.View import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState -import im.vector.app.core.di.ScreenComponent +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.core.ui.bottomsheet.BottomSheetGeneric import im.vector.app.core.ui.bottomsheet.BottomSheetGenericController import kotlinx.parcelize.Parcelize @@ -33,16 +33,13 @@ data class RoomHistoryVisibilityBottomSheetArgs( val currentRoomHistoryVisibility: RoomHistoryVisibility ) : Parcelable +@AndroidEntryPoint class RoomHistoryVisibilityBottomSheet : BottomSheetGeneric() { private lateinit var roomHistoryVisibilitySharedActionViewModel: RoomHistoryVisibilitySharedActionViewModel @Inject lateinit var controller: RoomHistoryVisibilityController private val viewModel: RoomHistoryVisibilityViewModel by fragmentViewModel(RoomHistoryVisibilityViewModel::class) - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - override fun getController(): BottomSheetGenericController = controller override fun onViewCreated(view: View, savedInstanceState: Bundle?) { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleActivity.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleActivity.kt index dcce7b2384..bb8db019c3 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleActivity.kt @@ -27,8 +27,8 @@ import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.withState +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.extensions.addFragment import im.vector.app.core.extensions.commitTransaction @@ -44,27 +44,18 @@ import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRul import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedViewModel import javax.inject.Inject -class RoomJoinRuleActivity : VectorBaseActivity(), - RoomJoinRuleChooseRestrictedViewModel.Factory { +@AndroidEntryPoint +class RoomJoinRuleActivity : VectorBaseActivity() { override fun getBinding() = ActivitySimpleBinding.inflate(layoutInflater) private lateinit var roomProfileArgs: RoomProfileArgs - @Inject - lateinit var allowListViewModelFactory: RoomJoinRuleChooseRestrictedViewModel.Factory - @Inject lateinit var errorFormatter: ErrorFormatter val viewModel: RoomJoinRuleChooseRestrictedViewModel by viewModel() - override fun create(initialState: RoomJoinRuleChooseRestrictedState) = allowListViewModelFactory.create(initialState) - - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - override fun initUiAndData() { roomProfileArgs = intent?.extras?.getParcelable(Mavericks.KEY_ARG) ?: return if (isFirstCreation()) { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleBottomSheet.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleBottomSheet.kt index f0f8193cc5..4185c2031b 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleBottomSheet.kt @@ -21,7 +21,7 @@ import android.os.Parcelable import android.view.View import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState -import im.vector.app.core.di.ScreenComponent +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.core.ui.bottomsheet.BottomSheetGeneric import im.vector.app.core.ui.bottomsheet.BottomSheetGenericController import kotlinx.parcelize.Parcelize @@ -44,16 +44,13 @@ data class RoomJoinRuleBottomSheetArgs( val parentSpaceName: String? ) : Parcelable +@AndroidEntryPoint class RoomJoinRuleBottomSheet : BottomSheetGeneric() { private lateinit var roomJoinRuleSharedActionViewModel: RoomJoinRuleSharedActionViewModel @Inject lateinit var controller: RoomJoinRuleController private val viewModel: RoomJoinRuleViewModel by fragmentViewModel(RoomJoinRuleViewModel::class) - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - override fun getController(): BottomSheetGenericController = controller override fun onViewCreated(view: View, savedInstanceState: Bundle?) { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/advanced/RoomJoinRuleChooseRestrictedViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/advanced/RoomJoinRuleChooseRestrictedViewModel.kt index 1e7f1d2111..4bd7568ccd 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/advanced/RoomJoinRuleChooseRestrictedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/advanced/RoomJoinRuleChooseRestrictedViewModel.kt @@ -18,19 +18,17 @@ package im.vector.app.features.roomprofile.settings.joinrule.advanced import android.graphics.Typeface import androidx.core.text.toSpannable -import androidx.lifecycle.viewModelScope -import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.R +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider @@ -175,8 +173,8 @@ class RoomJoinRuleChooseRestrictedViewModel @AssistedInject constructor( } @AssistedFactory - interface Factory { - fun create(initialState: RoomJoinRuleChooseRestrictedState): RoomJoinRuleChooseRestrictedViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: RoomJoinRuleChooseRestrictedState): RoomJoinRuleChooseRestrictedViewModel } override fun handle(action: RoomJoinRuleChooseRestrictedActions) { @@ -391,14 +389,5 @@ class RoomJoinRuleChooseRestrictedViewModel @AssistedInject constructor( } } - companion object : MavericksViewModelFactory { - - override fun create(viewModelContext: ViewModelContext, state: RoomJoinRuleChooseRestrictedState): RoomJoinRuleChooseRestrictedViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt index f079daf262..3716d9682c 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt @@ -42,11 +42,9 @@ import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject class RoomUploadsFragment @Inject constructor( - private val viewModelFactory: RoomUploadsViewModel.Factory, private val avatarRenderer: AvatarRenderer, private val notificationUtils: NotificationUtils -) : VectorBaseFragment(), - RoomUploadsViewModel.Factory by viewModelFactory { +) : VectorBaseFragment() { private val roomProfileArgs: RoomProfileArgs by args() diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt index 3d3ad375ea..92ff33395e 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt @@ -16,16 +16,15 @@ package im.vector.app.features.roomprofile.uploads -import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success -import com.airbnb.mvrx.ViewModelContext 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.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.launch @@ -40,21 +39,11 @@ class RoomUploadsViewModel @AssistedInject constructor( ) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: RoomUploadsViewState): RoomUploadsViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: RoomUploadsViewState): RoomUploadsViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: RoomUploadsViewState): RoomUploadsViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() private val room = session.getRoom(initialState.roomId)!! diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt index 7cd4fc2d3d..27fbacc362 100755 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt @@ -25,8 +25,8 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.replaceFragment import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivityVectorSettingsBinding @@ -45,6 +45,7 @@ private const val KEY_ACTIVITY_PAYLOAD = "settings-activity-payload" /** * Displays the client settings. */ +@AndroidEntryPoint class VectorSettingsActivity : VectorBaseActivity(), PreferenceFragmentCompat.OnPreferenceStartFragmentCallback, FragmentManager.OnBackStackChangedListener, @@ -62,10 +63,6 @@ class VectorSettingsActivity : VectorBaseActivity @Inject lateinit var session: Session - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - override fun initUiAndData() { configureToolbar(views.settingsToolbar) 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 efbd1cd1b4..bffabf2e93 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 @@ -23,10 +23,8 @@ import androidx.annotation.CallSuper import androidx.preference.PreferenceFragmentCompat import com.google.android.material.dialog.MaterialAlertDialogBuilder import im.vector.app.R -import im.vector.app.core.di.DaggerScreenComponent -import im.vector.app.core.di.HasScreenInjector -import im.vector.app.core.di.ScreenComponent 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.utils.toast import io.reactivex.disposables.CompositeDisposable @@ -34,7 +32,7 @@ import io.reactivex.disposables.Disposable import org.matrix.android.sdk.api.session.Session import timber.log.Timber -abstract class VectorSettingsBaseFragment : PreferenceFragmentCompat(), HasScreenInjector { +abstract class VectorSettingsBaseFragment : PreferenceFragmentCompat() { val vectorActivity: VectorBaseActivity<*> by lazy { activity as VectorBaseActivity<*> @@ -45,7 +43,6 @@ abstract class VectorSettingsBaseFragment : PreferenceFragmentCompat(), HasScree // members protected lateinit var session: Session protected lateinit var errorFormatter: ErrorFormatter - private lateinit var screenComponent: ScreenComponent abstract val preferenceXmlRes: Int @@ -56,17 +53,10 @@ abstract class VectorSettingsBaseFragment : PreferenceFragmentCompat(), HasScree } override fun onAttach(context: Context) { - screenComponent = DaggerScreenComponent.factory().create(vectorActivity.getVectorComponent(), vectorActivity) + val singletonEntryPoint = context.singletonEntryPoint() super.onAttach(context) - session = screenComponent.activeSessionHolder().getActiveSession() - errorFormatter = screenComponent.errorFormatter() - injectWith(injector()) - } - - protected open fun injectWith(injector: ScreenComponent) = Unit - - override fun injector(): ScreenComponent { - return screenComponent + session = singletonEntryPoint.activeSessionHolder().getActiveSession() + errorFormatter = singletonEntryPoint.errorFormatter() } override fun onResume() { diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt index 7e60e69379..b622d8aab4 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsSecurityPrivacyFragment.kt @@ -67,6 +67,7 @@ import kotlinx.coroutines.launch import me.gujun.android.span.span import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.extensions.getFingerprintHumanReadable +import org.matrix.android.sdk.api.raw.RawService import org.matrix.android.sdk.internal.crypto.crosssigning.isVerified import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo import org.matrix.android.sdk.internal.crypto.model.rest.DevicesListResponse @@ -79,6 +80,7 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor( private val pinCodeStore: PinCodeStore, private val keysExporter: KeysExporter, private val keysImporter: KeysImporter, + private val rawService: RawService, private val navigator: Navigator ) : VectorSettingsBaseFragment() { @@ -155,8 +157,7 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor( lifecycleScope.launchWhenResumed { findPreference(VectorPreferences.SETTINGS_CRYPTOGRAPHY_HS_ADMIN_DISABLED_E2E_DEFAULT)?.isVisible = - vectorActivity.getVectorComponent() - .rawService() + rawService .getElementWellknown(session.sessionParams) ?.isE2EByDefault() == false } diff --git a/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountFragment.kt b/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountFragment.kt index 4e599e81fb..5729e773b7 100644 --- a/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountFragment.kt @@ -36,9 +36,7 @@ import im.vector.app.features.settings.VectorSettingsActivity import org.matrix.android.sdk.api.auth.data.LoginFlowTypes import javax.inject.Inject -class DeactivateAccountFragment @Inject constructor( - val viewModelFactory: DeactivateAccountViewModel.Factory -) : VectorBaseFragment() { +class DeactivateAccountFragment @Inject constructor() : VectorBaseFragment() { private val viewModel: DeactivateAccountViewModel by fragmentViewModel() diff --git a/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewModel.kt index 5aaa0be13a..922435047f 100644 --- a/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewModel.kt @@ -15,14 +15,13 @@ */ package im.vector.app.features.settings.account.deactivation -import androidx.lifecycle.viewModelScope -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksViewModelFactory -import com.airbnb.mvrx.ViewModelContext 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.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.auth.ReAuthActivity @@ -49,8 +48,8 @@ class DeactivateAccountViewModel @AssistedInject constructor(@Assisted private v VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: DeactivateAccountViewState): DeactivateAccountViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: DeactivateAccountViewState): DeactivateAccountViewModel } var uiaContinuation: Continuation? = null @@ -114,12 +113,5 @@ class DeactivateAccountViewModel @AssistedInject constructor(@Assisted private v } } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: DeactivateAccountViewState): DeactivateAccountViewModel? { - val fragment: DeactivateAccountFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.viewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() } diff --git a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsFragment.kt index d60d9138d7..fa061cdf8d 100644 --- a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsFragment.kt @@ -42,7 +42,6 @@ import javax.inject.Inject */ class CrossSigningSettingsFragment @Inject constructor( private val controller: CrossSigningSettingsController, - val viewModelFactory: CrossSigningSettingsViewModel.Factory ) : VectorBaseFragment(), CrossSigningSettingsController.InteractionListener { @@ -55,14 +54,14 @@ class CrossSigningSettingsFragment @Inject constructor( private val reAuthActivityResultLauncher = registerStartForActivityResult { activityResult -> if (activityResult.resultCode == Activity.RESULT_OK) { when (activityResult.data?.extras?.getString(ReAuthActivity.RESULT_FLOW_TYPE)) { - LoginFlowTypes.SSO -> { + LoginFlowTypes.SSO -> { viewModel.handle(CrossSigningSettingsAction.SsoAuthDone) } LoginFlowTypes.PASSWORD -> { val password = activityResult.data?.extras?.getString(ReAuthActivity.RESULT_VALUE) ?: "" viewModel.handle(CrossSigningSettingsAction.PasswordAuthDone(password)) } - else -> { + else -> { viewModel.handle(CrossSigningSettingsAction.ReAuthCancelled) } } @@ -78,7 +77,7 @@ class CrossSigningSettingsFragment @Inject constructor( setupRecyclerView() viewModel.observeViewEvents { event -> when (event) { - is CrossSigningSettingsViewEvents.Failure -> { + is CrossSigningSettingsViewEvents.Failure -> { MaterialAlertDialogBuilder(requireContext()) .setTitle(R.string.dialog_title_error) .setMessage(errorFormatter.toHumanReadable(event.throwable)) @@ -86,7 +85,7 @@ class CrossSigningSettingsFragment @Inject constructor( .show() Unit } - is CrossSigningSettingsViewEvents.RequestReAuth -> { + is CrossSigningSettingsViewEvents.RequestReAuth -> { ReAuthActivity.newIntent(requireContext(), event.registrationFlowResponse, event.lastErrorCode, @@ -98,7 +97,7 @@ class CrossSigningSettingsFragment @Inject constructor( views.waitingView.waitingView.isVisible = true views.waitingView.waitingStatusText.setTextOrHide(event.status) } - CrossSigningSettingsViewEvents.HideModalWaitingView -> { + CrossSigningSettingsViewEvents.HideModalWaitingView -> { views.waitingView.waitingView.isVisible = false } }.exhaustive diff --git a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt index 033d9cf716..644b7f33dd 100644 --- a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt @@ -15,13 +15,13 @@ */ package im.vector.app.features.settings.crosssigning -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory -import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.R +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider @@ -61,27 +61,27 @@ class CrossSigningSettingsViewModel @AssistedInject constructor( ) { myDevicesInfo, mxCrossSigningInfo -> myDevicesInfo to mxCrossSigningInfo } - .execute { data -> - val crossSigningKeys = data.invoke()?.second?.getOrNull() - val xSigningIsEnableInAccount = crossSigningKeys != null - val xSigningKeysAreTrusted = session.cryptoService().crossSigningService().checkUserTrust(session.myUserId).isVerified() - val xSigningKeyCanSign = session.cryptoService().crossSigningService().canCrossSign() + .execute { data -> + val crossSigningKeys = data.invoke()?.second?.getOrNull() + val xSigningIsEnableInAccount = crossSigningKeys != null + val xSigningKeysAreTrusted = session.cryptoService().crossSigningService().checkUserTrust(session.myUserId).isVerified() + val xSigningKeyCanSign = session.cryptoService().crossSigningService().canCrossSign() - copy( - crossSigningInfo = crossSigningKeys, - xSigningIsEnableInAccount = xSigningIsEnableInAccount, - xSigningKeysAreTrusted = xSigningKeysAreTrusted, - xSigningKeyCanSign = xSigningKeyCanSign - ) - } + copy( + crossSigningInfo = crossSigningKeys, + xSigningIsEnableInAccount = xSigningIsEnableInAccount, + xSigningKeysAreTrusted = xSigningKeysAreTrusted, + xSigningKeyCanSign = xSigningKeyCanSign + ) + } } var uiaContinuation: Continuation? = null var pendingAuth: UIABaseAuth? = null @AssistedFactory - interface Factory { - fun create(initialState: CrossSigningSettingsViewState): CrossSigningSettingsViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: CrossSigningSettingsViewState): CrossSigningSettingsViewModel } override fun handle(action: CrossSigningSettingsAction) { @@ -154,12 +154,5 @@ class CrossSigningSettingsViewModel @AssistedInject constructor( _viewEvents.post(CrossSigningSettingsViewEvents.Failure(Exception(stringProvider.getString(R.string.failed_to_initialize_cross_signing)))) } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: CrossSigningSettingsViewState): CrossSigningSettingsViewModel? { - val fragment: CrossSigningSettingsFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.viewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheet.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheet.kt index 7ba6042027..441a344660 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheet.kt @@ -25,7 +25,7 @@ import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.parentFragmentViewModel import com.airbnb.mvrx.withState -import im.vector.app.core.di.ScreenComponent +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment @@ -39,6 +39,7 @@ data class DeviceVerificationInfoArgs( val deviceId: String ) : Parcelable +@AndroidEntryPoint class DeviceVerificationInfoBottomSheet : VectorBaseBottomSheetDialogFragment(), DeviceVerificationInfoBottomSheetController.Callback { @@ -47,12 +48,6 @@ class DeviceVerificationInfoBottomSheet : private val sharedViewModel: DevicesViewModel by parentFragmentViewModel(DevicesViewModel::class) - @Inject lateinit var deviceVerificationInfoViewModelFactory: DeviceVerificationInfoBottomSheetViewModel.Factory - - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - @Inject lateinit var controller: DeviceVerificationInfoBottomSheetController override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetGenericListWithTitleBinding { diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt index e6cde74440..3a944b5a71 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt @@ -15,13 +15,13 @@ */ package im.vector.app.features.settings.devices -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory -import com.airbnb.mvrx.ViewModelContext 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.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel @@ -31,15 +31,17 @@ import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@Assisted initialState: DeviceVerificationInfoBottomSheetViewState, - @Assisted val deviceId: String, val session: Session ) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: DeviceVerificationInfoBottomSheetViewState, deviceId: String): DeviceVerificationInfoBottomSheetViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: DeviceVerificationInfoBottomSheetViewState): DeviceVerificationInfoBottomSheetViewModel } + companion object : MavericksViewModelFactory + by hiltMavericksViewModelFactory() + init { setState { @@ -59,7 +61,7 @@ class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@As session.flow().liveUserCryptoDevices(session.myUserId) .map { list -> - list.firstOrNull { it.deviceId == deviceId } + list.firstOrNull { it.deviceId == initialState.deviceId } } .execute { copy( @@ -82,24 +84,13 @@ class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@As session.flow().liveMyDevicesInfo() .map { devices -> - devices.firstOrNull { it.deviceId == deviceId } ?: DeviceInfo(deviceId = deviceId) + devices.firstOrNull { it.deviceId == initialState.deviceId } ?: DeviceInfo(deviceId = initialState.deviceId) } .execute { copy(deviceInfo = it) } } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: DeviceVerificationInfoBottomSheetViewState): - DeviceVerificationInfoBottomSheetViewModel? { - val fragment: DeviceVerificationInfoBottomSheet = (viewModelContext as FragmentViewModelContext).fragment() - val args = viewModelContext.args() - return fragment.deviceVerificationInfoViewModelFactory.create(state, args.deviceId) - } - } - override fun handle(action: EmptyAction) { } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewState.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewState.kt index e320642ed0..32927ca068 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewState.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewState.kt @@ -23,6 +23,7 @@ import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo data class DeviceVerificationInfoBottomSheetViewState( + val deviceId: String, val cryptoDeviceInfo: Async = Uninitialized, val deviceInfo: Async = Uninitialized, val hasAccountCrossSigning: Boolean = false, @@ -32,6 +33,7 @@ data class DeviceVerificationInfoBottomSheetViewState( val isRecoverySetup: Boolean = false ) : MavericksState { - val canVerifySession: Boolean - get() = hasOtherSessions || isRecoverySetup + constructor(args: DeviceVerificationInfoArgs) : this(deviceId = args.deviceId) + + val canVerifySession = hasOtherSessions || isRecoverySetup } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt index a8154c3e11..e8300a1097 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt @@ -18,17 +18,17 @@ package im.vector.app.features.settings.devices import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.R +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.core.resources.StringProvider import im.vector.app.features.auth.ReAuthActivity @@ -97,18 +97,11 @@ class DevicesViewModel @AssistedInject constructor( var pendingAuth: UIABaseAuth? = null @AssistedFactory - interface Factory { - fun create(initialState: DevicesViewState): DevicesViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: DevicesViewState): DevicesViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: DevicesViewState): DevicesViewModel? { - val fragment: VectorSettingsDevicesFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.devicesViewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() private val refreshPublisher: PublishSubject = PublishSubject.create() diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/VectorSettingsDevicesFragment.kt index 62923d4f3d..531e9a944b 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/VectorSettingsDevicesFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/VectorSettingsDevicesFragment.kt @@ -47,7 +47,6 @@ import javax.inject.Inject * Display the list of the user's device */ class VectorSettingsDevicesFragment @Inject constructor( - val devicesViewModelFactory: DevicesViewModel.Factory, private val devicesController: DevicesController ) : VectorBaseFragment(), DevicesController.Callback { diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataFragment.kt index 570da6875a..a586e14d99 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataFragment.kt @@ -37,7 +37,6 @@ import org.matrix.android.sdk.internal.di.MoshiProvider import javax.inject.Inject class AccountDataFragment @Inject constructor( - val viewModelFactory: AccountDataViewModel.Factory, private val epoxyController: AccountDataEpoxyController, private val colorProvider: ColorProvider ) : VectorBaseFragment(), diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt index 104ee71edc..6289699687 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt @@ -17,14 +17,14 @@ package im.vector.app.features.settings.devtools import com.airbnb.mvrx.Async -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext 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.extensions.exhaustive import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel @@ -61,16 +61,9 @@ class AccountDataViewModel @AssistedInject constructor(@Assisted initialState: A } @AssistedFactory - interface Factory { - fun create(initialState: AccountDataViewState): AccountDataViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: AccountDataViewState): AccountDataViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: AccountDataViewState): AccountDataViewModel? { - val fragment: AccountDataFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.viewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() } diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailFragment.kt index 7325288c55..83740c5018 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailFragment.kt @@ -34,7 +34,6 @@ import org.matrix.android.sdk.api.session.events.model.Event import javax.inject.Inject class GossipingEventsPaperTrailFragment @Inject constructor( - val viewModelFactory: GossipingEventsPaperTrailViewModel.Factory, private val epoxyController: GossipingTrailPagedEpoxyController, private val colorProvider: ColorProvider ) : VectorBaseFragment(), diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt index fd09b38919..dde032d303 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt @@ -19,15 +19,15 @@ package im.vector.app.features.settings.devtools import androidx.lifecycle.asFlow import androidx.paging.PagedList import com.airbnb.mvrx.Async -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext 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.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel @@ -60,17 +60,9 @@ class GossipingEventsPaperTrailViewModel @AssistedInject constructor(@Assisted i override fun handle(action: EmptyAction) {} @AssistedFactory - interface Factory { - fun create(initialState: GossipingEventsPaperTrailState): GossipingEventsPaperTrailViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: GossipingEventsPaperTrailState): GossipingEventsPaperTrailViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: GossipingEventsPaperTrailState): GossipingEventsPaperTrailViewModel? { - val fragment: GossipingEventsPaperTrailFragment = (viewModelContext as FragmentViewModelContext).fragment() - - return fragment.viewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() } diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/IncomingKeyRequestListFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/IncomingKeyRequestListFragment.kt index 8bf89d975c..ac4bef9c94 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/IncomingKeyRequestListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/IncomingKeyRequestListFragment.kt @@ -30,7 +30,6 @@ import im.vector.app.databinding.FragmentGenericRecyclerBinding import javax.inject.Inject class IncomingKeyRequestListFragment @Inject constructor( - val viewModelFactory: KeyRequestListViewModel.Factory, private val epoxyController: IncomingKeyRequestPagedController ) : VectorBaseFragment() { diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt index 37decc4a12..197a72cb05 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt @@ -17,17 +17,16 @@ package im.vector.app.features.settings.devtools import androidx.lifecycle.asFlow -import androidx.lifecycle.viewModelScope import androidx.paging.PagedList import com.airbnb.mvrx.Async -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext 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.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel @@ -43,7 +42,7 @@ data class KeyRequestListViewState( class KeyRequestListViewModel @AssistedInject constructor(@Assisted initialState: KeyRequestListViewState, private val session: Session) : - VectorViewModel(initialState) { + VectorViewModel(initialState) { init { refresh() @@ -67,19 +66,9 @@ class KeyRequestListViewModel @AssistedInject constructor(@Assisted initialState override fun handle(action: EmptyAction) {} @AssistedFactory - interface Factory { - fun create(initialState: KeyRequestListViewState): KeyRequestListViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: KeyRequestListViewState): KeyRequestListViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: KeyRequestListViewState): KeyRequestListViewModel? { - val context = viewModelContext as FragmentViewModelContext - val factory = (context.fragment as? IncomingKeyRequestListFragment)?.viewModelFactory - ?: (context.fragment as? OutgoingKeyRequestListFragment)?.viewModelFactory - - return factory?.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() } diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestViewModel.kt index 362502d7d8..f480eb2db8 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestViewModel.kt @@ -17,19 +17,18 @@ package im.vector.app.features.settings.devtools import android.net.Uri -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext 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.extensions.exhaustive import im.vector.app.core.platform.VectorViewEvents import im.vector.app.core.platform.VectorViewModel @@ -56,18 +55,11 @@ class KeyRequestViewModel @AssistedInject constructor( VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: KeyRequestViewState): KeyRequestViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: KeyRequestViewState): KeyRequestViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: KeyRequestViewState): KeyRequestViewModel? { - val fragment: KeyRequestsFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.viewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() override fun handle(action: KeyRequestAction) { when (action) { diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestsFragment.kt index 0b3d8812f1..d807fc620a 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestsFragment.kt @@ -41,8 +41,7 @@ import im.vector.app.databinding.FragmentDevtoolKeyrequestsBinding import org.matrix.android.sdk.api.extensions.tryOrNull import javax.inject.Inject -class KeyRequestsFragment @Inject constructor( - val viewModelFactory: KeyRequestViewModel.Factory) : VectorBaseFragment() { +class KeyRequestsFragment @Inject constructor() : VectorBaseFragment() { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentDevtoolKeyrequestsBinding { return FragmentDevtoolKeyrequestsBinding.inflate(inflater, container, false) diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/OutgoingKeyRequestListFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/OutgoingKeyRequestListFragment.kt index 0cbca2f38a..0483d5fb4d 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/OutgoingKeyRequestListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/OutgoingKeyRequestListFragment.kt @@ -29,7 +29,6 @@ import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentGenericRecyclerBinding import javax.inject.Inject class OutgoingKeyRequestListFragment @Inject constructor( - val viewModelFactory: KeyRequestListViewModel.Factory, private val epoxyController: OutgoingKeyRequestPagedController ) : VectorBaseFragment() { diff --git a/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsFragment.kt index 20541a1ebb..28bce90424 100644 --- a/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsFragment.kt @@ -34,7 +34,6 @@ import javax.inject.Inject * Display some information about the homeserver */ class HomeserverSettingsFragment @Inject constructor( - val homeserverSettingsViewModelFactory: HomeserverSettingsViewModel.Factory, private val homeserverSettingsController: HomeserverSettingsController ) : VectorBaseFragment(), HomeserverSettingsController.Callback { diff --git a/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsViewModel.kt index 91ad34f1b6..fab563b49e 100644 --- a/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/homeserver/HomeserverSettingsViewModel.kt @@ -16,16 +16,15 @@ package im.vector.app.features.settings.homeserver -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success -import com.airbnb.mvrx.ViewModelContext 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.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.launch @@ -37,18 +36,11 @@ class HomeserverSettingsViewModel @AssistedInject constructor( ) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: HomeServerSettingsViewState): HomeserverSettingsViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: HomeServerSettingsViewState): HomeserverSettingsViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: HomeServerSettingsViewState): HomeserverSettingsViewModel? { - val fragment: HomeserverSettingsFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.homeserverSettingsViewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() init { setState { diff --git a/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt index 91213809de..b2a7b2cbd1 100644 --- a/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt @@ -18,16 +18,16 @@ package im.vector.app.features.settings.ignored import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext 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.VectorViewModel import im.vector.app.core.platform.VectorViewModelAction import kotlinx.coroutines.launch @@ -49,18 +49,11 @@ class IgnoredUsersViewModel @AssistedInject constructor(@Assisted initialState: VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: IgnoredUsersViewState): IgnoredUsersViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: IgnoredUsersViewState): IgnoredUsersViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: IgnoredUsersViewState): IgnoredUsersViewModel? { - val ignoredUsersFragment: VectorSettingsIgnoredUsersFragment = (viewModelContext as FragmentViewModelContext).fragment() - return ignoredUsersFragment.ignoredUsersViewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() init { observeIgnoredUsers() diff --git a/vector/src/main/java/im/vector/app/features/settings/ignored/VectorSettingsIgnoredUsersFragment.kt b/vector/src/main/java/im/vector/app/features/settings/ignored/VectorSettingsIgnoredUsersFragment.kt index 1526ac0e69..509014492d 100644 --- a/vector/src/main/java/im/vector/app/features/settings/ignored/VectorSettingsIgnoredUsersFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/ignored/VectorSettingsIgnoredUsersFragment.kt @@ -36,7 +36,6 @@ import im.vector.app.databinding.FragmentGenericRecyclerBinding import javax.inject.Inject class VectorSettingsIgnoredUsersFragment @Inject constructor( - val ignoredUsersViewModelFactory: IgnoredUsersViewModel.Factory, private val ignoredUsersController: IgnoredUsersController ) : VectorBaseFragment(), IgnoredUsersController.Callback { diff --git a/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerFragment.kt b/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerFragment.kt index 7368bec397..601574c908 100644 --- a/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerFragment.kt @@ -34,10 +34,8 @@ import java.util.Locale import javax.inject.Inject class LocalePickerFragment @Inject constructor( - private val viewModelFactory: LocalePickerViewModel.Factory, private val controller: LocalePickerController ) : VectorBaseFragment(), - LocalePickerViewModel.Factory by viewModelFactory, LocalePickerController.Listener { private val viewModel: LocalePickerViewModel by fragmentViewModel() diff --git a/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerViewModel.kt index 83858dff6a..d6b35fa4fe 100644 --- a/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerViewModel.kt @@ -16,15 +16,13 @@ package im.vector.app.features.settings.locale -import androidx.lifecycle.viewModelScope -import com.airbnb.mvrx.ActivityViewModelContext -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success -import com.airbnb.mvrx.ViewModelContext 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.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.configuration.VectorConfiguration @@ -37,8 +35,8 @@ class LocalePickerViewModel @AssistedInject constructor( ) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: LocalePickerViewState): LocalePickerViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: LocalePickerViewState): LocalePickerViewModel } init { @@ -53,17 +51,7 @@ class LocalePickerViewModel @AssistedInject constructor( } } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: LocalePickerViewState): LocalePickerViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() override fun handle(action: LocalePickerAction) { when (action) { 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 b014b3d2dc..4199bd1753 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.VectorPreference import im.vector.app.core.preference.VectorPreferenceCategory import im.vector.app.core.preference.VectorSwitchPreference import im.vector.app.core.pushers.PushersManager +import im.vector.app.core.services.GuardServiceStarter import im.vector.app.core.utils.isIgnoringBatteryOptimizations import im.vector.app.core.utils.requestDisablingBatteryOptimization import im.vector.app.features.notifications.NotificationUtils @@ -61,7 +62,8 @@ import javax.inject.Inject class VectorSettingsNotificationPreferenceFragment @Inject constructor( private val pushManager: PushersManager, private val activeSessionHolder: ActiveSessionHolder, - private val vectorPreferences: VectorPreferences + private val vectorPreferences: VectorPreferences, + private val guardServiceStarter: GuardServiceStarter ) : VectorSettingsBaseFragment(), BackgroundSyncModeChooserDialog.InteractionListener { @@ -216,14 +218,19 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor( it.isVisible = !FcmHelper.isPushSupported() } + val backgroundSyncEnabled = vectorPreferences.isBackgroundSyncEnabled() findPreference(VectorPreferences.SETTINGS_SET_SYNC_TIMEOUT_PREFERENCE_KEY)?.let { - it.isEnabled = vectorPreferences.isBackgroundSyncEnabled() + it.isEnabled = backgroundSyncEnabled it.summary = secondsToText(vectorPreferences.backgroundSyncTimeOut()) } findPreference(VectorPreferences.SETTINGS_SET_SYNC_DELAY_PREFERENCE_KEY)?.let { - it.isEnabled = vectorPreferences.isBackgroundSyncEnabled() + it.isEnabled = backgroundSyncEnabled it.summary = secondsToText(vectorPreferences.backgroundSyncDelay()) } + when { + backgroundSyncEnabled -> guardServiceStarter.start() + else -> guardServiceStarter.stop() + } } /** diff --git a/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysFragment.kt b/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysFragment.kt index 0801e78197..65c62542bb 100644 --- a/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysFragment.kt @@ -36,7 +36,6 @@ import javax.inject.Inject // Referenced in vector_settings_notifications.xml class PushGatewaysFragment @Inject constructor( - val pushGatewaysViewModelFactory: PushGatewaysViewModel.Factory, private val epoxyController: PushGateWayController ) : VectorBaseFragment() { diff --git a/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysViewModel.kt index d8205aada9..1256673364 100644 --- a/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysViewModel.kt @@ -17,14 +17,14 @@ package im.vector.app.features.settings.push import com.airbnb.mvrx.Async -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext 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.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.launch @@ -38,21 +38,14 @@ data class PushGatewayViewState( class PushGatewaysViewModel @AssistedInject constructor(@Assisted initialState: PushGatewayViewState, private val session: Session) : - VectorViewModel(initialState) { + VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: PushGatewayViewState): PushGatewaysViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: PushGatewayViewState): PushGatewaysViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: PushGatewayViewState): PushGatewaysViewModel? { - val fragment: PushGatewaysFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.pushGatewaysViewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() init { observePushers() diff --git a/vector/src/main/java/im/vector/app/features/settings/push/PushRulesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/push/PushRulesViewModel.kt index 745d71fd41..0b6b72bb10 100644 --- a/vector/src/main/java/im/vector/app/features/settings/push/PushRulesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/push/PushRulesViewModel.kt @@ -18,7 +18,8 @@ package im.vector.app.features.settings.push import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.ViewModelContext -import im.vector.app.core.di.HasScreenInjector +import dagger.hilt.EntryPoints +import im.vector.app.core.di.SingletonEntryPoint import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel @@ -34,7 +35,7 @@ class PushRulesViewModel(initialState: PushRulesViewState) : companion object : MavericksViewModelFactory { override fun initialState(viewModelContext: ViewModelContext): PushRulesViewState? { - val session = (viewModelContext.activity as HasScreenInjector).injector().activeSessionHolder().getActiveSession() + val session = EntryPoints.get(viewModelContext.app(), SingletonEntryPoint::class.java).activeSessionHolder().getActiveSession() val rules = session.getPushRules().getAllRules() return PushRulesViewState(rules) } diff --git a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsFragment.kt index 384348b85d..a893f0f508 100644 --- a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsFragment.kt @@ -43,12 +43,10 @@ import org.matrix.android.sdk.api.session.identity.ThreePid import javax.inject.Inject class ThreePidsSettingsFragment @Inject constructor( - private val viewModelFactory: ThreePidsSettingsViewModel.Factory, private val epoxyController: ThreePidsSettingsController ) : VectorBaseFragment(), OnBackPressed, - ThreePidsSettingsViewModel.Factory by viewModelFactory, ThreePidsSettingsController.InteractionListener { private val viewModel: ThreePidsSettingsViewModel by fragmentViewModel() diff --git a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt index cd0d74a288..12ff436ccb 100644 --- a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt @@ -16,16 +16,15 @@ package im.vector.app.features.settings.threepids -import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory -import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.R +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider @@ -79,21 +78,11 @@ class ThreePidsSettingsViewModel @AssistedInject constructor( } @AssistedFactory - interface Factory { - fun create(initialState: ThreePidsSettingsViewState): ThreePidsSettingsViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: ThreePidsSettingsViewState): ThreePidsSettingsViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: ThreePidsSettingsViewState): ThreePidsSettingsViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() init { observeThreePids() diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestSystemSettings.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestSystemSettings.kt index d4f089b943..42f506d4a6 100644 --- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestSystemSettings.kt +++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestSystemSettings.kt @@ -15,10 +15,10 @@ */ package im.vector.app.features.settings.troubleshoot -import android.content.Context import android.content.Intent import androidx.activity.result.ActivityResultLauncher import androidx.core.app.NotificationManagerCompat +import androidx.fragment.app.FragmentActivity import im.vector.app.R import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.startNotificationSettingsIntent @@ -27,7 +27,7 @@ import javax.inject.Inject /** * Checks if notifications are enable in the system settings for this app. */ -class TestSystemSettings @Inject constructor(private val context: Context, +class TestSystemSettings @Inject constructor(private val context: FragmentActivity, private val stringProvider: StringProvider) : TroubleshootTest(R.string.settings_troubleshoot_test_system_settings_title) { diff --git a/vector/src/main/java/im/vector/app/features/share/IncomingShareActivity.kt b/vector/src/main/java/im/vector/app/features/share/IncomingShareActivity.kt index b9d3b1ba14..09321ad27e 100644 --- a/vector/src/main/java/im/vector/app/features/share/IncomingShareActivity.kt +++ b/vector/src/main/java/im/vector/app/features/share/IncomingShareActivity.kt @@ -17,12 +17,14 @@ package im.vector.app.features.share import com.google.android.material.appbar.MaterialToolbar +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R import im.vector.app.core.extensions.addFragment import im.vector.app.core.platform.ToolbarConfigurable import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivitySimpleBinding +@AndroidEntryPoint class IncomingShareActivity : VectorBaseActivity(), ToolbarConfigurable { override fun getBinding() = ActivitySimpleBinding.inflate(layoutInflater) diff --git a/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt b/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt index 2132ba7989..d5fd3050e9 100644 --- a/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt +++ b/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt @@ -50,7 +50,6 @@ import javax.inject.Inject * The user can select multiple rooms to send the data to */ class IncomingShareFragment @Inject constructor( - val incomingShareViewModelFactory: IncomingShareViewModel.Factory, private val incomingShareController: IncomingShareController, private val sessionHolder: ActiveSessionHolder ) : diff --git a/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt b/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt index b476065035..4a413ad8ba 100644 --- a/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt @@ -16,12 +16,12 @@ package im.vector.app.features.share -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory -import com.airbnb.mvrx.ViewModelContext 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.extensions.exhaustive import im.vector.app.core.extensions.toggle import im.vector.app.core.platform.VectorViewModel @@ -46,18 +46,11 @@ class IncomingShareViewModel @AssistedInject constructor( VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: IncomingShareViewState): IncomingShareViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: IncomingShareViewState): IncomingShareViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: IncomingShareViewState): IncomingShareViewModel? { - val fragment: IncomingShareFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.incomingShareViewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() private val filterStream = MutableStateFlow("") diff --git a/vector/src/main/java/im/vector/app/features/signout/hard/SignedOutActivity.kt b/vector/src/main/java/im/vector/app/features/signout/hard/SignedOutActivity.kt index 6f05a73f13..ee7557b402 100644 --- a/vector/src/main/java/im/vector/app/features/signout/hard/SignedOutActivity.kt +++ b/vector/src/main/java/im/vector/app/features/signout/hard/SignedOutActivity.kt @@ -19,6 +19,7 @@ package im.vector.app.features.signout.hard import android.content.Context import android.content.Intent import android.os.Bundle +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivitySignedOutBinding import im.vector.app.features.MainActivity @@ -29,6 +30,7 @@ import timber.log.Timber /** * In this screen, the user is viewing a message informing that he has been logged out */ +@AndroidEntryPoint class SignedOutActivity : VectorBaseActivity() { override fun getBinding() = ActivitySignedOutBinding.inflate(layoutInflater) diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutActivity.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutActivity.kt index 72b9a278e2..6e70b34002 100644 --- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutActivity.kt +++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutActivity.kt @@ -23,8 +23,8 @@ import androidx.fragment.app.FragmentManager import com.airbnb.mvrx.Success import com.airbnb.mvrx.viewModel import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.extensions.replaceFragment import im.vector.app.features.MainActivity @@ -39,19 +39,14 @@ import javax.inject.Inject * In this screen, the user is viewing a message informing that he has been logged out * Extends LoginActivity to get the login with SSO and forget password functionality for (nearly) free */ +@AndroidEntryPoint class SoftLogoutActivity : LoginActivity() { private val softLogoutViewModel: SoftLogoutViewModel by viewModel() - @Inject lateinit var softLogoutViewModelFactory: SoftLogoutViewModel.Factory @Inject lateinit var session: Session @Inject lateinit var errorFormatter: ErrorFormatter - override fun injectWith(injector: ScreenComponent) { - super.injectWith(injector) - injector.inject(this) - } - override fun initUiAndData() { super.initUiAndData() diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutActivity2.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutActivity2.kt index 3689bff0c7..ed45069e92 100644 --- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutActivity2.kt +++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutActivity2.kt @@ -23,8 +23,8 @@ import androidx.fragment.app.FragmentManager import com.airbnb.mvrx.Success import com.airbnb.mvrx.viewModel import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.extensions.replaceFragment import im.vector.app.features.MainActivity @@ -41,19 +41,14 @@ import javax.inject.Inject * * This is just a copy of SoftLogoutActivity2, which extends LoginActivity2 */ +@AndroidEntryPoint class SoftLogoutActivity2 : LoginActivity2() { private val softLogoutViewModel: SoftLogoutViewModel by viewModel() - @Inject lateinit var softLogoutViewModelFactory: SoftLogoutViewModel.Factory @Inject lateinit var session: Session @Inject lateinit var errorFormatter: ErrorFormatter - override fun injectWith(injector: ScreenComponent) { - super.injectWith(injector) - injector.inject(this) - } - override fun initUiAndData() { super.initUiAndData() diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt index dfc483a813..52986a1f3b 100644 --- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt @@ -27,6 +27,8 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject 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.extensions.hasUnsavedKeys import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.login.LoginMode @@ -47,11 +49,11 @@ class SoftLogoutViewModel @AssistedInject constructor( ) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: SoftLogoutViewState): SoftLogoutViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: SoftLogoutViewState): SoftLogoutViewModel } - companion object : MavericksViewModelFactory { + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() { override fun initialState(viewModelContext: ViewModelContext): SoftLogoutViewState? { val activity: SoftLogoutActivity = (viewModelContext as ActivityViewModelContext).activity() @@ -64,12 +66,6 @@ class SoftLogoutViewModel @AssistedInject constructor( hasUnsavedKeys = activity.session.hasUnsavedKeys() ) } - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: SoftLogoutViewState): SoftLogoutViewModel? { - val activity: SoftLogoutActivity = (viewModelContext as ActivityViewModelContext).activity() - return activity.softLogoutViewModelFactory.create(state) - } } init { diff --git a/vector/src/main/java/im/vector/app/features/spaces/InviteRoomSpaceChooserBottomSheet.kt b/vector/src/main/java/im/vector/app/features/spaces/InviteRoomSpaceChooserBottomSheet.kt index cf7871bc99..b4c1e67cfb 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/InviteRoomSpaceChooserBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/InviteRoomSpaceChooserBottomSheet.kt @@ -23,14 +23,15 @@ import android.view.View import android.view.ViewGroup import androidx.core.view.isVisible import com.airbnb.mvrx.args +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.databinding.BottomSheetSpaceInviteChooserBinding import kotlinx.parcelize.Parcelize import javax.inject.Inject +@AndroidEntryPoint class InviteRoomSpaceChooserBottomSheet : VectorBaseBottomSheetDialogFragment() { @Parcelize @@ -53,10 +54,6 @@ class InviteRoomSpaceChooserBottomSheet : VectorBaseBottomSheetDialogFragment() { val settingsViewModel: SpaceMenuViewModel by parentFragmentViewModel() @@ -60,10 +61,6 @@ class LeaveSpaceBottomSheet : VectorBaseBottomSheetDialogFragment(), SpaceDirectoryViewModel.Factory, MatrixToBottomSheet.InteractionListener { - - @Inject lateinit var spaceDirectoryViewModelFactory: SpaceDirectoryViewModel.Factory - - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } +@AndroidEntryPoint +class SpaceExploreActivity : VectorBaseActivity(), MatrixToBottomSheet.InteractionListener { override fun getBinding(): ActivitySimpleBinding = ActivitySimpleBinding.inflate(layoutInflater) @@ -113,9 +106,6 @@ class SpaceExploreActivity : VectorBaseActivity(), SpaceD } } - override fun create(initialState: SpaceDirectoryState): SpaceDirectoryViewModel = - spaceDirectoryViewModelFactory.create(initialState) - override fun mxToBottomSheetNavigateToRoom(roomId: String) { navigator.openRoom(this, roomId) } diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceListFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceListFragment.kt index 0a67977e6c..dff98722eb 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceListFragment.kt @@ -39,12 +39,11 @@ import org.matrix.android.sdk.api.session.room.model.RoomSummary import javax.inject.Inject class SpaceListFragment @Inject constructor( - val spaceListViewModelFactory: SpacesListViewModel.Factory, private val spaceController: SpaceSummaryController ) : VectorBaseFragment(), SpaceSummaryController.Callback { private lateinit var sharedActionViewModel: HomeSharedActionViewModel - private val viewModel: SpacesListViewModel by fragmentViewModel() + private val viewModel: SpaceListViewModel by fragmentViewModel() override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentGroupListBinding { return FragmentGroupListBinding.inflate(inflater, container, false) diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewModel.kt similarity index 92% rename from vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt rename to vector/src/main/java/im/vector/app/features/spaces/SpaceListViewModel.kt index fbbaeafe72..4487833773 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewModel.kt @@ -17,16 +17,16 @@ package im.vector.app.features.spaces import androidx.lifecycle.asFlow -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success -import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.AppStateHandler import im.vector.app.RoomGroupingMethod +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.invite.AutoAcceptInvites import im.vector.app.features.session.coroutineScope @@ -59,26 +59,19 @@ import org.matrix.android.sdk.api.session.space.model.TopLevelSpaceComparator import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.flow.flow -class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: SpaceListViewState, - private val appStateHandler: AppStateHandler, - private val session: Session, - private val vectorPreferences: VectorPreferences, - private val autoAcceptInvites: AutoAcceptInvites +class SpaceListViewModel @AssistedInject constructor(@Assisted initialState: SpaceListViewState, + private val appStateHandler: AppStateHandler, + private val session: Session, + private val vectorPreferences: VectorPreferences, + private val autoAcceptInvites: AutoAcceptInvites ) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: SpaceListViewState): SpacesListViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: SpaceListViewState): SpaceListViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: SpaceListViewState): SpacesListViewModel { - val groupListFragment: SpaceListFragment = (viewModelContext as FragmentViewModelContext).fragment() - return groupListFragment.spaceListViewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() // private var currentGroupingMethod : RoomGroupingMethod? = null diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt index 887c93afd4..2e9af2eacb 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt @@ -16,18 +16,17 @@ package im.vector.app.features.spaces -import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.AppStateHandler +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.powerlevel.PowerLevelsFlowFactory @@ -52,21 +51,11 @@ class SpaceMenuViewModel @AssistedInject constructor( ) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: SpaceMenuState): SpaceMenuViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: SpaceMenuState): SpaceMenuViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: SpaceMenuState): SpaceMenuViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() init { val roomSummary = session.getRoomSummary(initialState.spaceId) diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpacePreviewActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/SpacePreviewActivity.kt index 59166529b9..ef65f35716 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpacePreviewActivity.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpacePreviewActivity.kt @@ -20,6 +20,7 @@ import android.content.Context import android.content.Intent import android.os.Bundle import com.airbnb.mvrx.Mavericks +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R import im.vector.app.core.extensions.commitTransaction import im.vector.app.core.platform.VectorBaseActivity @@ -27,6 +28,7 @@ import im.vector.app.databinding.ActivitySimpleBinding import im.vector.app.features.spaces.preview.SpacePreviewArgs import im.vector.app.features.spaces.preview.SpacePreviewFragment +@AndroidEntryPoint class SpacePreviewActivity : VectorBaseActivity() { lateinit var sharedActionViewModel: SpacePreviewSharedActionViewModel diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceSettingsMenuBottomSheet.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceSettingsMenuBottomSheet.kt index 040f1f9057..7449868292 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceSettingsMenuBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceSettingsMenuBottomSheet.kt @@ -26,7 +26,7 @@ import com.airbnb.mvrx.Success import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState -import im.vector.app.core.di.ScreenComponent +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.databinding.BottomSheetSpaceSettingsBinding @@ -46,12 +46,12 @@ data class SpaceBottomSheetSettingsArgs( val spaceId: String ) : Parcelable -class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment(), SpaceMenuViewModel.Factory { +@AndroidEntryPoint +class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment() { @Inject lateinit var navigator: Navigator @Inject lateinit var avatarRenderer: AvatarRenderer @Inject lateinit var bugReporter: BugReporter - @Inject lateinit var viewModelFactory: SpaceMenuViewModel.Factory private val spaceArgs: SpaceBottomSheetSettingsArgs by args() @@ -65,10 +65,6 @@ class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment { + override fun create(initialState: CreateSpaceState): CreateSpaceViewModel } private fun startListenToIdentityManager() { @@ -93,17 +92,9 @@ class CreateSpaceViewModel @AssistedInject constructor( super.onCleared() } - companion object : MavericksViewModelFactory { + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() { - override fun create(viewModelContext: ViewModelContext, state: CreateSpaceState): CreateSpaceViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - - override fun initialState(viewModelContext: ViewModelContext): CreateSpaceState? { + override fun initialState(viewModelContext: ViewModelContext): CreateSpaceState { return CreateSpaceState( defaultRooms = mapOf( 0 to viewModelContext.activity.getString(R.string.create_spaces_default_public_room_name), diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt index 5e2537f587..d7bdf4f511 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt @@ -16,17 +16,16 @@ package im.vector.app.features.spaces.explore -import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext 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.VectorViewModel import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import kotlinx.coroutines.Dispatchers @@ -52,19 +51,11 @@ class SpaceDirectoryViewModel @AssistedInject constructor( ) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: SpaceDirectoryState): SpaceDirectoryViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: SpaceDirectoryState): SpaceDirectoryViewModel } - companion object : MavericksViewModelFactory { - override fun create(viewModelContext: ViewModelContext, state: SpaceDirectoryState): SpaceDirectoryViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() init { diff --git a/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheet.kt b/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheet.kt index 4f8d0b6c2f..bd6dec7c4b 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheet.kt @@ -30,8 +30,8 @@ import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.platform.ButtonStateView import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.core.utils.toast @@ -43,7 +43,8 @@ import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject -class SpaceInviteBottomSheet : VectorBaseBottomSheetDialogFragment(), SpaceInviteBottomSheetViewModel.Factory { +@AndroidEntryPoint +class SpaceInviteBottomSheet : VectorBaseBottomSheetDialogFragment() { interface InteractionListener { fun spaceInviteBottomSheetOnAccept(spaceId: String) @@ -57,22 +58,11 @@ class SpaceInviteBottomSheet : VectorBaseBottomSheetDialogFragment { + override fun create(initialState: SpaceInviteBottomSheetState): SpaceInviteBottomSheetViewModel } - companion object : MavericksViewModelFactory { - - override fun create(viewModelContext: ViewModelContext, state: SpaceInviteBottomSheetState): SpaceInviteBottomSheetViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() override fun handle(action: SpaceInviteBottomSheetAction) { when (action) { diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedActivity.kt index 762abf10cb..541d883405 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedActivity.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedActivity.kt @@ -28,8 +28,8 @@ import com.airbnb.mvrx.Success import com.airbnb.mvrx.viewModel import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.extensions.commitTransaction import im.vector.app.core.extensions.hideKeyboard @@ -40,24 +40,16 @@ import im.vector.app.databinding.ActivitySimpleLoadingBinding import im.vector.app.features.spaces.SpaceBottomSheetSettingsArgs import javax.inject.Inject +@AndroidEntryPoint class SpaceLeaveAdvancedActivity : VectorBaseActivity(), - SpaceLeaveAdvancedViewModel.Factory, ToolbarConfigurable { override fun getBinding(): ActivitySimpleLoadingBinding = ActivitySimpleLoadingBinding.inflate(layoutInflater) val leaveViewModel: SpaceLeaveAdvancedViewModel by viewModel() - @Inject lateinit var viewModelFactory: SpaceLeaveAdvancedViewModel.Factory @Inject lateinit var errorFormatter: ErrorFormatter - override fun create(initialState: SpaceLeaveAdvanceViewState) = viewModelFactory.create(initialState) - - override fun injectWith(injector: ScreenComponent) { - super.injectWith(injector) - injector.inject(this) - } - override fun showWaitingView(text: String?) { hideKeyboard() views.waitingView.waitingStatusText.isGone = views.waitingView.waitingStatusText.text.isNullOrBlank() diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt index 3d24cf6225..e9b75836e9 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt @@ -16,18 +16,17 @@ package im.vector.app.features.spaces.leave -import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.AppStateHandler +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.flow.launchIn @@ -127,17 +126,9 @@ class SpaceLeaveAdvancedViewModel @AssistedInject constructor( } @AssistedFactory - interface Factory { - fun create(initialState: SpaceLeaveAdvanceViewState): SpaceLeaveAdvancedViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: SpaceLeaveAdvanceViewState): SpaceLeaveAdvancedViewModel } - companion object : MavericksViewModelFactory { - override fun create(viewModelContext: ViewModelContext, state: SpaceLeaveAdvanceViewState): SpaceLeaveAdvancedViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() } diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomFragment.kt index 0512a478a1..5dbd35fc20 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomFragment.kt @@ -44,9 +44,8 @@ class SpaceAddRoomFragment @Inject constructor( private val spaceEpoxyController: AddRoomListController, private val roomEpoxyController: AddRoomListController, private val dmEpoxyController: AddRoomListController, - private val viewModelFactory: SpaceAddRoomsViewModel.Factory ) : VectorBaseFragment(), - OnBackPressed, AddRoomListController.Listener, SpaceAddRoomsViewModel.Factory { + OnBackPressed, AddRoomListController.Listener { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) = FragmentSpaceAddRoomsBinding.inflate(layoutInflater, container, false) @@ -55,9 +54,6 @@ class SpaceAddRoomFragment @Inject constructor( private val sharedViewModel: SpaceManageSharedViewModel by activityViewModel() - override fun create(initialState: SpaceAddRoomsState): SpaceAddRoomsViewModel = - viewModelFactory.create(initialState) - override fun getMenuRes(): Int = R.menu.menu_space_add_room private var saveNeeded = false diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewModel.kt index bf062ce0a8..8fa269d439 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewModel.kt @@ -19,16 +19,15 @@ package im.vector.app.features.spaces.manage import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import androidx.paging.PagedList -import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success -import com.airbnb.mvrx.ViewModelContext 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.VectorViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -55,10 +54,12 @@ class SpaceAddRoomsViewModel @AssistedInject constructor( ) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: SpaceAddRoomsState): SpaceAddRoomsViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: SpaceAddRoomsState): SpaceAddRoomsViewModel } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() + val updatableLiveSpacePageResult: UpdatableLivePageResult by lazy { session.getFilteredPagedRoomSummariesLive( roomSummaryQueryParams { @@ -132,16 +133,6 @@ class SpaceAddRoomsViewModel @AssistedInject constructor( } } - companion object : MavericksViewModelFactory { - override fun create(viewModelContext: ViewModelContext, state: SpaceAddRoomsState): SpaceAddRoomsViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } - fun canGoBack(): Boolean { val needToSave = selectionList.values.any { it } if (needToSave) { diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageActivity.kt index f45f9099bb..2dae088c2e 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageActivity.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageActivity.kt @@ -26,8 +26,8 @@ import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.withState import com.google.android.material.appbar.MaterialToolbar +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.addFragmentToBackstack import im.vector.app.core.extensions.commitTransaction import im.vector.app.core.extensions.hideKeyboard @@ -42,7 +42,6 @@ import im.vector.app.features.roomprofile.RoomProfileArgs import im.vector.app.features.roomprofile.alias.RoomAliasFragment import im.vector.app.features.roomprofile.permissions.RoomPermissionsFragment import kotlinx.parcelize.Parcelize -import javax.inject.Inject @Parcelize data class SpaceManageArgs( @@ -50,17 +49,12 @@ data class SpaceManageArgs( val manageType: ManageType ) : Parcelable +@AndroidEntryPoint class SpaceManageActivity : VectorBaseActivity(), - ToolbarConfigurable, - SpaceManageSharedViewModel.Factory { + ToolbarConfigurable { - @Inject lateinit var sharedViewModelFactory: SpaceManageSharedViewModel.Factory private lateinit var sharedDirectoryActionViewModel: RoomDirectorySharedActionViewModel - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - override fun getBinding(): ActivitySimpleLoadingBinding = ActivitySimpleLoadingBinding.inflate(layoutInflater) override fun getTitleRes(): Int = R.string.space_add_existing_rooms @@ -194,8 +188,6 @@ class SpaceManageActivity : VectorBaseActivity(), } } - override fun create(initialState: SpaceManageViewState) = sharedViewModelFactory.create(initialState) - override fun configure(toolbar: MaterialToolbar) { configureToolbar(toolbar) } diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsFragment.kt index 8e16784a6d..5fbac3bb6a 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsFragment.kt @@ -45,10 +45,8 @@ import java.util.concurrent.TimeUnit import javax.inject.Inject class SpaceManageRoomsFragment @Inject constructor( - private val viewModelFactory: SpaceManageRoomsViewModel.Factory, private val epoxyController: SpaceManageRoomsController ) : VectorBaseFragment(), - SpaceManageRoomsViewModel.Factory, OnBackPressed, SpaceManageRoomsController.Listener, Callback { @@ -107,8 +105,6 @@ class SpaceManageRoomsFragment @Inject constructor( super.onDestroyView() } - override fun create(initialState: SpaceManageRoomViewState) = viewModelFactory.create(initialState) - override fun invalidate() = withState(viewModel) { state -> epoxyController.setData(state) diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsViewModel.kt index d36e62db13..a1dd26a936 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsViewModel.kt @@ -16,18 +16,16 @@ package im.vector.app.features.spaces.manage -import androidx.lifecycle.viewModelScope -import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext 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.mvrx.runCatchingToAsync import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.session.coroutineScope @@ -56,19 +54,11 @@ class SpaceManageRoomsViewModel @AssistedInject constructor( } @AssistedFactory - interface Factory { - fun create(initialState: SpaceManageRoomViewState): SpaceManageRoomsViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: SpaceManageRoomViewState): SpaceManageRoomsViewModel } - companion object : MavericksViewModelFactory { - override fun create(viewModelContext: ViewModelContext, state: SpaceManageRoomViewState): SpaceManageRoomsViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() override fun handle(action: SpaceManageRoomViewAction) { when (action) { diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageSharedViewModel.kt index 133054236e..bedd1873e8 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageSharedViewModel.kt @@ -16,13 +16,12 @@ package im.vector.app.features.spaces.manage -import com.airbnb.mvrx.ActivityViewModelContext -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory -import com.airbnb.mvrx.ViewModelContext 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.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import org.matrix.android.sdk.api.session.Session @@ -33,19 +32,11 @@ class SpaceManageSharedViewModel @AssistedInject constructor( ) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: SpaceManageViewState): SpaceManageSharedViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: SpaceManageViewState): SpaceManageSharedViewModel } - companion object : MavericksViewModelFactory { - override fun create(viewModelContext: ViewModelContext, state: SpaceManageViewState): SpaceManageSharedViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() override fun handle(action: SpaceManagedSharedAction) { when (action) { diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsFragment.kt index 5e5eb50b87..c2ab015858 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsFragment.kt @@ -59,11 +59,9 @@ import javax.inject.Inject class SpaceSettingsFragment @Inject constructor( private val epoxyController: SpaceSettingsController, private val colorProvider: ColorProvider, - val viewModelFactory: RoomSettingsViewModel.Factory, private val avatarRenderer: AvatarRenderer, private val drawableProvider: DrawableProvider ) : VectorBaseFragment(), - RoomSettingsViewModel.Factory, SpaceSettingsController.Callback, GalleryOrCameraDialogHelper.Listener, OnBackPressed { @@ -81,10 +79,6 @@ class SpaceSettingsFragment @Inject constructor( override fun getMenuRes() = R.menu.vector_room_settings - override fun create(initialState: RoomSettingsViewState): RoomSettingsViewModel { - return viewModelFactory.create(initialState) - } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) setupToolbar(views.roomSettingsToolbar) diff --git a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleActivity.kt index 3b84a12bc1..1f08802137 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleActivity.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleActivity.kt @@ -22,6 +22,7 @@ import android.os.Bundle import androidx.core.view.isGone import androidx.core.view.isVisible import com.airbnb.mvrx.Mavericks +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R import im.vector.app.core.extensions.commitTransaction import im.vector.app.core.extensions.hideKeyboard @@ -30,6 +31,7 @@ import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivitySimpleLoadingBinding import im.vector.app.features.spaces.share.ShareSpaceBottomSheet +@AndroidEntryPoint class SpacePeopleActivity : VectorBaseActivity() { override fun getBinding() = ActivitySimpleLoadingBinding.inflate(layoutInflater) diff --git a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleFragment.kt index e1629d5dc1..6e14893f77 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleFragment.kt @@ -37,21 +37,16 @@ import im.vector.app.core.resources.DrawableProvider import im.vector.app.databinding.FragmentRecyclerviewWithSearchBinding import im.vector.app.features.roomprofile.members.RoomMemberListAction import im.vector.app.features.roomprofile.members.RoomMemberListViewModel -import im.vector.app.features.roomprofile.members.RoomMemberListViewState import io.reactivex.rxkotlin.subscribeBy import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import java.util.concurrent.TimeUnit import javax.inject.Inject class SpacePeopleFragment @Inject constructor( - private val viewModelFactory: SpacePeopleViewModel.Factory, - private val roomMemberModelFactory: RoomMemberListViewModel.Factory, private val drawableProvider: DrawableProvider, private val colorProvider: ColorProvider, private val epoxyController: SpacePeopleListController ) : VectorBaseFragment(), - SpacePeopleViewModel.Factory, - RoomMemberListViewModel.Factory, OnBackPressed, SpacePeopleListController.InteractionListener { private val viewModel by fragmentViewModel(SpacePeopleViewModel::class) @@ -66,14 +61,6 @@ class SpacePeopleFragment @Inject constructor( return true } - override fun create(initialState: SpacePeopleViewState): SpacePeopleViewModel { - return viewModelFactory.create(initialState) - } - - override fun create(initialState: RoomMemberListViewState): RoomMemberListViewModel { - return roomMemberModelFactory.create(initialState) - } - override fun invalidate() = withState(membersViewModel) { memberListState -> views.appBarTitle.text = getString(R.string.bottom_action_people) val memberCount = (memberListState.roomSummary.invoke()?.otherMemberIds?.size ?: 0) + 1 diff --git a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewModel.kt index efa7d97e9c..55d1dbe61e 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewModel.kt @@ -16,17 +16,15 @@ package im.vector.app.features.spaces.people -import androidx.lifecycle.viewModelScope -import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success -import com.airbnb.mvrx.ViewModelContext 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.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.raw.wellknown.getElementWellknown @@ -44,19 +42,11 @@ class SpacePeopleViewModel @AssistedInject constructor( ) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: SpacePeopleViewState): SpacePeopleViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: SpacePeopleViewState): SpacePeopleViewModel } - companion object : MavericksViewModelFactory { - override fun create(viewModelContext: ViewModelContext, state: SpacePeopleViewState): SpacePeopleViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() override fun handle(action: SpacePeopleViewAction) { when (action) { diff --git a/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewFragment.kt index eb02ed7c2d..7e08d7c924 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewFragment.kt @@ -49,10 +49,9 @@ data class SpacePreviewArgs( ) : Parcelable class SpacePreviewFragment @Inject constructor( - private val viewModelFactory: SpacePreviewViewModel.Factory, private val avatarRenderer: AvatarRenderer, private val epoxyController: SpacePreviewController -) : VectorBaseFragment(), SpacePreviewViewModel.Factory { +) : VectorBaseFragment() { private val viewModel by fragmentViewModel(SpacePreviewViewModel::class) lateinit var sharedActionViewModel: SpacePreviewSharedActionViewModel @@ -66,8 +65,6 @@ class SpacePreviewFragment @Inject constructor( sharedActionViewModel = activityViewModelProvider.get(SpacePreviewSharedActionViewModel::class.java) } - override fun create(initialState: SpacePreviewState) = viewModelFactory.create(initialState) - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) diff --git a/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewViewModel.kt index d71a4bef46..8d34ad94d8 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewViewModel.kt @@ -17,17 +17,16 @@ package im.vector.app.features.spaces.preview import androidx.lifecycle.viewModelScope -import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext 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.error.ErrorFormatter import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.Dispatchers @@ -58,19 +57,11 @@ class SpacePreviewViewModel @AssistedInject constructor( } @AssistedFactory - interface Factory { - fun create(initialState: SpacePreviewState): SpacePreviewViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: SpacePreviewState): SpacePreviewViewModel } - companion object : MavericksViewModelFactory { - override fun create(viewModelContext: ViewModelContext, state: SpacePreviewState): SpacePreviewViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() override fun handle(action: SpacePreviewViewAction) { when (action) { diff --git a/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceBottomSheet.kt b/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceBottomSheet.kt index bd69de0d95..6a98aa3cf8 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceBottomSheet.kt @@ -25,17 +25,17 @@ import androidx.core.view.isVisible import androidx.fragment.app.FragmentManager import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.core.utils.startSharePlainTextIntent import im.vector.app.databinding.BottomSheetSpaceInviteBinding import im.vector.app.features.invite.InviteUsersToRoomActivity import kotlinx.parcelize.Parcelize -import javax.inject.Inject -class ShareSpaceBottomSheet : VectorBaseBottomSheetDialogFragment(), ShareSpaceViewModel.Factory { +@AndroidEntryPoint +class ShareSpaceBottomSheet : VectorBaseBottomSheetDialogFragment() { @Parcelize data class Args( @@ -47,14 +47,6 @@ class ShareSpaceBottomSheet : VectorBaseBottomSheetDialogFragment(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: ShareSpaceViewState): ShareSpaceViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: ShareSpaceViewState): ShareSpaceViewModel } - companion object : MavericksViewModelFactory { - override fun create(viewModelContext: ViewModelContext, state: ShareSpaceViewState): ShareSpaceViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() init { val roomSummary = session.getRoomSummary(initialState.spaceId) diff --git a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsActivity.kt b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsActivity.kt index 02f25563b8..0efb6119af 100644 --- a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsActivity.kt +++ b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsActivity.kt @@ -20,8 +20,8 @@ import android.content.Context import android.content.Intent import com.airbnb.mvrx.viewModel import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.replaceFragment @@ -29,18 +29,13 @@ import im.vector.app.core.platform.SimpleFragmentActivity import org.matrix.android.sdk.api.session.terms.TermsService import javax.inject.Inject +@AndroidEntryPoint class ReviewTermsActivity : SimpleFragmentActivity() { @Inject lateinit var errorFormatter: ErrorFormatter - @Inject lateinit var viewModelFactory: ReviewTermsViewModel.Factory private val reviewTermsViewModel: ReviewTermsViewModel by viewModel() - override fun injectWith(injector: ScreenComponent) { - super.injectWith(injector) - injector.inject(this) - } - override fun initUiAndData() { super.initUiAndData() diff --git a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsViewModel.kt b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsViewModel.kt index 6a46061a31..9932efb11a 100644 --- a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsViewModel.kt @@ -15,16 +15,15 @@ */ package im.vector.app.features.terms -import androidx.lifecycle.viewModelScope -import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext 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.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.launch @@ -37,18 +36,11 @@ class ReviewTermsViewModel @AssistedInject constructor( ) : VectorViewModel(initialState) { @AssistedFactory - interface Factory { - fun create(initialState: ReviewTermsViewState): ReviewTermsViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: ReviewTermsViewState): ReviewTermsViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: ReviewTermsViewState): ReviewTermsViewModel? { - val activity: ReviewTermsActivity = (viewModelContext as ActivityViewModelContext).activity() - return activity.viewModelFactory.create(state) - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() lateinit var termsArgs: ServiceTermsArgs diff --git a/vector/src/main/java/im/vector/app/features/usercode/UserCodeActivity.kt b/vector/src/main/java/im/vector/app/features/usercode/UserCodeActivity.kt index db1bc3056a..7fa7a45131 100644 --- a/vector/src/main/java/im/vector/app/features/usercode/UserCodeActivity.kt +++ b/vector/src/main/java/im/vector/app/features/usercode/UserCodeActivity.kt @@ -28,8 +28,8 @@ import androidx.fragment.app.FragmentManager import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.withState +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.commitTransaction import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorBaseActivity @@ -37,15 +37,12 @@ import im.vector.app.core.utils.onPermissionDeniedSnackbar import im.vector.app.databinding.ActivitySimpleBinding import im.vector.app.features.matrixto.MatrixToBottomSheet import kotlinx.parcelize.Parcelize -import javax.inject.Inject import kotlin.reflect.KClass +@AndroidEntryPoint class UserCodeActivity : VectorBaseActivity(), - UserCodeSharedViewModel.Factory, MatrixToBottomSheet.InteractionListener { - @Inject lateinit var viewModelFactory: UserCodeSharedViewModel.Factory - val sharedViewModel: UserCodeSharedViewModel by viewModel() @Parcelize @@ -57,10 +54,6 @@ class UserCodeActivity : VectorBaseActivity(), override fun getCoordinatorLayout() = views.coordinatorLayout - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() { override fun onFragmentResumed(fm: FragmentManager, f: Fragment) { if (f is MatrixToBottomSheet) { @@ -147,9 +140,6 @@ class UserCodeActivity : VectorBaseActivity(), }.exhaustive } - override fun create(initialState: UserCodeState) = - viewModelFactory.create(initialState) - companion object { fun newIntent(context: Context, userId: String): Intent { return Intent(context, UserCodeActivity::class.java).apply { diff --git a/vector/src/main/java/im/vector/app/features/usercode/UserCodeSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/usercode/UserCodeSharedViewModel.kt index 2319eef6c4..64bcf9cead 100644 --- a/vector/src/main/java/im/vector/app/features/usercode/UserCodeSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/usercode/UserCodeSharedViewModel.kt @@ -16,15 +16,13 @@ package im.vector.app.features.usercode -import androidx.lifecycle.viewModelScope -import com.airbnb.mvrx.ActivityViewModelContext -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory -import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.R +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.core.resources.StringProvider import im.vector.app.features.createdirect.DirectRoomHelper @@ -45,15 +43,7 @@ class UserCodeSharedViewModel @AssistedInject constructor( private val directRoomHelper: DirectRoomHelper, private val rawService: RawService) : VectorViewModel(initialState) { - companion object : MavericksViewModelFactory { - override fun create(viewModelContext: ViewModelContext, state: UserCodeState): UserCodeSharedViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() init { val user = session.getUser(initialState.userId) @@ -66,8 +56,8 @@ class UserCodeSharedViewModel @AssistedInject constructor( } @AssistedFactory - interface Factory { - fun create(initialState: UserCodeState): UserCodeSharedViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: UserCodeState): UserCodeSharedViewModel } override fun handle(action: UserCodeActions) { diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt index daf5d73e8f..aed134816a 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt @@ -52,7 +52,6 @@ import javax.inject.Inject class UserListFragment @Inject constructor( private val userListController: UserListController, private val dimensionConverter: DimensionConverter, - val homeServerCapabilitiesViewModelFactory: HomeServerCapabilitiesViewModel.Factory ) : VectorBaseFragment(), UserListController.Callback { diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt index 457f8cbd9a..fde69ce9ba 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt @@ -17,14 +17,13 @@ package im.vector.app.features.userdirectory import androidx.lifecycle.asFlow -import com.airbnb.mvrx.ActivityViewModelContext -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext 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.extensions.exhaustive import im.vector.app.core.extensions.isEmail import im.vector.app.core.extensions.toggle @@ -61,20 +60,11 @@ class UserListViewModel @AssistedInject constructor(@Assisted initialState: User private val identityServerUsersSearch = MutableStateFlow("") @AssistedFactory - interface Factory { - fun create(initialState: UserListViewState): UserListViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: UserListViewState): UserListViewModel } - companion object : MavericksViewModelFactory { - - override fun create(viewModelContext: ViewModelContext, state: UserListViewState): UserListViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() private val identityServerListener = object : IdentityServiceListener { override fun onIdentityServerChange() { diff --git a/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorder.kt b/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorder.kt index 8a0f829f94..786920aa22 100644 --- a/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorder.kt +++ b/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorder.kt @@ -26,17 +26,15 @@ abstract class AbstractVoiceRecorder( context: Context, private val filenameExt: String ) : VoiceRecorder { - private val outputDirectory = File(context.cacheDir, "voice_records") + private val outputDirectory: File by lazy { + File(context.cacheDir, "voice_records").also { + it.mkdirs() + } + } private var mediaRecorder: MediaRecorder? = null private var outputFile: File? = null - init { - if (!outputDirectory.exists()) { - outputDirectory.mkdirs() - } - } - abstract fun setOutputFormat(mediaRecorder: MediaRecorder) abstract fun convertFile(recordedFile: File?): File? diff --git a/vector/src/main/java/im/vector/app/features/voice/VoicePlayerHelper.kt b/vector/src/main/java/im/vector/app/features/voice/VoicePlayerHelper.kt index f1b316c456..d2f7927d75 100644 --- a/vector/src/main/java/im/vector/app/features/voice/VoicePlayerHelper.kt +++ b/vector/src/main/java/im/vector/app/features/voice/VoicePlayerHelper.kt @@ -27,11 +27,9 @@ import javax.inject.Inject class VoicePlayerHelper @Inject constructor( context: Context ) { - private val outputDirectory = File(context.cacheDir, "voice_records") - - init { - if (!outputDirectory.exists()) { - outputDirectory.mkdirs() + private val outputDirectory: File by lazy { + File(context.cacheDir, "voice_records").also { + it.mkdirs() } } diff --git a/vector/src/main/java/im/vector/app/features/webview/VectorWebViewActivity.kt b/vector/src/main/java/im/vector/app/features/webview/VectorWebViewActivity.kt index ab8af20063..ab7913a99c 100644 --- a/vector/src/main/java/im/vector/app/features/webview/VectorWebViewActivity.kt +++ b/vector/src/main/java/im/vector/app/features/webview/VectorWebViewActivity.kt @@ -20,8 +20,8 @@ import android.content.Context import android.content.Intent import android.webkit.WebChromeClient import android.webkit.WebView -import androidx.annotation.CallSuper -import im.vector.app.core.di.ScreenComponent +import dagger.hilt.android.AndroidEntryPoint +import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivityVectorWebViewBinding import org.matrix.android.sdk.api.session.Session @@ -33,15 +33,14 @@ import javax.inject.Inject * It relies on the VectorWebViewClient * This class shouldn't be extended. To add new behaviors, you might create a new WebViewMode and a new WebViewEventListener */ +@AndroidEntryPoint class VectorWebViewActivity : VectorBaseActivity() { override fun getBinding() = ActivityVectorWebViewBinding.inflate(layoutInflater) - @Inject lateinit var session: Session - - @CallSuper - override fun injectWith(injector: ScreenComponent) { - session = injector.activeSessionHolder().getActiveSession() + @Inject lateinit var activeSessionHolder: ActiveSessionHolder + val session: Session by lazy { + activeSessionHolder.getActiveSession() } override fun initUiAndData() { diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt index 23f1cfe119..a31edfcb02 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt @@ -23,8 +23,8 @@ import androidx.core.view.isVisible import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import com.google.android.material.appbar.MaterialToolbar +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.addFragment import im.vector.app.core.platform.ToolbarConfigurable import im.vector.app.core.platform.VectorBaseActivity @@ -32,15 +32,12 @@ import im.vector.app.databinding.ActivityWidgetBinding import im.vector.app.features.widgets.permissions.RoomWidgetPermissionBottomSheet import im.vector.app.features.widgets.permissions.RoomWidgetPermissionViewEvents import im.vector.app.features.widgets.permissions.RoomWidgetPermissionViewModel -import im.vector.app.features.widgets.permissions.RoomWidgetPermissionViewState import org.matrix.android.sdk.api.session.events.model.Content import java.io.Serializable -import javax.inject.Inject +@AndroidEntryPoint class WidgetActivity : VectorBaseActivity(), - ToolbarConfigurable, - WidgetViewModel.Factory, - RoomWidgetPermissionViewModel.Factory { + ToolbarConfigurable { companion object { @@ -66,9 +63,6 @@ class WidgetActivity : VectorBaseActivity(), } } - @Inject lateinit var viewModelFactory: WidgetViewModel.Factory - @Inject lateinit var permissionsViewModelFactory: RoomWidgetPermissionViewModel.Factory - private val viewModel: WidgetViewModel by viewModel() private val permissionViewModel: RoomWidgetPermissionViewModel by viewModel() @@ -78,10 +72,6 @@ class WidgetActivity : VectorBaseActivity(), override fun getTitleRes() = R.string.room_widget_activity_title - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - override fun initUiAndData() { val widgetArgs: WidgetArgs? = intent?.extras?.getParcelable(Mavericks.KEY_ARG) if (widgetArgs == null) { @@ -133,14 +123,6 @@ class WidgetActivity : VectorBaseActivity(), } } - override fun create(initialState: WidgetViewState): WidgetViewModel { - return viewModelFactory.create(initialState) - } - - override fun create(initialState: RoomWidgetPermissionViewState): RoomWidgetPermissionViewModel { - return permissionsViewModelFactory.create(initialState) - } - private fun handleClose(event: WidgetViewEvents.Close) { if (event.content != null) { val intent = createResultIntent(event.content) diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt index 1cf3e367ea..20fae6e31a 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt @@ -17,16 +17,15 @@ package im.vector.app.features.widgets import android.net.Uri -import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success -import com.airbnb.mvrx.ViewModelContext 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.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.features.widgets.permissions.WidgetPermissionsHelper @@ -57,21 +56,11 @@ class WidgetViewModel @AssistedInject constructor(@Assisted val initialState: Wi IntegrationManagerService.Listener { @AssistedFactory - interface Factory { - fun create(initialState: WidgetViewState): WidgetViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: WidgetViewState): WidgetViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: WidgetViewState): WidgetViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() private val room = session.getRoom(initialState.roomId) private val widgetService = session.widgetService() diff --git a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionBottomSheet.kt b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionBottomSheet.kt index e7ee2aed1f..ae3028925a 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionBottomSheet.kt @@ -26,8 +26,8 @@ import android.view.ViewGroup import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.withState +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.withArgs import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.databinding.BottomSheetRoomWidgetPermissionBinding @@ -36,6 +36,7 @@ import im.vector.app.features.widgets.WidgetArgs import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject +@AndroidEntryPoint class RoomWidgetPermissionBottomSheet : VectorBaseBottomSheetDialogFragment() { @@ -49,10 +50,6 @@ class RoomWidgetPermissionBottomSheet : override val showExpanded = true - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - // Use this if you don't need the full activity view model var directListener: ((Boolean) -> Unit)? = null diff --git a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt index 71eaebbc91..f29e6d1928 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt @@ -15,14 +15,13 @@ */ package im.vector.app.features.widgets.permissions -import com.airbnb.mvrx.ActivityViewModelContext -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksViewModelFactory -import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.R +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map @@ -141,19 +140,9 @@ class RoomWidgetPermissionViewModel @AssistedInject constructor(@Assisted val in } @AssistedFactory - interface Factory { - fun create(initialState: RoomWidgetPermissionViewState): RoomWidgetPermissionViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: RoomWidgetPermissionViewState): RoomWidgetPermissionViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: RoomWidgetPermissionViewState): RoomWidgetPermissionViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() } diff --git a/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt b/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt index 92af25994a..8fb5b27376 100644 --- a/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt @@ -17,16 +17,15 @@ package im.vector.app.features.workers.signout import androidx.lifecycle.MutableLiveData -import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Async -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext 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.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel @@ -68,21 +67,11 @@ class ServerBackupStatusViewModel @AssistedInject constructor(@Assisted initialS VectorViewModel(initialState), KeysBackupStateListener { @AssistedFactory - interface Factory { - fun create(initialState: ServerBackupStatusViewState): ServerBackupStatusViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: ServerBackupStatusViewState): ServerBackupStatusViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: ServerBackupStatusViewState): ServerBackupStatusViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() // Keys exported manually val keysExportedToFile = MutableLiveData() diff --git a/vector/src/main/java/im/vector/app/features/workers/signout/SignOutBottomSheetDialogFragment.kt b/vector/src/main/java/im/vector/app/features/workers/signout/SignOutBottomSheetDialogFragment.kt index 7f089082a2..5d38dac15f 100644 --- a/vector/src/main/java/im/vector/app/features/workers/signout/SignOutBottomSheetDialogFragment.kt +++ b/vector/src/main/java/im/vector/app/features/workers/signout/SignOutBottomSheetDialogFragment.kt @@ -31,8 +31,8 @@ import com.airbnb.mvrx.withState import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R -import im.vector.app.core.di.ScreenComponent import im.vector.app.core.dialogs.ExportKeysDialog import im.vector.app.core.extensions.queryExportKeys import im.vector.app.core.extensions.registerStartForActivityResult @@ -42,12 +42,11 @@ import im.vector.app.features.crypto.keysbackup.setup.KeysBackupSetupActivity import im.vector.app.features.crypto.recover.BootstrapBottomSheet import im.vector.app.features.crypto.recover.SetupMode import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState -import javax.inject.Inject // TODO this needs to be refactored to current standard and remove legacy +@AndroidEntryPoint class SignOutBottomSheetDialogFragment : - VectorBaseBottomSheetDialogFragment(), - SignoutCheckViewModel.Factory { + VectorBaseBottomSheetDialogFragment() { var onSignOut: Runnable? = null @@ -59,19 +58,8 @@ class SignOutBottomSheetDialogFragment : isCancelable = true } - @Inject - lateinit var viewModelFactory: SignoutCheckViewModel.Factory - - override fun create(initialState: SignoutCheckViewState): SignoutCheckViewModel { - return viewModelFactory.create(initialState) - } - private val viewModel: SignoutCheckViewModel by fragmentViewModel(SignoutCheckViewModel::class) - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - override fun onResume() { super.onResume() viewModel.refreshRemoteStateIfNeeded() diff --git a/vector/src/main/java/im/vector/app/features/workers/signout/SignOutUiWorker.kt b/vector/src/main/java/im/vector/app/features/workers/signout/SignOutUiWorker.kt index c5fa130d9b..59ea37036c 100644 --- a/vector/src/main/java/im/vector/app/features/workers/signout/SignOutUiWorker.kt +++ b/vector/src/main/java/im/vector/app/features/workers/signout/SignOutUiWorker.kt @@ -20,14 +20,14 @@ import androidx.fragment.app.FragmentActivity import com.google.android.material.dialog.MaterialAlertDialogBuilder import im.vector.app.R import im.vector.app.core.extensions.cannotLogoutSafely -import im.vector.app.core.extensions.vectorComponent +import im.vector.app.core.extensions.singletonEntryPoint import im.vector.app.features.MainActivity import im.vector.app.features.MainActivityArgs class SignOutUiWorker(private val activity: FragmentActivity) { fun perform() { - val session = activity.vectorComponent().activeSessionHolder().getSafeActiveSession() ?: return + val session = activity.singletonEntryPoint().activeSessionHolder().getSafeActiveSession() ?: return if (session.cannotLogoutSafely()) { // The backup check on logout flow has to be displayed if there are keys in the store, and the keys backup state is not Ready val signOutDialog = SignOutBottomSheetDialogFragment.newInstance() diff --git a/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt b/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt index 057d9e31f8..4daaef6fe1 100644 --- a/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/workers/signout/SignoutCheckViewModel.kt @@ -17,18 +17,17 @@ package im.vector.app.features.workers.signout import android.net.Uri -import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Async -import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized -import com.airbnb.mvrx.ViewModelContext 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.extensions.exhaustive import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel @@ -66,21 +65,11 @@ class SignoutCheckViewModel @AssistedInject constructor( } @AssistedFactory - interface Factory { - fun create(initialState: SignoutCheckViewState): SignoutCheckViewModel + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: SignoutCheckViewState): SignoutCheckViewModel } - companion object : MavericksViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: SignoutCheckViewState): SignoutCheckViewModel? { - val factory = when (viewModelContext) { - is FragmentViewModelContext -> viewModelContext.fragment as? Factory - is ActivityViewModelContext -> viewModelContext.activity as? Factory - } - return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") - } - } + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() init { session.cryptoService().keysBackupService().addListener(this) diff --git a/vector/src/main/res/menu/menu_devtools.xml b/vector/src/main/res/menu/menu_devtools.xml index d6f97292d6..dcef953d8f 100644 --- a/vector/src/main/res/menu/menu_devtools.xml +++ b/vector/src/main/res/menu/menu_devtools.xml @@ -9,6 +9,7 @@ tools:visible="true" app:showAsAction="ifRoom" android:icon="@drawable/ic_edit" + app:iconTint="?vctr_content_secondary" android:title="@string/edit" /> \ No newline at end of file diff --git a/vector/src/main/res/values-ar/strings.xml b/vector/src/main/res/values-ar/strings.xml index 8483f4d026..49d98fabb2 100644 --- a/vector/src/main/res/values-ar/strings.xml +++ b/vector/src/main/res/values-ar/strings.xml @@ -1330,4 +1330,10 @@ أمّن النسخ الاختياطي بعبارة مرور. تصدير المفاتيح يدويًا (متقدم) + أرسل سجل طلبات مشاركة المفاتيح + ابدأ مقابلة صوتية + ابدأ مقابلة فيديو + %1$s فعّل تشفير طرف لطرف (لم يُتعرف على خوارزمية %2$s). + عطلتّ تشفير طرف لطرف. + %1$s فعّل تشفير طرف لطرف. \ No newline at end of file diff --git a/vector/src/main/res/values-cs/strings.xml b/vector/src/main/res/values-cs/strings.xml index 42e1e3963f..b509b3a9e8 100644 --- a/vector/src/main/res/values-cs/strings.xml +++ b/vector/src/main/res/values-cs/strings.xml @@ -1916,7 +1916,7 @@ %1$d osoby %1$d osob - Nahrání + Nahrané Opustit místnost Opouštím místnost… Správci @@ -2179,7 +2179,7 @@ Odkaz %1$s Vás převede na jiný site: %2$s. \n \nOpravdu chcete pokračovat\? - Nemohli jsme vytvořit Vaši DM. Prosím, zkontrolujte uživatele, které chcete pozvat, a zkuste znovu. + Nemohli jsme vytvořit vaši přímou zprávu. Prosím, zkontrolujte uživatele, které chcete pozvat, a zkuste znovu. Přidat členy POZVAT Zvu uživatele… @@ -3024,7 +3024,7 @@ Nevypadá to jako platná e-mailová adresa Otevřít nastavení objevování Vyhledávání podle jména, ID nebo emailu - Vytvořit nový prostor + Založit nový prostor Každý může prostor najít a připojit se k němu Adresa prostoru Kdo má přístup\? diff --git a/vector/src/main/res/values-da/strings.xml b/vector/src/main/res/values-da/strings.xml index c6c854a128..077bf0efda 100644 --- a/vector/src/main/res/values-da/strings.xml +++ b/vector/src/main/res/values-da/strings.xml @@ -541,4 +541,5 @@ Er du sikker? Du har sendt et klistermærke. %1$s har sendt et klistermærke. Du har sendt et billede. + Forbind denne email med din konto \ No newline at end of file diff --git a/vector/src/main/res/values-de/strings.xml b/vector/src/main/res/values-de/strings.xml index 73f0a9eb70..4312898f17 100644 --- a/vector/src/main/res/values-de/strings.xml +++ b/vector/src/main/res/values-de/strings.xml @@ -1072,7 +1072,7 @@ Bestimmt das Berechtigungslevel des Benutzers Setzt Berechtigungen des Benutzers zurück Lädt Benutzer mit angegebener Kennung in den aktuellen Raum ein - Tritt dem Raum mit angegebenen Alias bei + Raum mit angegebener Adresse beitreten Verlasse Raum Raumthema ändern Entfernt die Person angegebener ID @@ -1741,7 +1741,7 @@ Ungelesene Nachrichten Privat oder in Gruppen mit Leuten chatten Halte Gespräche mittels Verschlüsselung privat - Beginne + Los geht\'s Wähle einen Server Genau wie bei E-Mails haben Accounts ein Zuhause, auch wenn du mit jedem kommunizieren kannst Folge Millionen Anderen kostenlos auf dem größten öffentlichen Server @@ -1755,7 +1755,7 @@ Stimme den Nutzungsbedingungen des Identitätsservers (%s) zu, um zu erlauben per E-Mail oder Telefonnummer gefunden zu werden. Zu teilende Daten nicht verarbeitbar Erweitere & individualisiere dein Benutzererlebnis - Verbinde mit %1$s + Mit %1$s verbinden Mit Element Matrix Services verbinden Mit einem benutzerdefinierten Server verbinden Bei %1$s anmelden @@ -1818,7 +1818,7 @@ Die Anwendung kann sich nicht bei diesem Home-Server anmelden. Der Home-Server unterstützt die folgenden Anmeldetypen: %1$s. \n \nMöchtest du dich mit einem Webclient anmelden\? - Eine Bestätigungsmail wird an dich gesendet, um dein neues Passwort zu bestätigen. + Dir wird eine Bestätigungsmail gesendet, um dein neues Passwort zu bestätigen. Weiter Du wurdest von allen Sitzungen abgemeldet und erhältst keine Push-Benachrichtigungen mehr. Um Benachrichtigungen wieder zu aktivieren, melde dich auf jedem Gerät erneut an. Warnung @@ -1831,7 +1831,7 @@ Weiter Internationale Telefonnummern müssen mit \'+\' beginnen Die Telefonnummer scheint ungültig zu sein. Bitte prüfen - Anmelden bei %1$s + Registrieren bei %1$s Benutzername Weiter Warnung @@ -2175,7 +2175,7 @@ Verifiziere alle deine Sitzungen, um sicherzustellen, dass dein Konto & deine Nachrichten sicher sind Bestätige neue Anmeldung zu deinem Konto: %1$s Verifiziere manuell mit einem Text - Verifiziere Anmeldung + Anmeldung verifizieren Verifiziere interaktiv mit Emojis Bestätige deine Identität, indem du diesen Login von einer deiner anderen Sitzungen verifizierst, um Zugriff auf deine verschlüsselten Nachrichten zu erhalten. Als vertraut markieren @@ -2313,7 +2313,7 @@ Bitte akzeptiere zuerst die AGB des Identitätsservers in den Einstellungen. Deiner Privatsphäre wegen unterstützt ${app_name} nur das Senden gehashter E-Mail-Adressen und Telefonnummern. Die Assoziierung ist fehlgeschlagen. - Für diese Kennung gibt es aktuell keine Assoziierung. + Für diese Kennung gibt es aktuell keine Zuordnung. Dein Home-Server (%1$s) schlägt %2$s als Identitätsserver vor Benutze %1$s Alternativ kannst du die URL eines beliebigen anderen Identitätsservers angeben @@ -2620,7 +2620,7 @@ Sendet Konfetti 🎉 Nachricht mit Schnee senden Sendet die nachfolgende Nachricht mit Konfetti - Nachrichtenverlauf bereinigen + Verlauf löschen Einmalanmeldung Anmelden mit %s Registrieren mit %s @@ -2658,7 +2658,7 @@ Zurückrufen Dieser Anruf wurde beendet %1$s hat diesen Anruf abgelehnt - Du hast diesen Anruf %1$s abgelehnt + Du hast diesen Anruf %s abgelehnt Du nimmst zur Zeit an diesem Anruf teil %1$s hat einen Anruf gestartet Du hast einen Anruf gestartet @@ -2696,7 +2696,7 @@ Standard-Vertrauensstufe Ausgewählt Video - Dieser Raum enthält einen ungesendeten Entwurf + enthält einen ungesendeten Entwurf Einige Nachrichten wurden nicht gesendet Avatar löschen Avatar ändern @@ -2800,7 +2800,7 @@ Einen Space erstellen Nur für mich Welche Art von Space möchtest du erstellen\? - Space hinzufügen + Space erstellen Space erstellen Jeder, der sich in einem Space mit diesem Raum befindet, kann diesen Raum finden und ihm beitreten. Nur die Administratoren des Raums können diesen zu einem Space hinzufügen. Nur Space-Mitglieder @@ -2846,8 +2846,8 @@ Für weitere Infos kontaktiere bitte die Administration des Homeservers Dein Homeserver scheint Spaces noch nicht zu unterstützen Räume hinzufügen - Du bist der Admin von diesem Space. Stelle vor dem Verlassen sicher, dass du die Adminrechte an jemand anderen übergibst. - Dieser Raum ist nicht öffentlich. Du wirst ihn ohne Einladung nicht wieder betreten können. + Du bist der einzige Admin von diesem Space. Wenn du ihn verlässt, hat niemand Kontrolle über ihn. + Du wirst diesen Raum ohne erneute Einladung nicht betreten können. Du bist die einzige Person hier. Wenn du den Space verlässt, ist er für immer verloren (eine lange Zeit). Einladen in %s Privater Space @@ -2891,12 +2891,12 @@ Klicke, um die Aufnahme zu starten oder stoppen %1$ds übrig Zum Aufnehmen drücken, zum Senden loslassen - Aufgenommene Sprachnachricht löschen + Aufnahme löschen Sprachnachricht wird aufgenommen Sprachnachricht pausieren Sprachnachricht abspielen Sprachnachricht einrasten - Sprachnachricht starten + Sprachnachricht aufnehmen Dieser Raum verwendet die Raumversion %s, die von diesem Heimserver als instabil markiert ist. Du benötigst die Berechtigung, um einen Raum upzugraden Übergeordneten Space automatisch updaten @@ -3027,4 +3027,25 @@ Kicken entfernt die Person aus dem Space \n \nUm sie für immer zu entfernen, solltest du sie bannen. + Aufnahme beenden + Welche Räume willst du verlassen\? + Einrichtung beenden + Nicht verfügbar + Offline + Online + %1$s Klicke zum Zurückkehren + Nicht erreicht + Nachricht mit ( ͡° ͜ʖ ͡°) beginnen + Zeigt Information über die Person an + Ändert dein Profilbild im aktuellen Raum + Ändert das Raumbild + Ändert deinen Nicknamen für diesen Raum + Ändert den Raumnamen + Entblockt eine Person und zeigt deren Nachrichten wieder an + Blockiert eine Person und versteckt deren Nachrichten + Jeder kann den Space finden und beitreten + Du kannst deine Benachrichtigungen in den %1$s verwalten. + Beachte, dass Benachrichtigungen zu Erwähnungen und Schlüsselwörtern in verschlüsselten Räumen momentan nicht verfügbar sind. + Wähle die Berechtigungen der Rollen aus + Rollen deren Berechtigungen einsehen und bearbeiten. \ No newline at end of file diff --git a/vector/src/main/res/values-fa/strings.xml b/vector/src/main/res/values-fa/strings.xml index ea798cac52..c2b55bb06c 100644 --- a/vector/src/main/res/values-fa/strings.xml +++ b/vector/src/main/res/values-fa/strings.xml @@ -2260,7 +2260,7 @@ اخراج کاربر با شناسه داده شده تنظیم موضوع اتاق ترک اتاق - با نام مستعار داده‌شده به اتاق بپیوندید + به اتاق با نشانی داده شده می‌پیوندد کاربر با شناسه داده شده را به این اتاق دعوت می کند کاربر با شناسه داده شده را غیر‌فعال می‌کند سطح قدرت کاربر را تعریف می‌کند diff --git a/vector/src/main/res/values-in/strings.xml b/vector/src/main/res/values-in/strings.xml index 8b9016b6c3..c087680eb4 100644 --- a/vector/src/main/res/values-in/strings.xml +++ b/vector/src/main/res/values-in/strings.xml @@ -2628,7 +2628,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan. Bukan kode QR Matrix yang valid Undangan terkirim ke %1$s dan %2$s Undangan terkirim ke %1$s - 🔐️ Bergabung dengan saya di ${app_name] + 🔐️ Bergabung dengan saya di ${app_name} Hi, bicara dengan saya di ${app_name}: %s Undang teman Undang Pengguna diff --git a/vector/src/main/res/values-it/strings.xml b/vector/src/main/res/values-it/strings.xml index 8d323c56e5..663e186ddf 100644 --- a/vector/src/main/res/values-it/strings.xml +++ b/vector/src/main/res/values-it/strings.xml @@ -1071,7 +1071,7 @@ Imposta i poteri di un utente Rimuove il rango di operatore dall\'utente con l\'ID specificato Invita l\'utente con l\'ID specificato nella stanza corrente - Entra nella stanza con un determinato nome + Entra nella stanza con l\'indirizzo scelto Esci dalla stanza Imposta l\'argomento della stanza Butta fuori l\'utente con l\'ID specificato diff --git a/vector/src/main/res/values-ko/strings.xml b/vector/src/main/res/values-ko/strings.xml index 3adf75e722..f691609567 100644 --- a/vector/src/main/res/values-ko/strings.xml +++ b/vector/src/main/res/values-ko/strings.xml @@ -162,13 +162,13 @@ 음성 통화 시작하기 영상 통화 시작하기 정말로 %s님과 새 대화를 시작하시겠습니까\? - 정말 음성통화를 시작하시겠어요? - 정말 영상통화를 시작하시겠어요? + 정말 음성통화를 시작하시겠습니까\? + 정말 영상통화를 시작하시겠습니까\? 파일 보내기 스티커 보내기 - 사진이나 영상 찍기 - 사진 찍기 - 영상 찍기 + 사진이나 영상 촬영 + 사진 촬영 + 영상 촬영 로그인 계정 만들기 제출하기 @@ -193,7 +193,7 @@ 머물기 넘기기 완료 - 정말 로그아웃하시겠어요\? + 정말 로그아웃하시겠습니까\? 읽음으로 표시 ${app_name}이 연락처에 접근할 수 없게 되어 있습니다 @@ -246,7 +246,7 @@ 무시하고 통화 수락하기 중단 - 무시 + 차단 세계 검색 즐겨찾기 필터 사람 필터 @@ -348,7 +348,7 @@ 미디어 연결 실패 카메라를 초기화할 수 없습니다 다른 곳에서 전화 응답 - 사진이나 영상 찍기 + 사진이나 영상 촬영 영상을 촬영할 수 없음 정보 첨부 파일을 보내고 저장하려면 ${app_name}은 영상과 사진 보관함에 접근하는 권한이 필요합니다. @@ -432,8 +432,8 @@ 일반 사용자로 재 설정 중재자로 하기 관리자로 하기 - 이 사용자의 모든 메시지 숨기기 - 이 사용자의 모든 메시지 보이기 + 차단 + 차단 해제 사용자 ID, 이름 혹은 이메일 언급 기기 목록 보이기 @@ -475,7 +475,7 @@ 신뢰함 신뢰하지 않음 로그아웃 - 무시하기 + 무시 핑거프린트 (%s): 원격 서버의 ID를 확인할 수 없습니다. 이는 누군가가 당신의 트래픽을 악의적으로 가로채고 있거나, 휴대 전화가 원격 서버에서 제공한 인증서를 신뢰하지 않는 것입니다. @@ -665,7 +665,7 @@ 미디어 캐시 지우기 사용자 설정 알림 - 차단한 사용자 + 차단된 사용자 기타 고급 암호화 @@ -747,7 +747,7 @@ 비밀번호 갱신 실패 비밀번호가 올바르지 않습니다 당신의 비밀번호가 갱신되었습니다 - %s님의 모든 메시지를 보이겠습니까\? + %s님의 모든 메시지를 표시하시겠습니까\? \n \n이 동작은 앱을 다시 시작하고 일정 시간이 걸릴 수 있습니다. 비밀번호가 맞지 않음 @@ -1411,19 +1411,19 @@ 이 내용 신고하기 이 내용을 신고하는 이유 신고 - 사용자 출입 금지 + 사용자 차단 내용 신고됨 이 내용을 신고했습니다. \n -\n이 사용자의 내용을 더 이상 보고 싶지 않다면, 사용자를 차단하거나 메시지를 감출 수 있습니다 +\n이 사용자의 내용을 더 이상 보고 싶지 않다면, 사용자를 차단하거나 메시지를 감출 수 있습니다. 스팸 문자로 신고됨 이 내용을 스팸 메일로 신고했습니다. \n -\n이 사용자의 내용을 더 이상 보고 싶지 않다면, 사용자를 차단하거나 메시지를 감출 수 있습니다 +\n이 사용자의 내용을 더 이상 보고 싶지 않다면, 사용자를 차단하거나 메시지를 감출 수 있습니다. 부적절한 문자로 신고됨 이 내용을 부적절한 문자로 신고했습니다. \n -\n이 사용자의 내용을 더 이상 보고 싶지 않다면, 사용자를 차단하거나 메시지를 감출 수 있습니다 +\n이 사용자의 내용을 더 이상 보고 싶지 않다면, 사용자를 차단하거나 메시지를 감출 수 있습니다. ${app_name}은 종단간 키를 디스크에 저장하려면 권한이 필요합니다. \n \n키를 수동으로 내보내려면 다음 팝업에서 접근을 허용해주세요. @@ -1483,4 +1483,37 @@ 일시 정지 재생 음성 메시지를 보내려면 마이크 권한을 허용해주세요. + 삭제된 메시지 + 복사 + 차단 해제 + 차단한 사용자가 없습니다 + 차단 + 차단을 해제하고, 차단 해제한 사용자의 메시지를 다시 표시합니다 + 사용자를 차단하고, 차단한 사용자의 모든 메시지를 숨깁니다 + 차단 해제한 사용자의 모든 메시지가 표시됩니다. + 차단 해제 + 타임라인 + 읽지 않은 메시지 + 항상 묻기 + 현재 세션 + + %d개의 활성 세션 + + 이 세션에서 로그아웃 + 세션 관리 + 모든 세션 보기 + 활성 세션 + 보안 설정 + ${app_name}을 2분 동안 사용하지 않으면 PIN을 사용하도록 설정합니다. + 2분 후 PIN 잠금 + 방 이름이나 메시지 내용같은 자세한 정보를 표시합니다. + 알림에 내용 표시 + 설정된 PIN을 변경합니다 + PIN 변경 + PIN을 재설정하고 싶다면 PIN 분실을 눌러 로그아웃 후 재설정을 진행할 수 있습니다. + 지문 인식이나 얼굴 인식과 같은 생체 인증을 활성화합니다. + 생체 인증 활성화 + PIN 활성화 + PIN이나 생체 인증으로 보안 접근을 사용합니다. + 보안 접근 \ No newline at end of file diff --git a/vector/src/main/res/values-ru/strings.xml b/vector/src/main/res/values-ru/strings.xml index 3a2491850d..ad395de686 100644 --- a/vector/src/main/res/values-ru/strings.xml +++ b/vector/src/main/res/values-ru/strings.xml @@ -1120,7 +1120,7 @@ Этот разговор продолжается здесь Эта комната является продолжением другого разговора Нажмите здесь для просмотра старых сообщений - Присоединиться к комнате с указанным псевдонимом + Присоединиться к комнате с указанным адресом Для исправления управления приложениями Matrix Из-за отсутствия разрешений это действие невозможно. diff --git a/vector/src/main/res/values-uk/strings.xml b/vector/src/main/res/values-uk/strings.xml index 06906804df..b6d52c59e4 100644 --- a/vector/src/main/res/values-uk/strings.xml +++ b/vector/src/main/res/values-uk/strings.xml @@ -991,7 +991,7 @@ Визначити рівень повноважень користувача Скинути рівень доступу користувача із вказаним ID Запросити користувача із вказаним ID до поточної кімнати - Приєднатися до кімнати із вказаним псевдонімом + Приєднує до кімнати із вказаною адресою Вийти з кімнати Встановити тему кімнати Копнути користувача із вказаним ID @@ -2860,4 +2860,15 @@ Не застосовуйте пароль облікового запису повторно. Введіть %s ще раз, щоб підтвердити. Захистіть та розблокуйте зашифровані повідомлення та перевіряйте довіреність за допомогою %s. + Політика + Перестає нехтувати користувача, показує їхні подальші повідомлення + Нехтує користувача, ховає їхні повідомлення від вас + Сховати політику ідентифікації сервера + Показати політику ідентифікації сервера + Показує відомості про користувача + Змінює ваш аватар лише у поточній кімнаті + Змінює аватар поточної кімнати + Змінює ваше показуване ім\'я лише у поточній кімнаті + Установлює назву кімнати + Сервер ідентифікації не надав жодних правил \ No newline at end of file diff --git a/vector/src/main/res/values-vi/strings.xml b/vector/src/main/res/values-vi/strings.xml index ab164d62c7..498ee9fbbb 100644 --- a/vector/src/main/res/values-vi/strings.xml +++ b/vector/src/main/res/values-vi/strings.xml @@ -1543,4 +1543,106 @@ Cuộc gọi âm thanh với %s Cuộc gọi video với %s Cuộc gọi đang reo… + Ai có thể tìm và tham gia không gian + Truy cập không gian + Ai có quyền truy cập\? + Không thể hoàn thành xác nhận email. Vui lòng kiểm tra email của bạn và bấm vào đường liên kết trong đó. Một khi đã xong, bấm tiếp tục. + Vui lòng kiểm tra email và bấm vào liên kết trong đó. Một khi xong, bấm tiếp tục. + Chế độ tiết kiệm dữ liệu sẽ dừng việc đưa ra các thông báo đang có mặt và đang gõ. + ${app_name} cần phải giữ kết nối mạng liên tục dưới nền để đạt hiệu quả thông báo cao nhất: +\nỞ màn hình tiếp theo ban sẽ được hỏi để cho phép ${app_name} hoạt động liên tục dưới nền, hãy nhấn cho phép. + ${app_name} sẽ chạy dưới nền để quản lý các thông báo của bạn một cách chính xác và riêng tư. Điều này sẽ có thể ảnh hưởng đến thời lượng pin. + Thông báo riêng tư + Sử dụng trình quản lý chung để quản lý bot, các cầu nối, widget và các gói nhãn dán. +\nTrình quản lý chung sẽ nhận được dữ liệu hiệu chỉnh, và sẽ có thể điều chỉnh các widget, gửi lời mời vào phòng và thiết lập các mốc quyền lợi theo ý bạn. + ${app_name} sẽ đồng bộ hóa dưới nền trong một khoảng thời gian nhất định (có thể điều chỉnh thời gian). +\nViệc này sẽ làm ảnh hưởng tới khả năng thu phát và sử dụng pin, sẽ xuất hiện một thông báo cho biết lúc nào ${app_name} đang hoạt động. + ${app_name} sẽ đồng bộ dưới nền và sẽ sử dụng các tài nguyên như pin một cách tiết kiệm. +\nTùy vào các tài nguyên có sẵn trên thiết bị, việc đồng bộ hóa có thể sẽ bị ngăn cản bởi hệ điều hành. + Nếu người dùng để thiết bị đã tháo sạc và đang chờ trong khoảng thời gian, với màn hình tắt, thiết bị sẽ khởi động chế độ Doze. Việc này ngăn cản các ứng dụng sử dụng mạng và dừng lại các hoạt động đồng bộ hóa, kể cả các báo thức. + Giới hạn dưới nền đã được bật cho ${app_name}. +\nCác hoạt động của ứng dụng sẽ bị giới hạn nặng nề khi chạy dưới nền, điều này có thể ảnh hưởng đến cách thông báo của ứng dụng. +\n%1$s + Các giới hạn dưới nền đã được tắt cho ${app_name}. Bài kiểm tra này sẽ được chạy khi sử dụng dữ liệu di động (không Wi-Fi). +\n%1$s + Kiểm tra giới hạn dưới nền + Bật khởi động cùng hệ thống + Dịch vụ sẽ không khởi động sau khi thiết bị đã khởi động lại, bạn sẽ không nhận được bất kỳ thông báo nào cho tới khi ${app_name} được mở một lần. + Dịch vụ sẽ bắt đầu ngay khi thiết bị được khởi động lại. + Khởi động cùng hệ thống + Dịch vụ không thể khởi động + Dịch vụ đã bị đóng và tự động khởi động lại. + Tụ động khởi động lại dịch vụ thông báo + Bắt đầu + Dịch vụ thông báo đang không hoạt động: +\nHãy thử khởi động lại ứng dụng. + Dịch vụ thông báo đang chạy. + Dịch vụ thông báo + Thông báo đã được bấm! + Hãy bấm vào thông báo. Nếu bạn không thấy bất kỳ thông báo nào, vui lòng kiểm tra cài đặt hệ thống. + Hiển thị thông báo + Bạn đang xem thông báo này! Bấm tôi đi! + Đẩy không thành công. Giải pháp có thể là cài đặt lại ứng dụng. + Ứng dụng đang được ĐẨY + Ứng dụng đang chờ được ĐẨY + Đẩy thử + Khai báo token FCM với máy chủ không thành công: +\n%1$s + Đăng ký token FCM thành công tới máy chủ. + Đăng ký token + Thêm tài khoản + [%1$s +\nLỗi này đã vượt khỏi tầm kiểm soát của ${app_name}. Không phát hiện thấy tài khoản Google trên thiết bị. Vui lòng thêm một tài khoản. + [%1$s +\nLỗi này đã vượt ra khỏi tầm kiểm soát của ${app_name}. Nó có thể xảy ra vì vài lý do. Có thể nó sẽ hoạt động nếu bạn thử lại sau, bạn cũng có thể kiểm rằng các dịch vụ của Google Play không bị giới hạn quyền sử dụng data trong cài đặt hệ thống, hoặc đồng hồ trên thiết bị của bạn bị sai, hoặc là do bản ROM custom của bạn. + [%1$s +\nLỗi này đã vượt ra khỏi tầm kiểm soát của ${app_name} và theo như Google thông báo, lỗi này chỉ ra rằng thiết bị đã có quá nhiều ứng dụng được đăng ký với FCM. Lỗi này chỉ xảy ra khi có quá nhiều ứng dụng hoạt động, vì vậy có thể sẽ không ảnh hưởng đến trải nghiệm người dùng cơ bản. + Lấy token FCM thất bại: +\n%1$s + Lấy token FCM thành công: +\n%1$s + Token Firebase + Sửa dịch vụ của Google Play + ${app_name} sử dụng Dịch vụ của Google Play nhằm đưa ra cái thông báo đẩy nhưng có vẻ nó đã không được căn chỉnh đúng cách: +\n%1$s + Dịch vụ của Google Play APK đang hoạt động và đã được nâng cấp lên phiên bản mới nhất. + Kiểm tra dịch vụ của Google Play + Kiểm tra các cài đặt + Không thể thiết lập các thay đổi nâng cao, vui lòng thử lại. + Bạn đã tắt một vài thông báo trong cài đặt nâng cao. + Một vài thông báo tin nhắn đã được thiết lập thành im lặng (sẽ thông báo nhưng không có âm thanh). + Một hoặc nhiều phép thử đã thất bại, vui lòng gửi một bản báo cáo lỗi để giúp chúng tôi điều tra. + Một hoặc nhiều phép thử đã thất bại, hãy thử (các) phương pháp được đề xuất sau. + Kết quả phân tích cơ bản là OK. Nếu bạn vẫn không thể nhận thông báo, vui lòng gửi cho chúng tôi một bản báo cáo lỗi để giúp chúng tôi điều tra. + Đang chạy… (%1$d of %2$d) + Chạy thử + Chuẩn đoán khắc phục sự cố + Bật thông báo qua email cho %s + Để được nhận thông báo qua email, hãy liên kết một địa chỉ mail với tài khoản Matrix của bạn + Thông báo qua email + ID không hợp lệ. ID hợp lệ có thể là một địa chỉ email hoặc một ID Matrix như \'@localpart:domain\' + Nâng cấp không gian + Thay đổi tên không gian + Bật mã hóa không gian + Đổi địa chỉ chính cho không gian + Đổi ảnh đại diện của không gian + Bạn không có quyền thay đổi các vai trò cần thiết để thay đổi các phần trong không gian này + Chọn các vai trò cần thiết để thay đổi các phần của không gian này + Xem và cập nhật các vai trò cần thiết để thay đổi các phần trong không gian. + Xem và cập nhật các vai trò cần thiết khi thay đổi các phần trong phòng. + Các quyền trong không gian + Các quyền trong phòng + Chỉ chấp nhận sử dụng chứng chỉ nếu quản trị viên máy chủ đã đăng tải một dấu vân tay trùng khớp với dấu ở trên. + Chứng chỉ này đã thay đổi từ một chứng chỉ được tin cậy trước đây sang một chứng chỉ không được tin cậy. Phía máy chủ có thể đã làm mới chứng chỉ. Liên hệ quản trị viên của máy chủ để được cấp dấu vân tay. + Chứng chỉ đã bị thay đổi từ một thiết bị được tin cậy của bạn. Điều này RẤT BẤT THƯỜNG. Chúng tôi khuyên bạn KHÔNG NÊN CHẤP NHẬN chứng chỉ mới này. + Nếu quản trị viên của máy chủ đã nói rằng điều này có thể xảy ra, hãy chắc chắn rằng dấu vân tay phía dưới trùng với dấu vân tay được họ cung cấp. + Hủy cấm người dùng sẽ cho phép họ tham gia không gian này lần nữa. + Cấm người dùng này sẽ đá họ khỏi không gian này và ngăn chặn họ tiếp tục tham gia. + hành động của bạn sẽ xóa họ khỏi không gian này. +\n +\nTrong trường hợp không muốn họ quay lại, bạn nên cấm họ tham gia lần nữa. + \'%s\' không phải là định dạng phù hợp + Định dạng không phù hợp + \'%s\' không phải là một ID cộng đồng hợp lệ + ID cộng đồng không hợp lệ \ No newline at end of file diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 0e8197dbae..f5fe801f65 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -197,16 +197,16 @@ Empty room Empty room (was %s) - Initial Sync:\nWaiting for server response… - Initial Sync:\nDownloading data… - Initial Sync:\nImporting account… - Initial Sync:\nImporting crypto - Initial Sync:\nImporting Rooms - Initial Sync:\nImporting Joined Rooms - Initial Sync:\nImporting Invited Rooms - Initial Sync:\nImporting Left Rooms - Initial Sync:\nImporting Communities - Initial Sync:\nImporting Account Data + Initial sync:\nWaiting for server response… + Initial sync:\nDownloading data… + Initial sync:\nImporting account… + Initial sync:\nImporting crypto + Initial sync:\nImporting rooms + Initial sync:\nLoading your conversations\nIf you\'ve joined lots of rooms, this might take a while + Initial sync:\nImporting invited rooms + Initial sync:\nImporting left rooms + Initial sync:\nImporting communities + Initial sync:\nImporting account data Message sent @@ -322,6 +322,7 @@ Initializing service Synchronising… Listening for events + Listening for notifications Noisy notifications Silent notifications diff --git a/vector/src/test/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModelTest.kt b/vector/src/test/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModelTest.kt index 0fff663972..506ac9c7d0 100644 --- a/vector/src/test/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModelTest.kt +++ b/vector/src/test/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModelTest.kt @@ -46,6 +46,7 @@ class SharedSecureStorageViewModelTest { private val stringProvider = FakeStringProvider() private val session = FakeSession() + val args = SharedSecureStorageActivity.Args(keyId = null, emptyList(), "alias") @Test fun `given a key info with passphrase when initialising then step is EnterPassphrase`() { @@ -123,14 +124,15 @@ class SharedSecureStorageViewModelTest { test.assertEvents(SharedSecureStorageViewEvent.Dismiss) } - private fun createViewModel() = SharedSecureStorageViewModel( - SharedSecureStorageViewState(), - SharedSecureStorageActivity.Args(keyId = null, emptyList(), "alias"), - stringProvider.instance, - session - ) + private fun createViewModel(): SharedSecureStorageViewModel { + return SharedSecureStorageViewModel( + SharedSecureStorageViewState(args), + stringProvider.instance, + session + ) + } - private fun aViewState(hasPassphrase: Boolean, step: SharedSecureStorageViewState.Step) = SharedSecureStorageViewState( + private fun aViewState(hasPassphrase: Boolean, step: SharedSecureStorageViewState.Step) = SharedSecureStorageViewState(args).copy( ready = true, hasPassphrase = hasPassphrase, checkingSSSSAction = Uninitialized, diff --git a/vector/src/test/java/im/vector/app/features/notifications/NotifiableEventProcessorTest.kt b/vector/src/test/java/im/vector/app/features/notifications/NotifiableEventProcessorTest.kt new file mode 100644 index 0000000000..229ab39d1d --- /dev/null +++ b/vector/src/test/java/im/vector/app/features/notifications/NotifiableEventProcessorTest.kt @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.notifications + +import im.vector.app.features.notifications.ProcessedEvent.Type +import im.vector.app.test.fakes.FakeAutoAcceptInvites +import im.vector.app.test.fakes.FakeOutdatedEventDetector +import org.amshove.kluent.shouldBeEqualTo +import org.junit.Test +import org.matrix.android.sdk.api.session.events.model.EventType + +private val NOT_VIEWING_A_ROOM: String? = null + +class NotifiableEventProcessorTest { + + private val outdatedDetector = FakeOutdatedEventDetector() + private val autoAcceptInvites = FakeAutoAcceptInvites() + + private val eventProcessor = NotifiableEventProcessor(outdatedDetector.instance, autoAcceptInvites) + + @Test + fun `given simple events when processing then keep simple events`() { + val events = listOf( + aSimpleNotifiableEvent(eventId = "event-1"), + aSimpleNotifiableEvent(eventId = "event-2") + ) + + val result = eventProcessor.process(events, currentRoomId = NOT_VIEWING_A_ROOM, renderedEvents = emptyList()) + + result shouldBeEqualTo listOfProcessedEvents( + Type.KEEP to events[0], + Type.KEEP to events[1] + ) + } + + @Test + fun `given redacted simple event when processing then remove redaction event`() { + val events = listOf(aSimpleNotifiableEvent(eventId = "event-1", type = EventType.REDACTION)) + + val result = eventProcessor.process(events, currentRoomId = NOT_VIEWING_A_ROOM, renderedEvents = emptyList()) + + result shouldBeEqualTo listOfProcessedEvents( + Type.REMOVE to events[0] + ) + } + + @Test + fun `given invites are auto accepted when processing then remove invitations`() { + autoAcceptInvites._isEnabled = true + val events = listOf( + anInviteNotifiableEvent(roomId = "room-1"), + anInviteNotifiableEvent(roomId = "room-2") + ) + + val result = eventProcessor.process(events, currentRoomId = NOT_VIEWING_A_ROOM, renderedEvents = emptyList()) + + result shouldBeEqualTo listOfProcessedEvents( + Type.REMOVE to events[0], + Type.REMOVE to events[1] + ) + } + + @Test + fun `given invites are not auto accepted when processing then keep invitation events`() { + autoAcceptInvites._isEnabled = false + val events = listOf( + anInviteNotifiableEvent(roomId = "room-1"), + anInviteNotifiableEvent(roomId = "room-2") + ) + + val result = eventProcessor.process(events, currentRoomId = NOT_VIEWING_A_ROOM, renderedEvents = emptyList()) + + result shouldBeEqualTo listOfProcessedEvents( + Type.KEEP to events[0], + Type.KEEP to events[1] + ) + } + + @Test + fun `given out of date message event when processing then removes message event`() { + val events = listOf(aNotifiableMessageEvent(eventId = "event-1", roomId = "room-1")) + outdatedDetector.givenEventIsOutOfDate(events[0]) + + val result = eventProcessor.process(events, currentRoomId = NOT_VIEWING_A_ROOM, renderedEvents = emptyList()) + + result shouldBeEqualTo listOfProcessedEvents( + Type.REMOVE to events[0], + ) + } + + @Test + fun `given in date message event when processing then keep message event`() { + val events = listOf(aNotifiableMessageEvent(eventId = "event-1", roomId = "room-1")) + outdatedDetector.givenEventIsInDate(events[0]) + + val result = eventProcessor.process(events, currentRoomId = NOT_VIEWING_A_ROOM, renderedEvents = emptyList()) + + result shouldBeEqualTo listOfProcessedEvents( + Type.KEEP to events[0], + ) + } + + @Test + fun `given viewing the same room as message event when processing then removes message`() { + val events = listOf(aNotifiableMessageEvent(eventId = "event-1", roomId = "room-1")) + + val result = eventProcessor.process(events, currentRoomId = "room-1", renderedEvents = emptyList()) + + result shouldBeEqualTo listOfProcessedEvents( + Type.REMOVE to events[0], + ) + } + + @Test + fun `given events are different to rendered events when processing then removes difference`() { + val events = listOf(aSimpleNotifiableEvent(eventId = "event-1")) + val renderedEvents = listOf>( + ProcessedEvent(Type.KEEP, events[0]), + ProcessedEvent(Type.KEEP, anInviteNotifiableEvent(roomId = "event-2")) + ) + + val result = eventProcessor.process(events, currentRoomId = NOT_VIEWING_A_ROOM, renderedEvents = renderedEvents) + + result shouldBeEqualTo listOfProcessedEvents( + Type.REMOVE to renderedEvents[1].event, + Type.KEEP to renderedEvents[0].event + ) + } + + private fun listOfProcessedEvents(vararg event: Pair) = event.map { + ProcessedEvent(it.first, it.second) + } +} + +fun aSimpleNotifiableEvent(eventId: String, type: String? = null) = SimpleNotifiableEvent( + matrixID = null, + eventId = eventId, + editedEventId = null, + noisy = false, + title = "title", + description = "description", + type = type, + timestamp = 0, + soundName = null, + canBeReplaced = false, + isRedacted = false +) + +fun anInviteNotifiableEvent(roomId: String) = InviteNotifiableEvent( + matrixID = null, + eventId = "event-id", + roomId = roomId, + roomName = "a room name", + editedEventId = null, + noisy = false, + title = "title", + description = "description", + type = null, + timestamp = 0, + soundName = null, + canBeReplaced = false, + isRedacted = false +) + +fun aNotifiableMessageEvent(eventId: String, roomId: String) = NotifiableMessageEvent( + eventId = eventId, + editedEventId = null, + noisy = false, + timestamp = 0, + senderName = "sender-name", + senderId = "sending-id", + body = "message-body", + roomId = roomId, + roomName = "room-name", + roomIsDirect = false, + canBeReplaced = false, + isRedacted = false +) diff --git a/vector/src/test/java/im/vector/app/features/notifications/NotificationFactoryTest.kt b/vector/src/test/java/im/vector/app/features/notifications/NotificationFactoryTest.kt new file mode 100644 index 0000000000..d720881bac --- /dev/null +++ b/vector/src/test/java/im/vector/app/features/notifications/NotificationFactoryTest.kt @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.notifications + +import im.vector.app.features.notifications.ProcessedEvent.Type +import im.vector.app.test.fakes.FakeNotificationUtils +import im.vector.app.test.fakes.FakeRoomGroupMessageCreator +import im.vector.app.test.fakes.FakeSummaryGroupMessageCreator +import org.amshove.kluent.shouldBeEqualTo +import org.junit.Test + +private const val MY_USER_ID = "user-id" +private const val A_ROOM_ID = "room-id" +private const val AN_EVENT_ID = "event-id" + +private val MY_AVATAR_URL: String? = null +private val AN_INVITATION_EVENT = anInviteNotifiableEvent(roomId = A_ROOM_ID) +private val A_SIMPLE_EVENT = aSimpleNotifiableEvent(eventId = AN_EVENT_ID) +private val A_MESSAGE_EVENT = aNotifiableMessageEvent(eventId = AN_EVENT_ID, roomId = A_ROOM_ID) + +class NotificationFactoryTest { + + private val notificationUtils = FakeNotificationUtils() + private val roomGroupMessageCreator = FakeRoomGroupMessageCreator() + private val summaryGroupMessageCreator = FakeSummaryGroupMessageCreator() + + private val notificationFactory = NotificationFactory( + notificationUtils.instance, + roomGroupMessageCreator.instance, + summaryGroupMessageCreator.instance + ) + + @Test + fun `given a room invitation when mapping to notification then is Append`() = testWith(notificationFactory) { + val expectedNotification = notificationUtils.givenBuildRoomInvitationNotificationFor(AN_INVITATION_EVENT, MY_USER_ID) + val roomInvitation = listOf(ProcessedEvent(Type.KEEP, AN_INVITATION_EVENT)) + + val result = roomInvitation.toNotifications(MY_USER_ID) + + result shouldBeEqualTo listOf(OneShotNotification.Append( + notification = expectedNotification, + meta = OneShotNotification.Append.Meta( + key = A_ROOM_ID, + summaryLine = AN_INVITATION_EVENT.description, + isNoisy = AN_INVITATION_EVENT.noisy, + timestamp = AN_INVITATION_EVENT.timestamp + )) + ) + } + + @Test + fun `given a missing event in room invitation when mapping to notification then is Removed`() = testWith(notificationFactory) { + val missingEventRoomInvitation = listOf(ProcessedEvent(Type.REMOVE, AN_INVITATION_EVENT)) + + val result = missingEventRoomInvitation.toNotifications(MY_USER_ID) + + result shouldBeEqualTo listOf(OneShotNotification.Removed( + key = A_ROOM_ID + )) + } + + @Test + fun `given a simple event when mapping to notification then is Append`() = testWith(notificationFactory) { + val expectedNotification = notificationUtils.givenBuildSimpleInvitationNotificationFor(A_SIMPLE_EVENT, MY_USER_ID) + val roomInvitation = listOf(ProcessedEvent(Type.KEEP, A_SIMPLE_EVENT)) + + val result = roomInvitation.toNotifications(MY_USER_ID) + + result shouldBeEqualTo listOf(OneShotNotification.Append( + notification = expectedNotification, + meta = OneShotNotification.Append.Meta( + key = AN_EVENT_ID, + summaryLine = A_SIMPLE_EVENT.description, + isNoisy = A_SIMPLE_EVENT.noisy, + timestamp = AN_INVITATION_EVENT.timestamp + )) + ) + } + + @Test + fun `given a missing simple event when mapping to notification then is Removed`() = testWith(notificationFactory) { + val missingEventRoomInvitation = listOf(ProcessedEvent(Type.REMOVE, A_SIMPLE_EVENT)) + + val result = missingEventRoomInvitation.toNotifications(MY_USER_ID) + + result shouldBeEqualTo listOf(OneShotNotification.Removed( + key = AN_EVENT_ID + )) + } + + @Test + fun `given room with message when mapping to notification then delegates to room group message creator`() = testWith(notificationFactory) { + val events = listOf(A_MESSAGE_EVENT) + val expectedNotification = roomGroupMessageCreator.givenCreatesRoomMessageFor(events, A_ROOM_ID, MY_USER_ID, MY_AVATAR_URL) + val roomWithMessage = mapOf(A_ROOM_ID to listOf(ProcessedEvent(Type.KEEP, A_MESSAGE_EVENT))) + + val result = roomWithMessage.toNotifications(MY_USER_ID, MY_AVATAR_URL) + + result shouldBeEqualTo listOf(expectedNotification) + } + + @Test + fun `given a room with no events to display when mapping to notification then is Empty`() = testWith(notificationFactory) { + val events = listOf(ProcessedEvent(Type.REMOVE, A_MESSAGE_EVENT)) + val emptyRoom = mapOf(A_ROOM_ID to events) + + val result = emptyRoom.toNotifications(MY_USER_ID, MY_AVATAR_URL) + + result shouldBeEqualTo listOf(RoomNotification.Removed( + roomId = A_ROOM_ID + )) + } + + @Test + fun `given a room with only redacted events when mapping to notification then is Empty`() = testWith(notificationFactory) { + val redactedRoom = mapOf(A_ROOM_ID to listOf(ProcessedEvent(Type.KEEP, A_MESSAGE_EVENT.copy(isRedacted = true)))) + + val result = redactedRoom.toNotifications(MY_USER_ID, MY_AVATAR_URL) + + result shouldBeEqualTo listOf(RoomNotification.Removed( + roomId = A_ROOM_ID + )) + } + + @Test + fun `given a room with redacted and non redacted message events when mapping to notification then redacted events are removed`() = testWith(notificationFactory) { + val roomWithRedactedMessage = mapOf(A_ROOM_ID to listOf( + ProcessedEvent(Type.KEEP, A_MESSAGE_EVENT.copy(isRedacted = true)), + ProcessedEvent(Type.KEEP, A_MESSAGE_EVENT.copy(eventId = "not-redacted")) + )) + val withRedactedRemoved = listOf(A_MESSAGE_EVENT.copy(eventId = "not-redacted")) + val expectedNotification = roomGroupMessageCreator.givenCreatesRoomMessageFor(withRedactedRemoved, A_ROOM_ID, MY_USER_ID, MY_AVATAR_URL) + + val result = roomWithRedactedMessage.toNotifications(MY_USER_ID, MY_AVATAR_URL) + + result shouldBeEqualTo listOf(expectedNotification) + } +} + +fun testWith(receiver: T, block: T.() -> Unit) { + receiver.block() +} diff --git a/vector/src/test/java/im/vector/app/features/notifications/NotificationRendererTest.kt b/vector/src/test/java/im/vector/app/features/notifications/NotificationRendererTest.kt new file mode 100644 index 0000000000..f726ff1b54 --- /dev/null +++ b/vector/src/test/java/im/vector/app/features/notifications/NotificationRendererTest.kt @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.notifications + +import android.app.Notification +import im.vector.app.test.fakes.FakeContext +import im.vector.app.test.fakes.FakeNotificationDisplayer +import im.vector.app.test.fakes.FakeNotificationFactory +import io.mockk.mockk +import org.junit.Test + +private const val MY_USER_ID = "my-user-id" +private const val MY_USER_DISPLAY_NAME = "display-name" +private const val MY_USER_AVATAR_URL = "avatar-url" +private const val AN_EVENT_ID = "event-id" +private const val A_ROOM_ID = "room-id" +private const val USE_COMPLETE_NOTIFICATION_FORMAT = true + +private val AN_EVENT_LIST = listOf>() +private val A_PROCESSED_EVENTS = GroupedNotificationEvents(emptyMap(), emptyList(), emptyList()) +private val A_SUMMARY_NOTIFICATION = SummaryNotification.Update(mockk()) +private val A_REMOVE_SUMMARY_NOTIFICATION = SummaryNotification.Removed +private val A_NOTIFICATION = mockk() +private val MESSAGE_META = RoomNotification.Message.Meta( + summaryLine = "ignored", messageCount = 1, latestTimestamp = -1, roomId = A_ROOM_ID, shouldBing = false +) +private val ONE_SHOT_META = OneShotNotification.Append.Meta(key = "ignored", summaryLine = "ignored", isNoisy = false, timestamp = -1) + +class NotificationRendererTest { + + private val context = FakeContext() + private val notificationDisplayer = FakeNotificationDisplayer() + private val notificationFactory = FakeNotificationFactory() + + private val notificationRenderer = NotificationRenderer( + notificationDisplayer = notificationDisplayer.instance, + notificationFactory = notificationFactory.instance, + appContext = context.instance + ) + + @Test + fun `given no notifications when rendering then cancels summary notification`() { + givenNoNotifications() + + renderEventsAsNotifications() + + notificationDisplayer.verifySummaryCancelled() + notificationDisplayer.verifyNoOtherInteractions() + } + + @Test + fun `given last room message group notification is removed when rendering then remove the summary and then remove message notification`() { + givenNotifications(roomNotifications = listOf(RoomNotification.Removed(A_ROOM_ID)), summaryNotification = A_REMOVE_SUMMARY_NOTIFICATION) + + renderEventsAsNotifications() + + notificationDisplayer.verifyInOrder { + cancelNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID) + cancelNotificationMessage(tag = A_ROOM_ID, NotificationDrawerManager.ROOM_MESSAGES_NOTIFICATION_ID) + } + } + + @Test + fun `given a room message group notification is removed when rendering then remove the message notification and update summary`() { + givenNotifications(roomNotifications = listOf(RoomNotification.Removed(A_ROOM_ID))) + + renderEventsAsNotifications() + + notificationDisplayer.verifyInOrder { + cancelNotificationMessage(tag = A_ROOM_ID, NotificationDrawerManager.ROOM_MESSAGES_NOTIFICATION_ID) + showNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID, A_SUMMARY_NOTIFICATION.notification) + } + } + + @Test + fun `given a room message group notification is added when rendering then show the message notification and update summary`() { + givenNotifications(roomNotifications = listOf(RoomNotification.Message( + A_NOTIFICATION, + shortcutInfo = null, + MESSAGE_META + ))) + + renderEventsAsNotifications() + + notificationDisplayer.verifyInOrder { + showNotificationMessage(tag = A_ROOM_ID, NotificationDrawerManager.ROOM_MESSAGES_NOTIFICATION_ID, A_NOTIFICATION) + showNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID, A_SUMMARY_NOTIFICATION.notification) + } + } + + @Test + fun `given last simple notification is removed when rendering then remove the summary and then remove simple notification`() { + givenNotifications(simpleNotifications = listOf(OneShotNotification.Removed(AN_EVENT_ID)), summaryNotification = A_REMOVE_SUMMARY_NOTIFICATION) + + renderEventsAsNotifications() + + notificationDisplayer.verifyInOrder { + cancelNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID) + cancelNotificationMessage(tag = AN_EVENT_ID, NotificationDrawerManager.ROOM_EVENT_NOTIFICATION_ID) + } + } + + @Test + fun `given a simple notification is removed when rendering then remove the simple notification and update summary`() { + givenNotifications(simpleNotifications = listOf(OneShotNotification.Removed(AN_EVENT_ID))) + + renderEventsAsNotifications() + + notificationDisplayer.verifyInOrder { + cancelNotificationMessage(tag = AN_EVENT_ID, NotificationDrawerManager.ROOM_EVENT_NOTIFICATION_ID) + showNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID, A_SUMMARY_NOTIFICATION.notification) + } + } + + @Test + fun `given a simple notification is added when rendering then show the simple notification and update summary`() { + givenNotifications(simpleNotifications = listOf(OneShotNotification.Append( + A_NOTIFICATION, + ONE_SHOT_META.copy(key = AN_EVENT_ID) + ))) + + renderEventsAsNotifications() + + notificationDisplayer.verifyInOrder { + showNotificationMessage(tag = AN_EVENT_ID, NotificationDrawerManager.ROOM_EVENT_NOTIFICATION_ID, A_NOTIFICATION) + showNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID, A_SUMMARY_NOTIFICATION.notification) + } + } + + @Test + fun `given last invitation notification is removed when rendering then remove the summary and then remove invitation notification`() { + givenNotifications(invitationNotifications = listOf(OneShotNotification.Removed(A_ROOM_ID)), summaryNotification = A_REMOVE_SUMMARY_NOTIFICATION) + + renderEventsAsNotifications() + + notificationDisplayer.verifyInOrder { + cancelNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID) + cancelNotificationMessage(tag = A_ROOM_ID, NotificationDrawerManager.ROOM_INVITATION_NOTIFICATION_ID) + } + } + + @Test + fun `given an invitation notification is removed when rendering then remove the invitation notification and update summary`() { + givenNotifications(invitationNotifications = listOf(OneShotNotification.Removed(A_ROOM_ID))) + + renderEventsAsNotifications() + + notificationDisplayer.verifyInOrder { + cancelNotificationMessage(tag = A_ROOM_ID, NotificationDrawerManager.ROOM_INVITATION_NOTIFICATION_ID) + showNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID, A_SUMMARY_NOTIFICATION.notification) + } + } + + @Test + fun `given an invitation notification is added when rendering then show the invitation notification and update summary`() { + givenNotifications(simpleNotifications = listOf(OneShotNotification.Append( + A_NOTIFICATION, + ONE_SHOT_META.copy(key = A_ROOM_ID) + ))) + + renderEventsAsNotifications() + + notificationDisplayer.verifyInOrder { + showNotificationMessage(tag = A_ROOM_ID, NotificationDrawerManager.ROOM_EVENT_NOTIFICATION_ID, A_NOTIFICATION) + showNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID, A_SUMMARY_NOTIFICATION.notification) + } + } + + private fun renderEventsAsNotifications() { + notificationRenderer.render( + myUserId = MY_USER_ID, + myUserDisplayName = MY_USER_DISPLAY_NAME, + myUserAvatarUrl = MY_USER_AVATAR_URL, + useCompleteNotificationFormat = USE_COMPLETE_NOTIFICATION_FORMAT, + eventsToProcess = AN_EVENT_LIST + ) + } + + private fun givenNoNotifications() { + givenNotifications(emptyList(), emptyList(), emptyList(), USE_COMPLETE_NOTIFICATION_FORMAT, A_REMOVE_SUMMARY_NOTIFICATION) + } + + private fun givenNotifications(roomNotifications: List = emptyList(), + invitationNotifications: List = emptyList(), + simpleNotifications: List = emptyList(), + useCompleteNotificationFormat: Boolean = USE_COMPLETE_NOTIFICATION_FORMAT, + summaryNotification: SummaryNotification = A_SUMMARY_NOTIFICATION) { + notificationFactory.givenNotificationsFor( + groupedEvents = A_PROCESSED_EVENTS, + myUserId = MY_USER_ID, + myUserDisplayName = MY_USER_DISPLAY_NAME, + myUserAvatarUrl = MY_USER_AVATAR_URL, + useCompleteNotificationFormat = useCompleteNotificationFormat, + roomNotifications = roomNotifications, + invitationNotifications = invitationNotifications, + simpleNotifications = simpleNotifications, + summaryNotification = summaryNotification + ) + } +} diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeAutoAcceptInvites.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeAutoAcceptInvites.kt new file mode 100644 index 0000000000..778c2f113d --- /dev/null +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeAutoAcceptInvites.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.test.fakes + +import im.vector.app.features.invite.AutoAcceptInvites + +class FakeAutoAcceptInvites : AutoAcceptInvites { + + var _isEnabled: Boolean = false + + override val isEnabled: Boolean + get() = _isEnabled +} diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeNotificationDisplayer.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeNotificationDisplayer.kt new file mode 100644 index 0000000000..2856b0f49c --- /dev/null +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeNotificationDisplayer.kt @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.test.fakes + +import im.vector.app.features.notifications.NotificationDisplayer +import im.vector.app.features.notifications.NotificationDrawerManager +import io.mockk.confirmVerified +import io.mockk.mockk +import io.mockk.verify +import io.mockk.verifyOrder + +class FakeNotificationDisplayer { + + val instance = mockk(relaxed = true) + + fun verifySummaryCancelled() { + verify { instance.cancelNotificationMessage(tag = null, NotificationDrawerManager.SUMMARY_NOTIFICATION_ID) } + } + + fun verifyNoOtherInteractions() { + confirmVerified(instance) + } + + fun verifyInOrder(verifyBlock: NotificationDisplayer.() -> Unit) { + verifyOrder { verifyBlock(instance) } + verifyNoOtherInteractions() + } +} diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeNotificationFactory.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeNotificationFactory.kt new file mode 100644 index 0000000000..a6e7d1a078 --- /dev/null +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeNotificationFactory.kt @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.test.fakes + +import im.vector.app.features.notifications.GroupedNotificationEvents +import im.vector.app.features.notifications.NotificationFactory +import im.vector.app.features.notifications.OneShotNotification +import im.vector.app.features.notifications.RoomNotification +import im.vector.app.features.notifications.SummaryNotification +import io.mockk.every +import io.mockk.mockk + +class FakeNotificationFactory { + + val instance = mockk() + + fun givenNotificationsFor(groupedEvents: GroupedNotificationEvents, + myUserId: String, + myUserDisplayName: String, + myUserAvatarUrl: String?, + useCompleteNotificationFormat: Boolean, + roomNotifications: List, + invitationNotifications: List, + simpleNotifications: List, + summaryNotification: SummaryNotification) { + with(instance) { + every { groupedEvents.roomEvents.toNotifications(myUserDisplayName, myUserAvatarUrl) } returns roomNotifications + every { groupedEvents.invitationEvents.toNotifications(myUserId) } returns invitationNotifications + every { groupedEvents.simpleEvents.toNotifications(myUserId) } returns simpleNotifications + + every { + createSummaryNotification( + roomNotifications, + invitationNotifications, + simpleNotifications, + useCompleteNotificationFormat + ) + } returns summaryNotification + } + } +} diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeNotificationUtils.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeNotificationUtils.kt new file mode 100644 index 0000000000..39f2ad59ff --- /dev/null +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeNotificationUtils.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.test.fakes + +import android.app.Notification +import im.vector.app.features.notifications.InviteNotifiableEvent +import im.vector.app.features.notifications.NotificationUtils +import im.vector.app.features.notifications.SimpleNotifiableEvent +import io.mockk.every +import io.mockk.mockk + +class FakeNotificationUtils { + + val instance = mockk() + + fun givenBuildRoomInvitationNotificationFor(event: InviteNotifiableEvent, myUserId: String): Notification { + val mockNotification = mockk() + every { instance.buildRoomInvitationNotification(event, myUserId) } returns mockNotification + return mockNotification + } + + fun givenBuildSimpleInvitationNotificationFor(event: SimpleNotifiableEvent, myUserId: String): Notification { + val mockNotification = mockk() + every { instance.buildSimpleEventNotification(event, myUserId) } returns mockNotification + return mockNotification + } +} diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeOutdatedEventDetector.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeOutdatedEventDetector.kt new file mode 100644 index 0000000000..0e1d617ca2 --- /dev/null +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeOutdatedEventDetector.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.test.fakes + +import im.vector.app.features.notifications.NotifiableEvent +import im.vector.app.features.notifications.OutdatedEventDetector +import io.mockk.every +import io.mockk.mockk + +class FakeOutdatedEventDetector { + val instance = mockk() + + fun givenEventIsOutOfDate(notifiableEvent: NotifiableEvent) { + every { instance.isMessageOutdated(notifiableEvent) } returns true + } + + fun givenEventIsInDate(notifiableEvent: NotifiableEvent) { + every { instance.isMessageOutdated(notifiableEvent) } returns false + } +} diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeRoomGroupMessageCreator.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeRoomGroupMessageCreator.kt new file mode 100644 index 0000000000..c164b9a661 --- /dev/null +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeRoomGroupMessageCreator.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.test.fakes + +import im.vector.app.features.notifications.NotifiableMessageEvent +import im.vector.app.features.notifications.RoomGroupMessageCreator +import im.vector.app.features.notifications.RoomNotification +import io.mockk.every +import io.mockk.mockk + +class FakeRoomGroupMessageCreator { + + val instance = mockk() + + fun givenCreatesRoomMessageFor(events: List, + roomId: String, + userDisplayName: String, + userAvatarUrl: String?): RoomNotification.Message { + val mockMessage = mockk() + every { instance.createRoomMessage(events, roomId, userDisplayName, userAvatarUrl) } returns mockMessage + return mockMessage + } +} diff --git a/vector/src/main/java/im/vector/app/core/di/HasScreenInjector.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeSummaryGroupMessageCreator.kt similarity index 62% rename from vector/src/main/java/im/vector/app/core/di/HasScreenInjector.kt rename to vector/src/test/java/im/vector/app/test/fakes/FakeSummaryGroupMessageCreator.kt index 4618bd04d1..eef77298a0 100644 --- a/vector/src/main/java/im/vector/app/core/di/HasScreenInjector.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeSummaryGroupMessageCreator.kt @@ -1,11 +1,11 @@ /* - * Copyright 2019 New Vector Ltd + * Copyright (c) 2021 New Vector Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -14,9 +14,12 @@ * limitations under the License. */ -package im.vector.app.core.di +package im.vector.app.test.fakes -interface HasScreenInjector { +import im.vector.app.features.notifications.SummaryGroupMessageCreator +import io.mockk.mockk - fun injector(): ScreenComponent +class FakeSummaryGroupMessageCreator { + + val instance = mockk() } 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 new file mode 100644 index 0000000000..eb8f9ac413 --- /dev/null +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.test.fakes + +import im.vector.app.features.settings.VectorPreferences +import io.mockk.every +import io.mockk.mockk + +class FakeVectorPreferences { + + val instance = mockk() + + fun givenUseCompleteNotificationFormat(value: Boolean) { + every { instance.useCompleteNotificationFormat() } returns value + } +}