diff --git a/.maestro/tests/account/changeServer.yaml b/.maestro/tests/account/changeServer.yaml
index e503589f5c..c6b092e018 100644
--- a/.maestro/tests/account/changeServer.yaml
+++ b/.maestro/tests/account/changeServer.yaml
@@ -15,7 +15,7 @@ appId: ${APP_ID}
- tapOn: "gnuradio.org"
- extendedWaitUntil:
visible: "This server currently doesn’t support sliding sync."
- timeout: 10_000
+ timeout: 10000
- tapOn: "Cancel"
- back
- back
diff --git a/.maestro/tests/assertions/assertAnalyticsDisplayed.yaml b/.maestro/tests/assertions/assertAnalyticsDisplayed.yaml
index 96a91a24af..9c63c99ffc 100644
--- a/.maestro/tests/assertions/assertAnalyticsDisplayed.yaml
+++ b/.maestro/tests/assertions/assertAnalyticsDisplayed.yaml
@@ -2,4 +2,4 @@ appId: ${APP_ID}
---
- extendedWaitUntil:
visible: "Help improve Element X dbg"
- timeout: 10_000
+ timeout: 10000
diff --git a/.maestro/tests/assertions/assertHomeDisplayed.yaml b/.maestro/tests/assertions/assertHomeDisplayed.yaml
index 6e9eec50db..ca409705e1 100644
--- a/.maestro/tests/assertions/assertHomeDisplayed.yaml
+++ b/.maestro/tests/assertions/assertHomeDisplayed.yaml
@@ -2,4 +2,4 @@ appId: ${APP_ID}
---
- extendedWaitUntil:
visible: "All Chats"
- timeout: 10_000
+ timeout: 10000
diff --git a/.maestro/tests/assertions/assertInitDisplayed.yaml b/.maestro/tests/assertions/assertInitDisplayed.yaml
index 417ac87711..9424f382c1 100644
--- a/.maestro/tests/assertions/assertInitDisplayed.yaml
+++ b/.maestro/tests/assertions/assertInitDisplayed.yaml
@@ -2,4 +2,4 @@ appId: ${APP_ID}
---
- extendedWaitUntil:
visible: "Be in your element"
- timeout: 10_000
+ timeout: 10000
diff --git a/.maestro/tests/assertions/assertLoginDisplayed.yaml b/.maestro/tests/assertions/assertLoginDisplayed.yaml
index 3abd86ceef..b18078f916 100644
--- a/.maestro/tests/assertions/assertLoginDisplayed.yaml
+++ b/.maestro/tests/assertions/assertLoginDisplayed.yaml
@@ -2,4 +2,4 @@ appId: ${APP_ID}
---
- extendedWaitUntil:
visible: "Change account provider"
- timeout: 10_000
+ timeout: 10000
diff --git a/.maestro/tests/assertions/assertRoomListSynced.yaml b/.maestro/tests/assertions/assertRoomListSynced.yaml
index 2d13c17df9..5fcd6e093e 100644
--- a/.maestro/tests/assertions/assertRoomListSynced.yaml
+++ b/.maestro/tests/assertions/assertRoomListSynced.yaml
@@ -2,4 +2,4 @@ appId: ${APP_ID}
---
- extendedWaitUntil:
visible: ${ROOM_NAME}
- timeout: 10_000
+ timeout: 10000
diff --git a/.maestro/tests/assertions/assertWelcomeScreenDisplayed.yaml b/.maestro/tests/assertions/assertWelcomeScreenDisplayed.yaml
index 73e8e78ef5..3fbd9d2513 100644
--- a/.maestro/tests/assertions/assertWelcomeScreenDisplayed.yaml
+++ b/.maestro/tests/assertions/assertWelcomeScreenDisplayed.yaml
@@ -3,4 +3,4 @@ appId: ${APP_ID}
- extendedWaitUntil:
visible:
id: "welcome_screen-title"
- timeout: 10_000
+ timeout: 10000
diff --git a/.maestro/tests/roomList/timeline/messages/text.yaml b/.maestro/tests/roomList/timeline/messages/text.yaml
index 4e3b7bbd45..963b2cf9e9 100644
--- a/.maestro/tests/roomList/timeline/messages/text.yaml
+++ b/.maestro/tests/roomList/timeline/messages/text.yaml
@@ -1,7 +1,8 @@
appId: ${APP_ID}
---
- takeScreenshot: build/maestro/510-Timeline
-- tapOn: "Message"
+- tapOn:
+ id: "rich_text_editor"
- inputText: "Hello world!"
- tapOn: "Send"
- hideKeyboard
diff --git a/appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt
index 703e59c7f1..6771718d81 100644
--- a/appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt
+++ b/appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt
@@ -37,8 +37,7 @@ import org.junit.Rule
import org.junit.Test
class RootPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt
index f0a9642f4f..efada670b0 100644
--- a/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt
+++ b/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt
@@ -37,8 +37,7 @@ import org.junit.Test
class LoggedInPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/changelog.d/1232.bugfix b/changelog.d/1232.bugfix
new file mode 100644
index 0000000000..ab540beeab
--- /dev/null
+++ b/changelog.d/1232.bugfix
@@ -0,0 +1 @@
+Fix long click on simple formatted messages
diff --git a/changelog.d/1297.bugfix b/changelog.d/1297.bugfix
new file mode 100644
index 0000000000..3b7d61fd8d
--- /dev/null
+++ b/changelog.d/1297.bugfix
@@ -0,0 +1 @@
+Fix top padding in room list when app is opened in offline mode.
diff --git a/changelog.d/510.misc b/changelog.d/510.misc
new file mode 100644
index 0000000000..556aeab74f
--- /dev/null
+++ b/changelog.d/510.misc
@@ -0,0 +1 @@
+Add a sub-screen "Notifications" in the existing application Settings
diff --git a/features/analytics/impl/src/main/res/values-de/translations.xml b/features/analytics/impl/src/main/res/values-de/translations.xml
index 7ef2ff2500..4282ead092 100644
--- a/features/analytics/impl/src/main/res/values-de/translations.xml
+++ b/features/analytics/impl/src/main/res/values-de/translations.xml
@@ -1,10 +1,4 @@
- "Wir werden keine personenbezogenen Daten aufzeichnen oder auswerten"
- "Teile anonyme Nutzungsdaten, um uns bei der Identifizierung von Problemen zu helfen."
- "Du kannst alle unsere Nutzerbedingungen %1$s lesen."
"hier"
- "Du kannst dies jederzeit deaktivieren"
- "Wir geben deine Daten nicht an Dritte weiter"
- "Hilf uns, %1$s zu verbessern"
diff --git a/features/analytics/impl/src/main/res/values-fr/translations.xml b/features/analytics/impl/src/main/res/values-fr/translations.xml
index 55231f7b6c..63a97dc905 100644
--- a/features/analytics/impl/src/main/res/values-fr/translations.xml
+++ b/features/analytics/impl/src/main/res/values-fr/translations.xml
@@ -1,10 +1,10 @@
- "Nous n\'enregistrerons ni ne traiterons aucune donnée personnelle"
+ "Nous n\'enregistrerons ni ne profilerons aucune donnée personnelle"
"Partagez des données d\'utilisation anonymes pour nous aider à identifier les problèmes."
- "Consultez nos conditions d\'utilisation %1$s."
+ "Vous pouvez lire toutes nos conditions %1$s."
"ici"
- "Vous pouvez désactiver cette fonction à tout moment"
+ "Vous pouvez le désactiver à tout moment"
"Nous ne partagerons pas vos données avec des tiers"
- "Aidez-nous à améliorer %1$s"
+ "Aidez à améliorer %1$s"
diff --git a/features/analytics/impl/src/main/res/values-zh-rTW/translations.xml b/features/analytics/impl/src/main/res/values-zh-rTW/translations.xml
index 35a607c95e..81349674b7 100644
--- a/features/analytics/impl/src/main/res/values-zh-rTW/translations.xml
+++ b/features/analytics/impl/src/main/res/values-zh-rTW/translations.xml
@@ -1,5 +1,10 @@
+ "我們不會紀錄或剖繪您的個人資料"
+ "分享匿名的使用數據以協助我們釐清問題"
+ "您可以到 %1$s 閱讀我們的條款。"
+ "這裡"
"您可以在任何時候關閉它"
"我們不會和第三方分享您的資料"
+ "讓 %1$s 變得更好"
diff --git a/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInPresenterTest.kt b/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInPresenterTest.kt
index aad9973282..3dcb4674df 100644
--- a/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInPresenterTest.kt
+++ b/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInPresenterTest.kt
@@ -31,8 +31,7 @@ import org.junit.Test
class AnalyticsOptInPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/preferences/AnalyticsPreferencesPresenterTest.kt b/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/preferences/AnalyticsPreferencesPresenterTest.kt
index 3a2812b72e..494468c530 100644
--- a/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/preferences/AnalyticsPreferencesPresenterTest.kt
+++ b/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/preferences/AnalyticsPreferencesPresenterTest.kt
@@ -30,8 +30,7 @@ import org.junit.Test
class AnalyticsPreferencesPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/features/createroom/impl/src/main/res/values-de/translations.xml b/features/createroom/impl/src/main/res/values-de/translations.xml
index abc2ef9d71..f033df7792 100644
--- a/features/createroom/impl/src/main/res/values-de/translations.xml
+++ b/features/createroom/impl/src/main/res/values-de/translations.xml
@@ -1,15 +1,4 @@
- "Neuer Raum"
- "Freunde zu Element einladen"
- "Personen hinzufügen"
- "Beim Erstellen des Raums ist ein Fehler aufgetreten"
- "Die Nachrichten in diesem Raum sind verschlüsselt. Die Verschlüsselung kann nicht nachträglich deaktiviert werden."
- "Privater Raum (nur auf Einladung)"
- "Nachrichten sind nicht verschlüsselt und jeder kann sie lesen. Du kannst die Verschlüsselung zu einem späteren Zeitpunkt aktivieren."
- "Öffentlicher Raum (jeder)"
- "Raumname"
- "Thema (optional)"
- "Beim Versuch, einen Chat zu starten, ist ein Fehler aufgetreten"
"Raum erstellen"
diff --git a/features/createroom/impl/src/main/res/values-fr/translations.xml b/features/createroom/impl/src/main/res/values-fr/translations.xml
index e37a30c457..289fb3f50d 100644
--- a/features/createroom/impl/src/main/res/values-fr/translations.xml
+++ b/features/createroom/impl/src/main/res/values-fr/translations.xml
@@ -1,15 +1,15 @@
- "Nouveau salon"
+ "Nouvelle salle"
"Inviter des amis sur Element"
"Inviter des personnes"
- "Une erreur s\'est produite lors de la création du salon"
- "Les messages dans ce salon sont chiffrés. Une fois activé, le chiffrement ne peut pas être désactivé."
- "Salon privé (sur invitation uniquement)"
- "Les messages ne sont pas chiffrés et n\'importe qui peut les lire. Vous pouvez activer le chiffrement ultérieurement."
- "Salon public (n’importe qui)"
- "Nom du salon"
- "Sujet (optionnel)"
+ "Une erreur s\'est produite lors de la création de la salle"
+ "Les messages dans cette pièce sont cryptés. Le cryptage ne peut pas être désactivé par la suite."
+ "Salle privée (sur invitation seulement)"
+ "Les messages ne sont pas cryptés et n\'importe qui peut les lire. Vous pouvez activer le chiffrement ultérieurement."
+ "Salle publique (tout le monde)"
+ "Nom de la salle"
+ "Sujet (facultatif)"
"Une erreur s\'est produite lors de la tentative de démarrage d\'une discussion"
- "Créer un salon"
+ "Créer une salle"
diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt
index af68f0dd57..968b2a1b57 100644
--- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt
+++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt
@@ -32,8 +32,7 @@ import org.junit.Test
class AddPeoplePresenterTests {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
private lateinit var presenter: AddPeoplePresenter
diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt
index bc5bafa843..a330384f60 100644
--- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt
+++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt
@@ -60,8 +60,7 @@ private const val AN_URI_FROM_GALLERY = "content://uri_from_gallery"
@RunWith(RobolectricTestRunner::class)
class ConfigureRoomPresenterTests {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
private lateinit var presenter: ConfigureRoomPresenter
diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt
index 1809df135b..bf4593bdbb 100644
--- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt
+++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt
@@ -44,8 +44,7 @@ import org.junit.Test
class CreateRoomRootPresenterTests {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
private lateinit var userRepository: FakeUserRepository
diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/userlist/DefaultUserListPresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/userlist/DefaultUserListPresenterTests.kt
index 12e9f2fc90..a591a7a8e9 100644
--- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/userlist/DefaultUserListPresenterTests.kt
+++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/userlist/DefaultUserListPresenterTests.kt
@@ -33,8 +33,7 @@ import org.junit.Test
class DefaultUserListPresenterTests {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
private val userRepository = FakeUserRepository()
diff --git a/features/ftue/impl/src/main/res/values-de/translations.xml b/features/ftue/impl/src/main/res/values-de/translations.xml
index 19b445bd50..36fc85fba5 100644
--- a/features/ftue/impl/src/main/res/values-de/translations.xml
+++ b/features/ftue/impl/src/main/res/values-de/translations.xml
@@ -1,11 +1,5 @@
- "Dies ist ein einmaliger Vorgang, danke fürs Warten."
- "Dein Konto einrichten"
- "Anrufe, Umfragen, Suche und mehr werden später in diesem Jahr hinzugefügt."
- "Der Nachrichtenverlauf für verschlüsselte Räume wird in diesem Update nicht verfügbar sein."
- "Wir würden uns freuen, wenn du uns über die Einstellungsseite deine Meinung mitteilst."
- "Los geht\'s!"
- "Folgendes musst du wissen:"
- "Willkommen bei %1$s!"
+ "Du kannst deine Einstellungen später ändern."
+ "Erlaube Benachrichtigungen und verpasse keine Nachricht"
diff --git a/features/ftue/impl/src/main/res/values-fr/translations.xml b/features/ftue/impl/src/main/res/values-fr/translations.xml
index 9f431f545d..68345265d4 100644
--- a/features/ftue/impl/src/main/res/values-fr/translations.xml
+++ b/features/ftue/impl/src/main/res/values-fr/translations.xml
@@ -1,10 +1,13 @@
- "Ce processus n’a besoin d’être fait qu’une seule fois, merci de patienter."
+ "Il s\'agit d\'un processus unique, merci d\'avoir attendu."
"Configuration de votre compte."
- "L’historique des messages pour les salons chiffrés ne sera pas disponible dans cette mise à jour."
- "Nous serions ravis d’avoir votre avis, n’hésitez pas à nous le partager via la page des paramètres."
- "C’est parti !"
- "Voici ce qu’il faut savoir :"
- "Bienvenue sur %1$s !"
+ "Vous pourrez modifier vos paramètres ultérieurement."
+ "Autorisez les notifications et ne manquez aucun message"
+ "Les appels, les sondages, les recherches et plus encore seront ajoutés plus tard cette année."
+ "L\'historique des messages pour les salles cryptées ne sera pas disponible dans cette mise à jour."
+ "N\'hésitez pas à nous faire part de vos commentaires via la page des paramètres."
+ "C\'est parti !"
+ "Voici ce que vous devez savoir :"
+ "Bienvenue à %1$s !"
diff --git a/features/ftue/impl/src/main/res/values-sk/translations.xml b/features/ftue/impl/src/main/res/values-sk/translations.xml
index 5bbd2d386d..aa76053cea 100644
--- a/features/ftue/impl/src/main/res/values-sk/translations.xml
+++ b/features/ftue/impl/src/main/res/values-sk/translations.xml
@@ -2,6 +2,8 @@
"Ide o jednorazový proces, ďakujeme za trpezlivosť."
"Nastavenie vášho účtu."
+ "Svoje nastavenia môžete neskôr zmeniť."
+ "Povoľte oznámenia a nikdy nezmeškajte žiadnu správu"
"Hovory, ankety, vyhľadávanie a ďalšie funkcie pribudnú neskôr v tomto roku."
"História správ pre zašifrované miestnosti nebude v tejto aktualizácii k dispozícii."
"Radi by sme od vás počuli, dajte nám vedieť, čo si myslíte, prostredníctvom stránky nastavení."
diff --git a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenPresenterTest.kt b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenPresenterTest.kt
index 1a9a33130d..ac56718e06 100644
--- a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenPresenterTest.kt
+++ b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenPresenterTest.kt
@@ -32,8 +32,7 @@ import org.junit.Test
class MigrationScreenPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenterTests.kt b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenterTests.kt
index 74a867a0fc..9bfa1cf33c 100644
--- a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenterTests.kt
+++ b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenterTests.kt
@@ -39,8 +39,7 @@ import org.junit.Test
class NotificationsOptInPresenterTests {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
private var isFinished = false
diff --git a/features/invitelist/impl/src/main/res/values-de/translations.xml b/features/invitelist/impl/src/main/res/values-de/translations.xml
deleted file mode 100644
index 2cec59d6a0..0000000000
--- a/features/invitelist/impl/src/main/res/values-de/translations.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
- "Möchtest du den Beitritt zu %1$s wirklich ablehnen?"
- "Einladung ablehnen"
- "Möchtest du den privaten Chat mit %1$s wirklich ablehnen?"
- "Chat ablehnen"
- "Keine Einladungen"
- "%1$s (%2$s) hat dich eingeladen"
-
diff --git a/features/invitelist/impl/src/main/res/values-fr/translations.xml b/features/invitelist/impl/src/main/res/values-fr/translations.xml
index 677fadd539..55606b100d 100644
--- a/features/invitelist/impl/src/main/res/values-fr/translations.xml
+++ b/features/invitelist/impl/src/main/res/values-fr/translations.xml
@@ -1,8 +1,8 @@
- "Voulez-vous vraiment refuser l‘invitation à rejoindre %1$s ?"
+ "Êtes-vous sûr de vouloir décliner l\'invitation à participer %1$s ?"
"Refuser l\'invitation"
- "Voulez-vous vraiment refuser ce chat privé avec %1$s ?"
+ "Êtes-vous sûr de vouloir refuser cette discussion privée avec %1$s ?"
"Refuser le chat"
"Aucune invitation"
"%1$s (%2$s) vous a invité"
diff --git a/features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt b/features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt
index 9ba26b5e2d..ca0c73d53e 100644
--- a/features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt
+++ b/features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt
@@ -50,8 +50,7 @@ import org.junit.Rule
import org.junit.Test
class InviteListPresenterTests {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenterImplTest.kt b/features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenterImplTest.kt
index 7bbab71a8d..fff7c00410 100644
--- a/features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenterImplTest.kt
+++ b/features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenterImplTest.kt
@@ -39,8 +39,7 @@ import org.junit.Test
class LeaveRoomPresenterImplTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt
index af31f1bfeb..c0b4cb7d35 100644
--- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt
+++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt
@@ -42,8 +42,7 @@ import org.junit.Test
class SendLocationPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
private val permissionsPresenterFake = PermissionsPresenterFake()
diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt
index ebb146ca57..9eb0c3e1e2 100644
--- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt
+++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt
@@ -35,8 +35,7 @@ import org.junit.Test
class ShowLocationPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
private val permissionsPresenterFake = PermissionsPresenterFake()
diff --git a/features/login/impl/src/main/res/values-de/translations.xml b/features/login/impl/src/main/res/values-de/translations.xml
index 7464e17f8d..8a69936ff8 100644
--- a/features/login/impl/src/main/res/values-de/translations.xml
+++ b/features/login/impl/src/main/res/values-de/translations.xml
@@ -1,47 +1,8 @@
- "Kontoanbieter wechseln"
- "Adresse des Homeservers"
- "Gib einen Suchbegriff oder eine Domainadresse ein."
- "Suche nach einem Unternehmen, einer Community oder einem privaten Server."
- "Finde einen Kontoanbieter"
- "Hier werden deine Konversationen stattfinden — genauso wie du einen E-Mail-Anbieter verwenden würdest, um deine E-Mails aufzubewahren."
- "Du bist dabei dich bei %s anzumelden"
- "Hier werden deine Konversationen stattfinden — genauso wie du einen E-Mail-Anbieter verwenden würdest, um deine E-Mails aufzubewahren."
- "Du bist dabei ein Konto auf %s zu erstellen"
- "Matrix.org ist ein großer, kostenloser Server im öffentlichen Matrix-Netzwerk für sichere, dezentrale Kommunikation, der von der Matrix.org Foundation betrieben wird."
- "Andere"
- "Verwende einen anderen Kontoanbieter, z. B. deinen eigenen privaten Server oder ein Arbeitskonto."
- "Kontoanbieter ändern"
- "Wir konnten diesen Homeserver nicht erreichen. Bitte überprüfe, dass du die Homeserver-URL korrekt eingegeben hast. Wenn die URL korrekt ist, wende dich an deinen Homeserver-Administrator für weitere Hilfe."
- "Dieser Server unterstützt derzeit keine Sliding Sync."
- "Homeserver-URL"
- "Du kannst dich nur mit einem existierenden Server verbinden, der Sliding Sync unterstützt. Dein Homeserver-Administrator muss es konfigurieren. %1$s"
- "Wie lautet die Adresse deines Servers?"
- "Dieses Konto wurde deaktiviert."
- "Falscher Benutzername und/oder Passwort"
- "Dies ist kein gültiger Benutzeridentifikator. Erwartetes Format: \'@user:homeserver.org\'"
- "Der ausgewählte Homeserver unterstützt kein Passwort- oder OIDC-Login. Bitte kontaktiere deinen Admin oder wähle einen anderen Homeserver."
- "Gib deine Daten ein"
- "Willkommen zurück!"
- "Bei %1$s anmelden"
- "Kontoanbieter wechseln"
- "Ein privater Server für Element-Mitarbeiter."
- "Matrix ist ein offenes Netzwerk für sichere, dezentrale Kommunikation"
- "Hier werden deine Konversationen stattfinden — genau so wie du einen E-Mail-Anbieter verwenden würdest, um deine E-Mails aufzubewahren."
- "Du bist dabei dich bei %1$s anzumelden"
- "Du bist dabei ein Konto auf %1$s zu erstellen"
- "Im Moment besteht eine hohe Nachfrage nach %1$s auf %2$s. Besuche die App in ein paar Tagen wieder und versuche es erneut.
-
-Vielen Dank für deine Geduld!"
- "Willkommen bei %1$s!"
- "Du hast es fast geschafft!"
- "Du bist dabei."
"Weiter"
"Weiter"
- "Wählen deinen Server"
"Passwort"
"Weiter"
- "Matrix ist ein offenes Netzwerk für sichere, dezentrale Kommunikation"
"Benutzername"
diff --git a/features/login/impl/src/main/res/values-fr/translations.xml b/features/login/impl/src/main/res/values-fr/translations.xml
index 74f23cc5c9..41be63cb2c 100644
--- a/features/login/impl/src/main/res/values-fr/translations.xml
+++ b/features/login/impl/src/main/res/values-fr/translations.xml
@@ -1,39 +1,40 @@
- "Changer de fournisseur"
+ "Changer de fournisseur de compte"
"Adresse du serveur d\'accueil"
- "Entrez un mot clé de recherche ou un nom de domaine."
- "Rechercher une entreprise, une communauté ou un serveur privé."
- "Trouver un fournisseur de services"
- "C\'est ici que vos conversations seront stockées - tout comme vous utiliseriez un fournisseur de messagerie pour conserver vos e-mails."
+ "Entrez un terme de recherche ou une adresse de domaine."
+ "Recherchez une entreprise, une communauté ou un serveur privé."
+ "Trouver un fournisseur de comptes"
+ "C\'est ici que vos conversations seront enregistrées, comme vous le feriez avec un fournisseur de messagerie pour conserver vos e-mails."
"Vous êtes sur le point de vous connecter à %s"
- "C\'est ici que vos conversations seront stockées - tout comme vous utiliseriez un fournisseur de messagerie pour conserver vos e-mails."
+ "C\'est ici que vos conversations seront enregistrées, comme vous le feriez avec un fournisseur de messagerie pour conserver vos e-mails."
"Vous êtes sur le point de créer un compte sur %s"
- "Autre"
- "Utilisez un autre fournisseur de compte, tel que votre propre serveur ou un compte professionnel."
- "Changer de fournisseur"
- "Nous n\'avons pas pu atteindre ce serveur domestique. Vérifiez que vous avez correctement saisi l\'URL du serveur d\'accueil. Si l\'URL est correcte, contactez l\'administrateur de votre serveur domestique pour obtenir de l\'aide."
+ "Matrix.org est un grand serveur gratuit sur le réseau public Matrix pour une communication sécurisée et décentralisée, géré par la Fondation Matrix.org."
+ "Autres"
+ "Utilisez un autre fournisseur de compte, tel que votre propre serveur privé ou un compte professionnel."
+ "Changer de fournisseur de compte"
+ "Nous n\'avons pas pu atteindre ce serveur d\'accueil. Vérifiez que vous avez correctement saisi l\'URL du serveur d\'accueil. Si l\'URL est correcte, contactez l\'administrateur de votre serveur d\'accueil pour obtenir de l\'aide."
"Ce serveur ne prend actuellement pas en charge la synchronisation glissante."
"URL du serveur d\'accueil"
- "Vous ne pouvez vous connecter qu\'à un serveur existant qui prend en charge la synchronisation glissante. L\'administrateur de votre serveur domestique devra la configurer. %1$s"
+ "Vous ne pouvez vous connecter qu\'à un serveur existant qui prend en charge la synchronisation par glissement. L\'administrateur de votre serveur d\'accueil devra le configurer. %1$s"
"Quelle est l\'adresse de votre serveur ?"
"Ce compte a été désactivé."
- "Nom d\'utilisateur et/ou mot de passe incorrect"
+ "Nom d\'utilisateur et/ou mot de passe incorrects"
"Il ne s\'agit pas d\'un identifiant utilisateur valide. Format attendu : « @user:homeserver.org »"
- "Le serveur domestique sélectionné ne prend pas en charge le mot de passe ou la connexion OIDC. Contactez votre administrateur ou choisissez un autre serveur domestique."
+ "Le serveur d\'accueil sélectionné ne prend pas en charge le mot de passe ou la connexion OIDC. Contactez votre administrateur ou choisissez un autre serveur d\'accueil."
"Saisir vos informations personnelles"
- "Heureux de vous revoir!"
- "Se connecter à %1$s"
+ "Bienvenue !"
+ "Connectez-vous à %1$s"
"Changer de fournisseur de compte"
- "Un serveur privé pour les employés d’Element."
- "Matrix est un réseau ouvert de communication sécurisée et décentralisée."
- "C\'est là que vos conversations seront conservées — de la même manière que votre service d’e-mail habituel conserverait vos e-mails."
- "Vous allez vous connecter à %1$s"
- "Vous allez créer un compte sur %1$s"
- "Il y a une forte demande pour %1$s sur %2$s en ce moment. Rouvrez l’app dans quelques jours et réessayez.
+ "Un serveur privé pour les employés d\'Element."
+ "Matrix est un réseau ouvert pour une communication sécurisée et décentralisée."
+ "C\'est ici que vos conversations seront enregistrées, comme vous le feriez avec un fournisseur de messagerie pour conserver vos e-mails."
+ "Vous êtes sur le point de vous connecter à %1$s"
+ "Vous êtes sur le point de créer un compte sur %1$s"
+ "Il y a une forte demande pour %1$s sur %2$s à l\'heure actuelle. Revenez sur l\'application dans quelques jours et réessayez.
-Merci de votre patience !"
- "Bienvenue sur %1$s !"
+Merci pour votre patience !"
+ "Bienvenue à %1$s !"
"Vous y êtes presque."
"Vous y êtes."
"Continuer"
@@ -41,6 +42,6 @@ Merci de votre patience !"
"Sélectionnez votre serveur"
"Mot de passe"
"Continuer"
- "Matrix est un réseau ouvert de communication sécurisée et décentralisée."
+ "Matrix est un réseau ouvert pour une communication sécurisée et décentralisée."
"Nom d\'utilisateur"
diff --git a/features/login/impl/src/main/res/values-sk/translations.xml b/features/login/impl/src/main/res/values-sk/translations.xml
index 14a5407055..e94990554f 100644
--- a/features/login/impl/src/main/res/values-sk/translations.xml
+++ b/features/login/impl/src/main/res/values-sk/translations.xml
@@ -9,6 +9,7 @@
"Chystáte sa prihlásiť do %s"
"Tu budú žiť vaše konverzácie — podobne ako používate poskytovateľa e-mailových služieb na uchovávanie e-mailov."
"Chystáte sa vytvoriť účet na %s"
+ "Matrix.org je veľký bezplatný server vo verejnej sieti Matrix na bezpečnú, decentralizovanú komunikáciu, ktorý prevádzkuje nadácia Matrix.org."
"Iný"
"Použite iného poskytovateľa účtu, ako napríklad vlastný súkromný server alebo pracovný účet."
"Zmeniť poskytovateľa účtu"
diff --git a/features/login/impl/src/main/res/values-zh-rTW/translations.xml b/features/login/impl/src/main/res/values-zh-rTW/translations.xml
index d3dca0942a..e4bfd61b35 100644
--- a/features/login/impl/src/main/res/values-zh-rTW/translations.xml
+++ b/features/login/impl/src/main/res/values-zh-rTW/translations.xml
@@ -1,10 +1,11 @@
- "您即將登入%s"
+ "您即將登入 %s"
"您即將在 %s 建立帳號"
"其他"
"此伺服器目前不支援 sliding sync。"
"家伺服器 URL"
+ "輸入您的詳細資料"
"歡迎回來!"
"登入 %1$s"
"您即將登入 %1$s"
diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt
index 4a2fcc397c..f94ce4e65d 100644
--- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt
+++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt
@@ -33,8 +33,7 @@ import org.junit.Test
class ChangeServerPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/webview/OidcPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/webview/OidcPresenterTest.kt
index 60cc1285e3..70d6da7783 100644
--- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/webview/OidcPresenterTest.kt
+++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/webview/OidcPresenterTest.kt
@@ -35,8 +35,7 @@ import org.junit.Test
class OidcPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt
index 6fe3248ec3..edf198fe35 100644
--- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt
+++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt
@@ -31,8 +31,7 @@ import org.junit.Test
class ChangeAccountProviderPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt
index 0ec396694b..f348839c97 100644
--- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt
+++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt
@@ -39,8 +39,7 @@ import org.junit.Test
class ConfirmAccountProviderPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt
index 1848940966..a2b0fae449 100644
--- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt
+++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt
@@ -38,8 +38,7 @@ import org.junit.Test
class LoginPasswordPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt
index 89d1a22b0c..84bd6925db 100644
--- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt
+++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt
@@ -38,8 +38,7 @@ import org.junit.Test
class SearchAccountProviderPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListPresenterTest.kt
index dadcbe186a..590fa8efc0 100644
--- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListPresenterTest.kt
+++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListPresenterTest.kt
@@ -37,8 +37,7 @@ import org.junit.Test
class WaitListPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/features/logout/api/src/main/res/values-de/translations.xml b/features/logout/api/src/main/res/values-de/translations.xml
deleted file mode 100644
index 0cd8ac389a..0000000000
--- a/features/logout/api/src/main/res/values-de/translations.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
- "Möchtest du Dich wirklich abmelden?"
- "Abmelden"
- "Abmeldung läuft…"
- "Abmelden"
- "Abmelden"
-
diff --git a/features/logout/api/src/main/res/values-fr/translations.xml b/features/logout/api/src/main/res/values-fr/translations.xml
index b6d5137072..d4d8b8477b 100644
--- a/features/logout/api/src/main/res/values-fr/translations.xml
+++ b/features/logout/api/src/main/res/values-fr/translations.xml
@@ -1,8 +1,8 @@
- "Êtes-vous sûr de vouloir vous déconnecter?"
+ "Êtes-vous sûr de vouloir vous déconnecter ?"
"Se déconnecter"
- "Déconnexion en cours…"
+ "Déconnexion…"
"Se déconnecter"
"Se déconnecter"
diff --git a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPreferencePresenterTest.kt b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPreferencePresenterTest.kt
index 9527012e28..52e673cba6 100644
--- a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPreferencePresenterTest.kt
+++ b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPreferencePresenterTest.kt
@@ -32,8 +32,7 @@ import org.junit.Test
class LogoutPreferencePresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt
index 9df5399727..1febdd6092 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt
@@ -126,7 +126,12 @@ private fun HtmlBody(
when (val node = nodes.next()) {
is TextNode -> {
if (!node.isBlank) {
- ClickableLinkText(text = node.text(), interactionSource = interactionSource)
+ ClickableLinkText(
+ text = node.text(),
+ interactionSource = interactionSource,
+ onClick = onTextClicked,
+ onLongClick = onTextLongClicked,
+ )
}
}
is Element -> {
diff --git a/features/messages/impl/src/main/res/values-de/translations.xml b/features/messages/impl/src/main/res/values-de/translations.xml
index 482065c887..5d43a8167f 100644
--- a/features/messages/impl/src/main/res/values-de/translations.xml
+++ b/features/messages/impl/src/main/res/values-de/translations.xml
@@ -1,41 +1,5 @@
-
- - "%1$d Raumänderung"
- - "%1$d Raumänderungen"
-
- "Kamera"
- "Foto aufnehmen"
- "Video aufnehmen"
- "Anhang"
- "Foto- & Video-Bibliothek"
- "Standort"
- "Umfrage"
- "Der Nachrichtenverlauf ist in diesem Raum derzeit nicht verfügbar"
- "Benutzerdetails konnten nicht abgerufen werden"
- "Möchtest du sie wieder einladen?"
- "Du bist allein in diesem Chat"
- "Nachricht kopiert"
- "Du bist keine Berechtigung, um in diesem Raum zu posten"
- "Benutzerdefinierte Einstellung zulassen"
- "Das Aktivieren dieser Option wird die Standardeinstellungen überschreiben."
- "Benachrichtige mich in diesem Chat für"
- "Du kannst es in deinem %1$s ändern."
- "Globale Einstellungen"
- "Standardeinstellung"
- "Benutzerdefinierte Einstellung entfernen"
- "Beim Laden der Benachrichtigungseinstellungen ist ein Fehler aufgetreten."
- "Wiederherstellung des Standardmodus fehlgeschlagen. Bitte versuche es erneut."
- "Fehler beim Einstellen des Modus. Bitte versuche es erneut."
- "Alle Nachrichten"
- "Nur Erwähnungen und Schlüsselwörter"
- "In diesem Raum, benachrichtige mich für"
- "Weniger anzeigen"
- "Mehr anzeigen"
- "Erneut senden"
- "Ihre Nachricht konnte nicht gesendet werden"
- "Emoji hinzufügen"
- "Weniger anzeigen"
- "Fehler bei der Verarbeitung von Medien zum Hochladen, bitte versuche es erneut."
+ "Textformatierung"
"Entfernen"
diff --git a/features/messages/impl/src/main/res/values-fr/translations.xml b/features/messages/impl/src/main/res/values-fr/translations.xml
index e88ddc43cb..5bbd8e85b8 100644
--- a/features/messages/impl/src/main/res/values-fr/translations.xml
+++ b/features/messages/impl/src/main/res/values-fr/translations.xml
@@ -1,36 +1,42 @@
- - "%1$d changement dans la conversation"
- - "%1$d changements dans la conversation"
+ - "%1$d changement de salle"
+ - "%1$d changements de salle"
"Appareil photo"
"Prendre une photo"
"Enregistrer une vidéo"
- "Pièce-jointe"
- "Gallerie photo et vidéo"
- "L’historique des messages n’est pas disponible actuellement dans ce salon"
- "Impossible de récupérer les détails de l’utilisateur"
- "Souhaitez-vous les inviter à revenir ?"
+ "Pièce jointe"
+ "Photothèque et vidéothèque"
+ "Emplacement"
+ "Sondage"
+ "Formatage du texte"
+ "L\'historique des messages n\'est actuellement pas disponible dans cette salle"
+ "Impossible de récupérer les détails de l\'utilisateur"
+ "Aimeriez-vous les inviter à revenir ?"
"Vous êtes seul dans ce chat"
"Message copié"
- "Vous n‘avez pas le droit de poster dans ce salon"
+ "Vous n\'êtes pas autorisé à publier dans cette salle"
"Autoriser les paramètres personnalisés"
- "Activer cette option remplacera votre paramètre par défaut"
- "Me notifier dans ce chat pour"
- "paramètres généraux"
+ "L\'activation de cette option annulera votre paramètre par défaut"
+ "Prévenez-moi dans ce chat pour"
+ "Vous pouvez le modifier dans votre %1$s."
+ "paramètres globaux"
"Paramètre par défaut"
- "Une erreur s’est produite lors du chargement des paramètres de notification."
- "Impossible de restaurer le mode par défaut, veuillez réessayer."
- "Impossible de régler le mode, veuillez réessayer."
+ "Supprimer le paramètre personnalisé"
+ "Une erreur s\'est produite lors du chargement des paramètres de notification."
+ "Échec de la restauration du mode par défaut, veuillez réessayer."
+ "Échec de la configuration du mode, veuillez réessayer."
"Tous les messages"
- "Mentions et mots-clés uniquement"
+ "Mentions et mots clés uniquement"
+ "Dans cette salle, prévenez-moi pour"
"Afficher moins"
"Afficher plus"
- "Renvoyer"
+ "Envoyer à nouveau"
"Votre message n\'a pas pu être envoyé"
- "Ajouter un emoji"
- "Montrer moins"
- "Échec du traitement du média avant son envoi, veuillez réessayer."
- "Supprimer"
+ "Ajouter un émoji"
+ "Afficher moins"
+ "Échec du traitement des médias à télécharger, veuillez réessayer."
+ "Enlever"
diff --git a/features/messages/impl/src/main/res/values-sk/translations.xml b/features/messages/impl/src/main/res/values-sk/translations.xml
index 2390e1e25e..2c98aab148 100644
--- a/features/messages/impl/src/main/res/values-sk/translations.xml
+++ b/features/messages/impl/src/main/res/values-sk/translations.xml
@@ -12,6 +12,7 @@
"Knižnica fotografií a videí"
"Poloha"
"Anketa"
+ "Formátovanie textu"
"História správ v tejto miestnosti nie je momentálne k dispozícii"
"Nepodarilo sa získať údaje o používateľovi"
"Chceli by ste ich pozvať späť?"
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt
index c1c7e1c993..c9ef88f18c 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt
@@ -86,8 +86,7 @@ import kotlin.time.Duration.Companion.milliseconds
class MessagesPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
private val mockMediaUrl: Uri = mockk("localMediaUri")
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt
index 5e68906216..2682a71207 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt
@@ -41,8 +41,7 @@ import org.junit.Test
class ActionListPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt
index 5a66fe13b8..30c8d761b3 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt
@@ -43,8 +43,7 @@ import org.junit.Test
class AttachmentsPreviewPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
private val mediaPreProcessor = FakeMediaPreProcessor()
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/forward/ForwardMessagesPresenterTests.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/forward/ForwardMessagesPresenterTests.kt
index 7b6f48403f..c3a70deac6 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/forward/ForwardMessagesPresenterTests.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/forward/ForwardMessagesPresenterTests.kt
@@ -39,8 +39,7 @@ import org.junit.Test
class ForwardMessagesPresenterTests {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt
index 97ee81bcb6..6401f60f47 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt
@@ -44,8 +44,7 @@ private val TESTED_MEDIA_INFO = aFileInfo()
class MediaViewerPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/report/ReportMessagePresenterTests.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/report/ReportMessagePresenterTests.kt
index 97b0227def..8244562622 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/report/ReportMessagePresenterTests.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/report/ReportMessagePresenterTests.kt
@@ -35,8 +35,7 @@ import org.junit.Test
class ReportMessagePresenterTests {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt
index 07a4800ffb..3f66269fe1 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt
@@ -24,6 +24,7 @@ import app.cash.molecule.moleculeFlow
import app.cash.turbine.ReceiveTurbine
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
+import im.vector.app.features.analytics.plan.Composer
import io.element.android.features.messages.impl.messagecomposer.AttachmentsState
import io.element.android.features.messages.impl.messagecomposer.MessageComposerContextImpl
import io.element.android.features.messages.impl.messagecomposer.MessageComposerEvents
@@ -57,6 +58,7 @@ import io.element.android.libraries.textcomposer.Message
import io.element.android.libraries.textcomposer.MessageComposerMode
import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.tests.testutils.WarmUpRule
+import io.element.android.tests.testutils.waitForPredicate
import io.mockk.mockk
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -67,8 +69,7 @@ import java.io.File
class MessageComposerPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
private val pickerProvider = FakePickerProvider().apply {
@@ -208,6 +209,15 @@ class MessageComposerPresenterTest {
val messageSentState = awaitItem()
assertThat(messageSentState.richTextEditorState.messageHtml).isEqualTo("")
assertThat(messageSentState.canSendMessage).isFalse()
+ waitForPredicate { analyticsService.capturedEvents.size == 1 }
+ assertThat(analyticsService.capturedEvents).containsExactly(
+ Composer(
+ inThread = false,
+ isEditing = false,
+ isReply = false,
+ messageType = Composer.MessageType.Text,
+ )
+ )
}
}
@@ -240,6 +250,14 @@ class MessageComposerPresenterTest {
assertThat(messageSentState.richTextEditorState.messageHtml).isEqualTo("")
assertThat(messageSentState.canSendMessage).isFalse()
assertThat(fakeMatrixRoom.editMessageCalls.first()).isEqualTo(ANOTHER_MESSAGE to ANOTHER_MESSAGE)
+ assertThat(analyticsService.capturedEvents).containsExactly(
+ Composer(
+ inThread = false,
+ isEditing = true,
+ isReply = false,
+ messageType = Composer.MessageType.Text,
+ )
+ )
}
}
@@ -272,6 +290,14 @@ class MessageComposerPresenterTest {
assertThat(messageSentState.richTextEditorState.messageHtml).isEqualTo("")
assertThat(messageSentState.canSendMessage).isFalse()
assertThat(fakeMatrixRoom.editMessageCalls.first()).isEqualTo(ANOTHER_MESSAGE to ANOTHER_MESSAGE)
+ assertThat(analyticsService.capturedEvents).containsExactly(
+ Composer(
+ inThread = false,
+ isEditing = true,
+ isReply = false,
+ messageType = Composer.MessageType.Text,
+ )
+ )
}
}
@@ -304,6 +330,14 @@ class MessageComposerPresenterTest {
assertThat(messageSentState.richTextEditorState.messageHtml).isEqualTo("")
assertThat(messageSentState.canSendMessage).isFalse()
assertThat(fakeMatrixRoom.replyMessageParameter).isEqualTo(A_REPLY to A_REPLY)
+ assertThat(analyticsService.capturedEvents).containsExactly(
+ Composer(
+ inThread = false,
+ isEditing = false,
+ isReply = true,
+ messageType = Composer.MessageType.Text,
+ )
+ )
}
}
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt
index eb5a7b72e1..0435be3cb1 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt
@@ -51,8 +51,7 @@ import java.util.Date
class TimelinePresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/customreaction/CustomReactionPresenterTests.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/customreaction/CustomReactionPresenterTests.kt
index 9f9b7358d8..40912c0cc0 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/customreaction/CustomReactionPresenterTests.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/customreaction/CustomReactionPresenterTests.kt
@@ -33,8 +33,7 @@ import org.junit.Test
class CustomReactionPresenterTests {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
private val presenter = CustomReactionPresenter(emojibaseProvider = FakeEmojibaseProvider())
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/reactionsummary/ReactionSummaryPresenterTests.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/reactionsummary/ReactionSummaryPresenterTests.kt
index c2a415ea44..7b18d890df 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/reactionsummary/ReactionSummaryPresenterTests.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/reactionsummary/ReactionSummaryPresenterTests.kt
@@ -37,8 +37,7 @@ import org.junit.Test
class ReactionSummaryPresenterTests {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
private val aggregatedReaction = anAggregatedReaction(userId = A_USER_ID, key = "👍", isHighlighted = true)
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/retrysendmenu/RetrySendMenuPresenterTests.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/retrysendmenu/RetrySendMenuPresenterTests.kt
index 5a23eba6da..011db1f038 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/retrysendmenu/RetrySendMenuPresenterTests.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/retrysendmenu/RetrySendMenuPresenterTests.kt
@@ -32,8 +32,7 @@ import org.junit.Test
class RetrySendMenuPresenterTests {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
private val room = FakeMatrixRoom()
diff --git a/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt b/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt
index 7c03be6a41..dd586d4576 100644
--- a/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt
+++ b/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt
@@ -109,7 +109,9 @@ fun ConnectivityIndicatorContainer(
} else {
WindowInsets.statusBars.asPaddingValues().calculateTopPadding() + 6.dp
}
- val target = remember(isOnline) { if (isOnline) 0.dp else statusBarTopPadding }
+ val target = remember(isIndicatorVisible.targetState, statusBarTopPadding) {
+ if (!isIndicatorVisible.targetState) 0.dp else statusBarTopPadding
+ }
val animationStateOffset by animateDpAsState(
targetValue = target,
animationSpec = spring(
diff --git a/features/onboarding/impl/src/main/res/values-de/translations.xml b/features/onboarding/impl/src/main/res/values-de/translations.xml
deleted file mode 100644
index 82e20c3509..0000000000
--- a/features/onboarding/impl/src/main/res/values-de/translations.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- "Manuell anmelden"
- "Mit QR-Code anmelden"
- "Konto erstellen"
- "Sicher kommunizieren und zusammenarbeiten"
- "Willkommen beim schnellsten Element aller Zeiten. Optimiert für Geschwindigkeit und Einfachheit."
- "Willkommen zur %1$s. Verbessert, für Geschwindigkeit und Einfachheit."
- "Sei in deinem Element"
-
diff --git a/features/onboarding/impl/src/main/res/values-fr/translations.xml b/features/onboarding/impl/src/main/res/values-fr/translations.xml
index 5ed96ebf60..cbcfd477f0 100644
--- a/features/onboarding/impl/src/main/res/values-fr/translations.xml
+++ b/features/onboarding/impl/src/main/res/values-fr/translations.xml
@@ -1,10 +1,10 @@
- "Se connecter manuellement"
- "Se connecter avec un code QR"
+ "Connectez-vous manuellement"
+ "Connectez-vous avec le code QR"
"Créer un compte"
- "Communiquer et collaborer en toute sécurité"
- "Bienvenue dans l’Element le plus rapide de tous les temps. Surpuissant pour plus de vitesse et de simplicité."
- "Bienvenue dans %1$s. Affiné pour plus de rapidité et de simplicité."
+ "Communiquez et collaborez en toute sécurité"
+ "Bienvenue dans l\'Element le plus rapide de tous les temps. Boosté pour plus de rapidité et de simplicité."
+ "Bienvenue sur %1$s. Boosté, pour rapidité et simplicité."
"Soyez dans votre Element"
diff --git a/features/onboarding/impl/src/main/res/values-zh-rTW/translations.xml b/features/onboarding/impl/src/main/res/values-zh-rTW/translations.xml
index 5150b50c9a..22c9d70004 100644
--- a/features/onboarding/impl/src/main/res/values-zh-rTW/translations.xml
+++ b/features/onboarding/impl/src/main/res/values-zh-rTW/translations.xml
@@ -4,5 +4,5 @@
"使用 QR code 登入"
"建立帳號"
"歡迎使用有史以來最快的 Element。速度超快,操作簡便。"
- "得心應手"
+ "Be in your element"
diff --git a/features/onboarding/impl/src/test/kotlin/io/element/android/features/onboarding/impl/OnBoardingPresenterTest.kt b/features/onboarding/impl/src/test/kotlin/io/element/android/features/onboarding/impl/OnBoardingPresenterTest.kt
index 538fba98d5..9a356aa488 100644
--- a/features/onboarding/impl/src/test/kotlin/io/element/android/features/onboarding/impl/OnBoardingPresenterTest.kt
+++ b/features/onboarding/impl/src/test/kotlin/io/element/android/features/onboarding/impl/OnBoardingPresenterTest.kt
@@ -29,8 +29,7 @@ import org.junit.Test
class OnBoardingPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerView.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerView.kt
index 3e52cc5fc7..4e7a23094b 100644
--- a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerView.kt
+++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerView.kt
@@ -41,6 +41,7 @@ import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.IconToggleButton
import io.element.android.libraries.designsystem.theme.components.LinearProgressIndicator
import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.theme.progressIndicatorTrackColor
import io.element.android.libraries.designsystem.toEnabledColor
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonPlurals
@@ -111,6 +112,7 @@ fun PollAnswerView(
answerItem.isSelected -> 1f
else -> 0f
},
+ trackColor = ElementTheme.colors.progressIndicatorTrackColor,
strokeCap = StrokeCap.Round,
)
}
diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollView.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollView.kt
index 8e3de07574..0355b34375 100644
--- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollView.kt
+++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollView.kt
@@ -18,6 +18,7 @@ package io.element.android.features.poll.impl.create
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
@@ -25,6 +26,7 @@ import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
+import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
@@ -32,6 +34,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
@@ -61,6 +64,8 @@ import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.matrix.api.poll.PollKind
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -68,6 +73,8 @@ fun CreatePollView(
state: CreatePollState,
modifier: Modifier = Modifier,
) {
+ val coroutineScope = rememberCoroutineScope()
+
val navBack = { state.eventSink(CreatePollEvents.ConfirmNavBack) }
BackHandler(onBack = navBack)
if (state.showConfirmation) ConfirmationDialog(
@@ -76,6 +83,7 @@ fun CreatePollView(
onDismiss = { state.eventSink(CreatePollEvents.HideConfirmation) }
)
val questionFocusRequester = remember { FocusRequester() }
+ val answerFocusRequester = remember { FocusRequester() }
LaunchedEffect(Unit) {
questionFocusRequester.requestFocus()
}
@@ -102,40 +110,43 @@ fun CreatePollView(
)
},
) { paddingValues ->
+ val lazyListState = rememberLazyListState()
LazyColumn(
modifier = Modifier
.padding(paddingValues)
.consumeWindowInsets(paddingValues)
.imePadding()
.fillMaxSize(),
+ state = lazyListState,
) {
item {
- Text(
- text = stringResource(id = R.string.screen_create_poll_question_desc),
- modifier = Modifier.padding(start = 32.dp),
- style = ElementTheme.typography.fontBodyMdRegular,
- )
- }
- item {
- ListItem(
- headlineContent = {
- OutlinedTextField(
- value = state.question,
- onValueChange = {
- state.eventSink(CreatePollEvents.SetQuestion(it))
- },
- modifier = Modifier
- .focusRequester(questionFocusRequester)
- .fillMaxWidth(),
- placeholder = {
- Text(text = stringResource(id = R.string.screen_create_poll_question_hint))
- },
- keyboardOptions = keyboardOptions,
- )
- }
- )
+ Column {
+ Text(
+ text = stringResource(id = R.string.screen_create_poll_question_desc),
+ modifier = Modifier.padding(start = 32.dp),
+ style = ElementTheme.typography.fontBodyMdRegular,
+ )
+ ListItem(
+ headlineContent = {
+ OutlinedTextField(
+ value = state.question,
+ onValueChange = {
+ state.eventSink(CreatePollEvents.SetQuestion(it))
+ },
+ modifier = Modifier
+ .focusRequester(questionFocusRequester)
+ .fillMaxWidth(),
+ placeholder = {
+ Text(text = stringResource(id = R.string.screen_create_poll_question_hint))
+ },
+ keyboardOptions = keyboardOptions,
+ )
+ }
+ )
+ }
}
itemsIndexed(state.answers) { index, answer ->
+ val isLastItem = index == state.answers.size - 1
ListItem(
headlineContent = {
OutlinedTextField(
@@ -143,7 +154,9 @@ fun CreatePollView(
onValueChange = {
state.eventSink(CreatePollEvents.SetAnswer(index, it))
},
- modifier = Modifier.fillMaxWidth(),
+ modifier = Modifier
+ .then(if (isLastItem) Modifier.focusRequester(answerFocusRequester) else Modifier)
+ .fillMaxWidth(),
placeholder = {
Text(text = stringResource(id = R.string.screen_create_poll_answer_hint, index + 1))
},
@@ -170,22 +183,28 @@ fun CreatePollView(
iconSource = IconSource.Vector(Icons.Default.Add),
),
style = ListItemStyle.Primary,
- onClick = { state.eventSink(CreatePollEvents.AddAnswer) },
+ onClick = {
+ state.eventSink(CreatePollEvents.AddAnswer)
+ coroutineScope.launch(Dispatchers.Main) {
+ lazyListState.animateScrollToItem(state.answers.size + 1)
+ answerFocusRequester.requestFocus()
+ }
+ },
)
}
}
item {
- HorizontalDivider()
- }
- item {
- ListItem(
- headlineContent = { Text(text = stringResource(id = R.string.screen_create_poll_anonymous_headline)) },
- supportingContent = { Text(text = stringResource(id = R.string.screen_create_poll_anonymous_desc)) },
- trailingContent = ListItemContent.Switch(
- checked = state.pollKind == PollKind.Undisclosed,
- onChange = { state.eventSink(CreatePollEvents.SetPollKind(if (it) PollKind.Undisclosed else PollKind.Disclosed)) },
- ),
- )
+ Column {
+ HorizontalDivider()
+ ListItem(
+ headlineContent = { Text(text = stringResource(id = R.string.screen_create_poll_anonymous_headline)) },
+ supportingContent = { Text(text = stringResource(id = R.string.screen_create_poll_anonymous_desc)) },
+ trailingContent = ListItemContent.Switch(
+ checked = state.pollKind == PollKind.Undisclosed,
+ onChange = { state.eventSink(CreatePollEvents.SetPollKind(if (it) PollKind.Undisclosed else PollKind.Disclosed)) },
+ ),
+ )
+ }
}
}
}
diff --git a/features/poll/impl/src/main/res/values-de/translations.xml b/features/poll/impl/src/main/res/values-de/translations.xml
new file mode 100644
index 0000000000..762afd5fd8
--- /dev/null
+++ b/features/poll/impl/src/main/res/values-de/translations.xml
@@ -0,0 +1,11 @@
+
+
+ "Option hinzufügen"
+ "Ergebnisse erst nach Ende der Umfrage anzeigen"
+ "Anonyme Umfrage"
+ "Option %1$d"
+ "Bist du sicher, dass du diese Umfrage verwerfen willst?"
+ "Umfrage verwerfen"
+ "Frage oder Thema"
+ "Umfrage erstellen"
+
diff --git a/features/poll/impl/src/main/res/values-fr/translations.xml b/features/poll/impl/src/main/res/values-fr/translations.xml
new file mode 100644
index 0000000000..adfcfd274d
--- /dev/null
+++ b/features/poll/impl/src/main/res/values-fr/translations.xml
@@ -0,0 +1,12 @@
+
+
+ "Ajouter une option"
+ "Afficher les résultats uniquement après la fin du sondage"
+ "Masquer les votes"
+ "Option %1$d"
+ "Êtes-vous sûr de vouloir supprimer ce sondage ?"
+ "Supprimer le sondage"
+ "Question ou sujet"
+ "Quel est le sujet du sondage ?"
+ "Créer un sondage"
+
diff --git a/features/poll/impl/src/main/res/values-sk/translations.xml b/features/poll/impl/src/main/res/values-sk/translations.xml
new file mode 100644
index 0000000000..5e2d9ee7bc
--- /dev/null
+++ b/features/poll/impl/src/main/res/values-sk/translations.xml
@@ -0,0 +1,12 @@
+
+
+ "Pridať možnosť"
+ "Zobraziť výsledky až po skončení ankety"
+ "Anonymná anketa"
+ "Možnosť %1$d"
+ "Ste si istí, že chcete túto anketu zahodiť?"
+ "Odstrániť anketu"
+ "Otázka alebo téma"
+ "O čom je anketa?"
+ "Vytvoriť anketu"
+
diff --git a/features/poll/impl/src/main/res/values/localazy.xml b/features/poll/impl/src/main/res/values/localazy.xml
index 4c80a4ce45..3c5006d0af 100644
--- a/features/poll/impl/src/main/res/values/localazy.xml
+++ b/features/poll/impl/src/main/res/values/localazy.xml
@@ -2,7 +2,7 @@
"Add option"
"Show results only after poll ends"
- "Anonymous Poll"
+ "Hide votes"
"Option %1$d"
"Are you sure you want to discard this poll?"
"Discard Poll"
diff --git a/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenterTest.kt b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenterTest.kt
index a58fb5476b..8e16084a59 100644
--- a/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenterTest.kt
+++ b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenterTest.kt
@@ -35,8 +35,7 @@ import org.junit.Test
class CreatePollPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
private var navUpInvocationsCount = 0
diff --git a/features/preferences/impl/build.gradle.kts b/features/preferences/impl/build.gradle.kts
index 2773379ccc..e2df4cfa9a 100644
--- a/features/preferences/impl/build.gradle.kts
+++ b/features/preferences/impl/build.gradle.kts
@@ -40,6 +40,8 @@ dependencies {
implementation(projects.libraries.featureflag.api)
implementation(projects.libraries.featureflag.ui)
implementation(projects.libraries.network)
+ implementation(projects.libraries.pushstore.api)
+ implementation(projects.libraries.pushstore.test)
implementation(projects.libraries.testtags)
implementation(projects.libraries.uiStrings)
implementation(projects.features.rageshake.api)
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt
index 872520105f..85ae3ac55f 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt
@@ -33,6 +33,8 @@ import io.element.android.features.preferences.api.PreferencesEntryPoint
import io.element.android.features.preferences.impl.about.AboutNode
import io.element.android.features.preferences.impl.analytics.AnalyticsSettingsNode
import io.element.android.features.preferences.impl.developer.DeveloperSettingsNode
+import io.element.android.features.preferences.impl.notifications.NotificationSettingsNode
+import io.element.android.features.preferences.impl.notifications.edit.EditDefaultNotificationSettingNode
import io.element.android.features.preferences.impl.developer.tracing.ConfigureTracingNode
import io.element.android.features.preferences.impl.root.PreferencesRootNode
import io.element.android.libraries.architecture.BackstackNode
@@ -69,6 +71,12 @@ class PreferencesFlowNode @AssistedInject constructor(
@Parcelize
data object About : NavTarget
+
+ @Parcelize
+ data object NotificationSettings : NavTarget
+
+ @Parcelize
+ data class EditDefaultNotificationSetting(val isOneToOne: Boolean) : NavTarget
}
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
@@ -94,6 +102,10 @@ class PreferencesFlowNode @AssistedInject constructor(
override fun onOpenDeveloperSettings() {
backstack.push(NavTarget.DeveloperSettings)
}
+
+ override fun onOpenNotificationSettings() {
+ backstack.push(NavTarget.NotificationSettings)
+ }
}
createNode(buildContext, plugins = listOf(callback))
}
@@ -114,6 +126,18 @@ class PreferencesFlowNode @AssistedInject constructor(
NavTarget.AnalyticsSettings -> {
createNode(buildContext)
}
+ NavTarget.NotificationSettings -> {
+ val notificationSettingsCallback = object : NotificationSettingsNode.Callback {
+ override fun editDefaultNotificationMode(isOneToOne: Boolean) {
+ backstack.push(NavTarget.EditDefaultNotificationSetting(isOneToOne))
+ }
+ }
+ createNode(buildContext, listOf(notificationSettingsCallback))
+ }
+ is NavTarget.EditDefaultNotificationSetting -> {
+ val input = EditDefaultNotificationSettingNode.Inputs(navTarget.isOneToOne)
+ createNode(buildContext, plugins = listOf(input))
+ }
}
}
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsEvents.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsEvents.kt
new file mode 100644
index 0000000000..374b8078ca
--- /dev/null
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsEvents.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.preferences.impl.notifications
+
+sealed interface NotificationSettingsEvents {
+
+ data object RefreshSystemNotificationsEnabled : NotificationSettingsEvents
+ data class SetNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvents
+ data class SetAtRoomNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvents
+ data class SetCallNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvents
+ data object FixConfigurationMismatch : NotificationSettingsEvents
+ data object ClearConfigurationMismatchError : NotificationSettingsEvents
+}
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsNode.kt
new file mode 100644
index 0000000000..0e3861c5ec
--- /dev/null
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsNode.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.preferences.impl.notifications
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.bumble.appyx.core.modality.BuildContext
+import com.bumble.appyx.core.node.Node
+import com.bumble.appyx.core.plugin.Plugin
+import com.bumble.appyx.core.plugin.plugins
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import io.element.android.anvilannotations.ContributesNode
+import io.element.android.libraries.di.SessionScope
+
+@ContributesNode(SessionScope::class)
+class NotificationSettingsNode @AssistedInject constructor(
+ @Assisted buildContext: BuildContext,
+ @Assisted plugins: List,
+ private val presenter: NotificationSettingsPresenter,
+) : Node(buildContext, plugins = plugins) {
+
+ interface Callback : Plugin {
+ fun editDefaultNotificationMode(isOneToOne: Boolean)
+ }
+
+ private val callbacks = plugins()
+
+ private fun openEditDefault(isOneToOne: Boolean) {
+ callbacks.forEach { it.editDefaultNotificationMode(isOneToOne) }
+ }
+
+ @Composable
+ override fun View(modifier: Modifier) {
+ val state = presenter.present()
+ NotificationSettingsView(
+ state = state,
+ onOpenEditDefault = { openEditDefault(isOneToOne = it) },
+ onBackPressed = ::navigateUp,
+ modifier = modifier,
+ )
+ }
+}
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt
new file mode 100644
index 0000000000..697d5887f0
--- /dev/null
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.preferences.impl.notifications
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import io.element.android.libraries.architecture.Presenter
+import io.element.android.libraries.matrix.api.MatrixClient
+import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
+import io.element.android.libraries.matrix.api.room.RoomNotificationMode
+import io.element.android.libraries.pushstore.api.UserPushStore
+import io.element.android.libraries.pushstore.api.UserPushStoreFactory
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.FlowPreview
+import kotlinx.coroutines.flow.debounce
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.seconds
+
+class NotificationSettingsPresenter @Inject constructor(
+ private val notificationSettingsService: NotificationSettingsService,
+ private val userPushStoreFactory: UserPushStoreFactory,
+ private val matrixClient: MatrixClient,
+ private val systemNotificationsEnabledProvider: SystemNotificationsEnabledProvider
+) : Presenter {
+ @Composable
+ override fun present(): NotificationSettingsState {
+ val userPushStore = remember { userPushStoreFactory.create(matrixClient.sessionId) }
+ val systemNotificationsEnabled: MutableState = remember {
+ mutableStateOf(systemNotificationsEnabledProvider.notificationsEnabled())
+ }
+
+ val localCoroutineScope = rememberCoroutineScope()
+ val appNotificationsEnabled = userPushStore
+ .getNotificationEnabledForDevice()
+ .collectAsState(initial = false)
+
+ val matrixSettings: MutableState = remember {
+ mutableStateOf(NotificationSettingsState.MatrixSettings.Uninitialized)
+ }
+
+ LaunchedEffect(Unit) {
+ fetchSettings(matrixSettings)
+ observeNotificationSettings(matrixSettings)
+ }
+
+ fun handleEvents(event: NotificationSettingsEvents) {
+ when (event) {
+ is NotificationSettingsEvents.SetAtRoomNotificationsEnabled -> localCoroutineScope.setAtRoomNotificationsEnabled(event.enabled)
+ is NotificationSettingsEvents.SetCallNotificationsEnabled -> localCoroutineScope.setCallNotificationsEnabled(event.enabled)
+ is NotificationSettingsEvents.SetNotificationsEnabled -> localCoroutineScope.setNotificationsEnabled(userPushStore, event.enabled)
+ NotificationSettingsEvents.ClearConfigurationMismatchError -> {
+ matrixSettings.value = NotificationSettingsState.MatrixSettings.Invalid(fixFailed = false)
+ }
+ NotificationSettingsEvents.FixConfigurationMismatch -> localCoroutineScope.fixConfigurationMismatch(matrixSettings)
+ NotificationSettingsEvents.RefreshSystemNotificationsEnabled -> {
+ systemNotificationsEnabled.value = systemNotificationsEnabledProvider.notificationsEnabled()
+ }
+ }
+ }
+
+ return NotificationSettingsState(
+ matrixSettings = matrixSettings.value,
+ appSettings = NotificationSettingsState.AppSettings(
+ systemNotificationsEnabled = systemNotificationsEnabled.value,
+ appNotificationsEnabled = appNotificationsEnabled.value
+ ),
+ eventSink = ::handleEvents
+ )
+ }
+
+ @OptIn(FlowPreview::class)
+ private fun CoroutineScope.observeNotificationSettings(target: MutableState) {
+ notificationSettingsService.notificationSettingsChangeFlow
+ .debounce(0.5.seconds)
+ .onEach {
+ fetchSettings(target)
+ }
+ .launchIn(this)
+ }
+
+ private fun CoroutineScope.fetchSettings(target: MutableState) = launch {
+ val groupDefaultMode = notificationSettingsService.getDefaultRoomNotificationMode(isEncrypted = false, isOneToOne = false).getOrThrow()
+ val encryptedGroupDefaultMode = notificationSettingsService.getDefaultRoomNotificationMode(isEncrypted = true, isOneToOne = false).getOrThrow()
+
+ val oneToOneDefaultMode = notificationSettingsService.getDefaultRoomNotificationMode(isEncrypted = false, isOneToOne = true).getOrThrow()
+ val encryptedOneToOneDefaultMode = notificationSettingsService.getDefaultRoomNotificationMode(isEncrypted = true, isOneToOne = true).getOrThrow()
+
+ if(groupDefaultMode != encryptedGroupDefaultMode || oneToOneDefaultMode != encryptedOneToOneDefaultMode) {
+ target.value = NotificationSettingsState.MatrixSettings.Invalid(fixFailed = false)
+ return@launch
+ }
+
+ val callNotificationsEnabled = notificationSettingsService.isCallEnabled().getOrThrow()
+ val atRoomNotificationsEnabled = notificationSettingsService.isRoomMentionEnabled().getOrThrow()
+
+ target.value = NotificationSettingsState.MatrixSettings.Valid(
+ atRoomNotificationsEnabled = atRoomNotificationsEnabled,
+ callNotificationsEnabled = callNotificationsEnabled,
+ defaultGroupNotificationMode = encryptedGroupDefaultMode,
+ defaultOneToOneNotificationMode = encryptedOneToOneDefaultMode,
+ )
+ }
+
+ private fun CoroutineScope.fixConfigurationMismatch(target: MutableState) = launch {
+ runCatching {
+ val groupDefaultMode = notificationSettingsService.getDefaultRoomNotificationMode(isEncrypted = false, isOneToOne = false).getOrThrow()
+ val encryptedGroupDefaultMode = notificationSettingsService.getDefaultRoomNotificationMode(isEncrypted = true, isOneToOne = false).getOrThrow()
+
+ if (groupDefaultMode != encryptedGroupDefaultMode) {
+ notificationSettingsService.setDefaultRoomNotificationMode(
+ isEncrypted = encryptedGroupDefaultMode != RoomNotificationMode.ALL_MESSAGES,
+ mode = RoomNotificationMode.ALL_MESSAGES,
+ isOneToOne = false,
+ )
+ }
+
+ val oneToOneDefaultMode = notificationSettingsService.getDefaultRoomNotificationMode(isEncrypted = false, isOneToOne = true).getOrThrow()
+ val encryptedOneToOneDefaultMode = notificationSettingsService.getDefaultRoomNotificationMode(isEncrypted = true, isOneToOne = true).getOrThrow()
+
+ if (oneToOneDefaultMode != encryptedOneToOneDefaultMode) {
+ notificationSettingsService.setDefaultRoomNotificationMode(
+ isEncrypted = encryptedOneToOneDefaultMode != RoomNotificationMode.ALL_MESSAGES,
+ mode = RoomNotificationMode.ALL_MESSAGES,
+ isOneToOne = true,
+ )
+ }
+ }.fold(
+ onSuccess = {},
+ onFailure = {
+ target.value = NotificationSettingsState.MatrixSettings.Invalid(fixFailed = true)
+ }
+ )
+ }
+
+ private fun CoroutineScope.setAtRoomNotificationsEnabled(enabled: Boolean) = launch {
+ notificationSettingsService.setRoomMentionEnabled(enabled)
+ }
+
+ private fun CoroutineScope.setCallNotificationsEnabled(enabled: Boolean) = launch {
+ notificationSettingsService.setCallEnabled(enabled)
+ }
+
+ private fun CoroutineScope.setNotificationsEnabled(userPushStore: UserPushStore, enabled: Boolean) = launch {
+ userPushStore.setNotificationEnabledForDevice(enabled)
+ }
+}
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsState.kt
new file mode 100644
index 0000000000..cf3cf6e3d0
--- /dev/null
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsState.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.preferences.impl.notifications
+
+import androidx.compose.runtime.Immutable
+import io.element.android.libraries.matrix.api.room.RoomNotificationMode
+
+@Immutable
+data class NotificationSettingsState(
+ val matrixSettings: MatrixSettings,
+ val appSettings: AppSettings,
+ val eventSink: (NotificationSettingsEvents) -> Unit,
+) {
+ sealed interface MatrixSettings {
+ data object Uninitialized : MatrixSettings
+ data class Valid(
+ val atRoomNotificationsEnabled: Boolean,
+ val callNotificationsEnabled: Boolean,
+ val defaultGroupNotificationMode: RoomNotificationMode?,
+ val defaultOneToOneNotificationMode: RoomNotificationMode?,
+ ) : MatrixSettings
+
+ data class Invalid(
+ val fixFailed: Boolean
+ ) : MatrixSettings
+ }
+
+ data class AppSettings(
+ val systemNotificationsEnabled: Boolean,
+ val appNotificationsEnabled: Boolean,
+ )
+}
+
+
+
+
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsStateProvider.kt
new file mode 100644
index 0000000000..1e653c47e0
--- /dev/null
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsStateProvider.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.preferences.impl.notifications
+
+import androidx.compose.ui.tooling.preview.PreviewParameterProvider
+import io.element.android.libraries.matrix.api.room.RoomNotificationMode
+
+open class NotificationSettingsStateProvider : PreviewParameterProvider {
+ override val values: Sequence
+ get() = sequenceOf(
+ aNotificationSettingsState(),
+ )
+}
+
+fun aNotificationSettingsState() = NotificationSettingsState(
+ matrixSettings = NotificationSettingsState.MatrixSettings.Valid(
+ atRoomNotificationsEnabled = true,
+ callNotificationsEnabled = true,
+ defaultGroupNotificationMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
+ defaultOneToOneNotificationMode = RoomNotificationMode.ALL_MESSAGES,
+ ),
+ appSettings = NotificationSettingsState.AppSettings(
+ systemNotificationsEnabled = false,
+ appNotificationsEnabled = true,
+ ),
+ eventSink = {}
+)
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt
new file mode 100644
index 0000000000..4b17eba4ca
--- /dev/null
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.preferences.impl.notifications
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.NotificationsOff
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.tooling.preview.PreviewParameter
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.Lifecycle
+import io.element.android.libraries.androidutils.system.startNotificationSettingsIntent
+import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
+import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
+import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch
+import io.element.android.libraries.designsystem.components.preferences.PreferenceText
+import io.element.android.libraries.designsystem.components.preferences.PreferenceView
+import io.element.android.libraries.designsystem.preview.ElementPreviewDark
+import io.element.android.libraries.designsystem.preview.ElementPreviewLight
+import io.element.android.libraries.designsystem.theme.components.Button
+import io.element.android.libraries.designsystem.theme.components.ButtonSize
+import io.element.android.libraries.designsystem.theme.components.Surface
+import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.designsystem.utils.OnLifecycleEvent
+import io.element.android.libraries.matrix.api.room.RoomNotificationMode
+import io.element.android.libraries.theme.ElementTheme
+import io.element.android.libraries.ui.strings.CommonStrings
+
+/**
+ * A view that allows a user edit their global notification settings.
+ */
+@Composable
+fun NotificationSettingsView(
+ state: NotificationSettingsState,
+ onOpenEditDefault: (isOneToOne: Boolean) -> Unit,
+ onBackPressed: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ OnLifecycleEvent { _, event ->
+ when (event) {
+ Lifecycle.Event.ON_RESUME -> state.eventSink.invoke(NotificationSettingsEvents.RefreshSystemNotificationsEnabled)
+ else -> Unit
+ }
+ }
+ PreferenceView(
+ modifier = modifier,
+ onBackPressed = onBackPressed,
+ title = stringResource(id = CommonStrings.screen_notification_settings_title)
+ ) {
+
+ when (state.matrixSettings) {
+ is NotificationSettingsState.MatrixSettings.Invalid -> InvalidNotificationSettingsView(
+ showError = state.matrixSettings.fixFailed,
+ onContinueClicked = { state.eventSink(NotificationSettingsEvents.FixConfigurationMismatch) },
+ onDismissError = { state.eventSink(NotificationSettingsEvents.ClearConfigurationMismatchError) },
+ )
+ NotificationSettingsState.MatrixSettings.Uninitialized -> return@PreferenceView
+ is NotificationSettingsState.MatrixSettings.Valid -> NotificationSettingsContentView(
+ matrixSettings = state.matrixSettings,
+ systemSettings = state.appSettings,
+ onNotificationsEnabledChanged = { state.eventSink(NotificationSettingsEvents.SetNotificationsEnabled(it))},
+ onGroupChatsClicked = { onOpenEditDefault(false) },
+ onDirectChatsClicked = { onOpenEditDefault(true) },
+ onMentionNotificationsChanged = { state.eventSink(NotificationSettingsEvents.SetAtRoomNotificationsEnabled(it)) },
+// onCallsNotificationsChanged = { state.eventSink(NotificationSettingsEvents.SetCallNotificationsEnabled(it)) },
+ )
+ }
+ }
+}
+
+@Composable
+private fun NotificationSettingsContentView(
+ matrixSettings: NotificationSettingsState.MatrixSettings.Valid,
+ systemSettings: NotificationSettingsState.AppSettings,
+ onNotificationsEnabledChanged: (Boolean) -> Unit,
+ onGroupChatsClicked: () -> Unit,
+ onDirectChatsClicked: () -> Unit,
+ onMentionNotificationsChanged: (Boolean) -> Unit,
+// onCallsNotificationsChanged: (Boolean) -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ val context = LocalContext.current
+ if (systemSettings.appNotificationsEnabled && !systemSettings.systemNotificationsEnabled) {
+ PreferenceText(
+ icon = Icons.Filled.NotificationsOff,
+ title = stringResource(id = CommonStrings.screen_notification_settings_system_notifications_turned_off),
+ subtitle = stringResource(id = CommonStrings.screen_notification_settings_system_notifications_action_required,
+ stringResource(id = CommonStrings.screen_notification_settings_system_notifications_action_required_content_link)),
+ onClick = {
+ context.startNotificationSettingsIntent()
+ }
+ )
+ }
+
+ PreferenceSwitch(
+ modifier = modifier,
+ title = stringResource(id = CommonStrings.screen_notification_settings_enable_notifications),
+ isChecked = systemSettings.appNotificationsEnabled,
+ switchAlignment = Alignment.Top,
+ onCheckedChange = onNotificationsEnabledChanged
+ )
+
+ if (systemSettings.appNotificationsEnabled) {
+ PreferenceCategory(title = stringResource(id = CommonStrings.screen_notification_settings_notification_section_title)) {
+ PreferenceText(
+ title = stringResource(id = CommonStrings.screen_notification_settings_group_chats),
+ subtitle = getTitleForRoomNotificationMode(mode = matrixSettings.defaultGroupNotificationMode),
+ onClick = onGroupChatsClicked
+ )
+
+ PreferenceText(
+ title = stringResource(id = CommonStrings.screen_notification_settings_direct_chats),
+ subtitle = getTitleForRoomNotificationMode(mode = matrixSettings.defaultOneToOneNotificationMode),
+ onClick = onDirectChatsClicked
+ )
+ }
+
+ PreferenceCategory(title = stringResource(id = CommonStrings.screen_notification_settings_mode_mentions)) {
+ PreferenceSwitch(
+ modifier = Modifier,
+ title = stringResource(id = CommonStrings.screen_notification_settings_room_mention_label),
+ isChecked = matrixSettings.atRoomNotificationsEnabled,
+ switchAlignment = Alignment.Top,
+ onCheckedChange = onMentionNotificationsChanged
+ )
+ }
+ // We are removing the call notification toggle until call support has been added
+// PreferenceCategory(title = stringResource(id = CommonStrings.screen_notification_settings_additional_settings_section_title)) {
+// PreferenceSwitch(
+// modifier = Modifier,
+// title = stringResource(id = CommonStrings.screen_notification_settings_calls_label),
+// isChecked = matrixSettings.callNotificationsEnabled,
+// switchAlignment = Alignment.Top,
+// onCheckedChange = onCallsNotificationsChanged
+// )
+// }
+ }
+}
+
+@Composable
+private fun getTitleForRoomNotificationMode(mode: RoomNotificationMode?) =
+when(mode) {
+ RoomNotificationMode.ALL_MESSAGES -> stringResource(id = CommonStrings.screen_notification_settings_edit_mode_all_messages)
+ RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY -> stringResource(id = CommonStrings.screen_notification_settings_edit_mode_mentions_and_keywords)
+ RoomNotificationMode.MUTE -> stringResource(id = CommonStrings.common_mute)
+ null -> ""
+}
+
+@Composable
+private fun InvalidNotificationSettingsView(
+ showError: Boolean,
+ onContinueClicked: () -> Unit,
+ onDismissError: () -> Unit,
+ modifier: Modifier = Modifier
+) {
+ Box(modifier = modifier.padding(horizontal = 16.dp, vertical = 8.dp)) {
+ Surface(
+ Modifier.fillMaxWidth(),
+ shape = MaterialTheme.shapes.small,
+ color = MaterialTheme.colorScheme.surfaceVariant
+ ) {
+ Column(
+ Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp, vertical = 12.dp)
+ ) {
+ Row {
+ Text(
+ stringResource(CommonStrings.screen_notification_settings_configuration_mismatch),
+ modifier = Modifier.weight(1f),
+ style = ElementTheme.typography.fontBodyLgMedium,
+ color = MaterialTheme.colorScheme.primary,
+ textAlign = TextAlign.Start,
+ )
+ }
+ Spacer(modifier = Modifier.height(4.dp))
+ Text(
+ stringResource(CommonStrings.screen_notification_settings_configuration_mismatch_description),
+ style = ElementTheme.typography.fontBodyMdRegular,
+ )
+ Spacer(modifier = Modifier.height(12.dp))
+ Button(
+ text = stringResource(CommonStrings.action_continue),
+ size = ButtonSize.Medium,
+ modifier = Modifier.fillMaxWidth(),
+ onClick = onContinueClicked,
+ )
+ }
+ }
+ }
+ if(showError) {
+ ErrorDialog(
+ title = stringResource(id = CommonStrings.dialog_title_error),
+ content = stringResource(id = CommonStrings.screen_notification_settings_failed_fixing_configuration),
+ onDismiss = onDismissError
+ )
+ }
+}
+
+@Preview
+@Composable
+internal fun NotificationSettingsViewLightPreview(@PreviewParameter(NotificationSettingsStateProvider::class) state: NotificationSettingsState) =
+ ElementPreviewLight { ContentToPreview(state) }
+
+@Preview
+@Composable
+internal fun NotificationSettingsViewDarkPreview(@PreviewParameter(NotificationSettingsStateProvider::class) state: NotificationSettingsState) =
+ ElementPreviewDark { ContentToPreview(state) }
+
+@Composable
+private fun ContentToPreview(state: NotificationSettingsState) {
+ NotificationSettingsView(
+ state = state,
+ onBackPressed = {},
+ onOpenEditDefault = {},
+ )
+}
+
+@Preview
+@Composable
+internal fun InvalidNotificationSettingsViewightPreview() =
+ ElementPreviewLight { InvalidNotificationSettingsContentToPreview() }
+
+@Preview
+@Composable
+internal fun InvalidNotificationSettingsViewDarkPreview() =
+ ElementPreviewDark { InvalidNotificationSettingsContentToPreview() }
+
+@Composable
+private fun InvalidNotificationSettingsContentToPreview() {
+ InvalidNotificationSettingsView(
+ showError = false,
+ onContinueClicked = {},
+ onDismissError = {},
+ )
+}
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/SystemNotificationsEnabledProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/SystemNotificationsEnabledProvider.kt
new file mode 100644
index 0000000000..f33b1cb773
--- /dev/null
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/SystemNotificationsEnabledProvider.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.preferences.impl.notifications
+
+import android.content.Context
+import androidx.core.app.NotificationManagerCompat
+import com.squareup.anvil.annotations.ContributesBinding
+import io.element.android.libraries.di.AppScope
+import io.element.android.libraries.di.ApplicationContext
+import io.element.android.libraries.di.SingleIn
+import javax.inject.Inject
+
+interface SystemNotificationsEnabledProvider {
+ fun notificationsEnabled(): Boolean
+}
+@SingleIn(AppScope::class)
+@ContributesBinding(AppScope::class, boundType = SystemNotificationsEnabledProvider::class)
+class DefaultSystemNotificationsEnabledProvider @Inject constructor(
+ @ApplicationContext private val context: Context,
+): SystemNotificationsEnabledProvider {
+ override fun notificationsEnabled(): Boolean {
+ return NotificationManagerCompat.from(context).areNotificationsEnabled()
+ }
+}
+
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/DefaultNotificationSettingOption.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/DefaultNotificationSettingOption.kt
new file mode 100644
index 0000000000..e60b2bc8dc
--- /dev/null
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/DefaultNotificationSettingOption.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.preferences.impl.notifications.edit
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.selection.selectable
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.unit.dp
+import io.element.android.libraries.designsystem.preview.DayNightPreviews
+import io.element.android.libraries.designsystem.preview.ElementPreview
+import io.element.android.libraries.designsystem.theme.components.RadioButton
+import io.element.android.libraries.designsystem.theme.components.Text
+import io.element.android.libraries.matrix.api.room.RoomNotificationMode
+import io.element.android.libraries.theme.ElementTheme
+import io.element.android.libraries.ui.strings.CommonStrings
+
+@Composable
+fun DefaultNotificationSettingOption(
+ mode: RoomNotificationMode,
+ modifier: Modifier = Modifier,
+ isSelected: Boolean = false,
+ onOptionSelected: (RoomNotificationMode) -> Unit = {},
+) {
+ val subtitle = when(mode) {
+ RoomNotificationMode.ALL_MESSAGES -> stringResource(id = CommonStrings.screen_notification_settings_edit_mode_all_messages)
+ RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY -> stringResource(id = CommonStrings.screen_notification_settings_edit_mode_mentions_and_keywords)
+ else -> ""
+ }
+ Row(
+ modifier
+ .fillMaxWidth()
+ .selectable(
+ selected = isSelected,
+ onClick = { onOptionSelected(mode) },
+ role = Role.RadioButton,
+ )
+ .padding(8.dp),
+ ) {
+ Column(
+ Modifier
+ .weight(1f)
+ .padding(horizontal = 8.dp)
+ .align(Alignment.CenterVertically)
+ ) {
+ Text(
+ text = subtitle,
+ style = ElementTheme.typography.fontBodyLgRegular,
+ )
+ }
+
+ RadioButton(
+ modifier = Modifier
+ .align(Alignment.CenterVertically)
+ .size(48.dp),
+ selected = isSelected,
+ onClick = null // null recommended for accessibility with screenreaders
+ )
+ }
+}
+@DayNightPreviews
+@Composable
+internal fun DefaultNotificationSettingOptionPreview() = ElementPreview { ContentToPreview() }
+
+@Composable
+private fun ContentToPreview() {
+ Column {
+ DefaultNotificationSettingOption(
+ mode = RoomNotificationMode.ALL_MESSAGES,
+ isSelected = true,
+ )
+ DefaultNotificationSettingOption(
+ mode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
+ isSelected = false,
+ )
+ }
+}
+
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingNode.kt
new file mode 100644
index 0000000000..6c4fd646f4
--- /dev/null
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingNode.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.preferences.impl.notifications.edit
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.bumble.appyx.core.modality.BuildContext
+import com.bumble.appyx.core.node.Node
+import com.bumble.appyx.core.plugin.Plugin
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import io.element.android.anvilannotations.ContributesNode
+import io.element.android.libraries.architecture.NodeInputs
+import io.element.android.libraries.architecture.inputs
+import io.element.android.libraries.di.SessionScope
+
+@ContributesNode(SessionScope::class)
+class EditDefaultNotificationSettingNode @AssistedInject constructor(
+ @Assisted buildContext: BuildContext,
+ @Assisted plugins: List,
+ presenterFactory: EditDefaultNotificationSettingPresenter.Factory
+) : Node(buildContext, plugins = plugins) {
+
+ data class Inputs(
+ val isOneToOne: Boolean
+ ) : NodeInputs
+
+ private val inputs = inputs()
+ private val presenter = presenterFactory.create(inputs.isOneToOne)
+
+ @Composable
+ override fun View(modifier: Modifier) {
+ val state = presenter.present()
+ EditDefaultNotificationSettingView(
+ state = state,
+ onBackPressed = ::navigateUp,
+ modifier = modifier
+ )
+ }
+}
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt
new file mode 100644
index 0000000000..764b37c52d
--- /dev/null
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.preferences.impl.notifications.edit
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import io.element.android.libraries.architecture.Presenter
+import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
+import io.element.android.libraries.matrix.api.room.RoomNotificationMode
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.FlowPreview
+import kotlinx.coroutines.flow.debounce
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+import kotlin.time.Duration.Companion.seconds
+
+class EditDefaultNotificationSettingPresenter @AssistedInject constructor(
+ private val notificationSettingsService: NotificationSettingsService,
+ @Assisted private val isOneToOne: Boolean,
+) : Presenter {
+ @AssistedFactory
+ interface Factory {
+ fun create(oneToOne: Boolean): EditDefaultNotificationSettingPresenter
+ }
+ @Composable
+ override fun present(): EditDefaultNotificationSettingState {
+
+ val mode: MutableState = remember {
+ mutableStateOf(null)
+ }
+ val localCoroutineScope = rememberCoroutineScope()
+ LaunchedEffect(Unit) {
+ fetchSettings(mode)
+ observeNotificationSettings(mode)
+ }
+
+ fun handleEvents(event: EditDefaultNotificationSettingStateEvents) {
+ when (event) {
+ is EditDefaultNotificationSettingStateEvents.SetNotificationMode -> localCoroutineScope.setDefaultNotificationMode(event.mode)
+ }
+ }
+
+ return EditDefaultNotificationSettingState(
+ isOneToOne = isOneToOne,
+ mode = mode.value,
+ eventSink = ::handleEvents
+ )
+ }
+
+ private fun CoroutineScope.fetchSettings(mode: MutableState) = launch {
+ mode.value = notificationSettingsService.getDefaultRoomNotificationMode(isEncrypted = true, isOneToOne = isOneToOne).getOrThrow()
+ }
+
+ @OptIn(FlowPreview::class)
+ private fun CoroutineScope.observeNotificationSettings(mode: MutableState) {
+ notificationSettingsService.notificationSettingsChangeFlow
+ .debounce(0.5.seconds)
+ .onEach {
+ fetchSettings(mode)
+ }
+ .launchIn(this)
+ }
+
+ private fun CoroutineScope.setDefaultNotificationMode(mode: RoomNotificationMode) = launch {
+ // On modern clients, we don't have different settings for encrypted and non-encrypted rooms (Legacy clients did).
+ notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = true, mode = mode, isOneToOne = isOneToOne)
+ notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = false, mode = mode, isOneToOne = isOneToOne)
+ }
+
+}
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingState.kt
new file mode 100644
index 0000000000..62c708d988
--- /dev/null
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingState.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.preferences.impl.notifications.edit
+
+import io.element.android.libraries.matrix.api.room.RoomNotificationMode
+
+data class EditDefaultNotificationSettingState(
+ val isOneToOne: Boolean,
+ val mode: RoomNotificationMode?,
+ val eventSink: (EditDefaultNotificationSettingStateEvents) -> Unit,
+)
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingStateEvents.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingStateEvents.kt
new file mode 100644
index 0000000000..75c9b6c1a4
--- /dev/null
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingStateEvents.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.preferences.impl.notifications.edit
+
+import io.element.android.libraries.matrix.api.room.RoomNotificationMode
+
+sealed interface EditDefaultNotificationSettingStateEvents {
+ data class SetNotificationMode(val mode: RoomNotificationMode): EditDefaultNotificationSettingStateEvents
+}
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt
new file mode 100644
index 0000000000..4cc95af71f
--- /dev/null
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.preferences.impl.notifications.edit
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.selection.selectableGroup
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
+import io.element.android.libraries.designsystem.components.preferences.PreferenceView
+import io.element.android.libraries.matrix.api.room.RoomNotificationMode
+import io.element.android.libraries.ui.strings.CommonStrings
+
+/**
+ * A view that allows a user to edit the default notification setting for rooms. This can be set separately
+ * for one-to-one and group rooms, indicated by [EditDefaultNotificationSettingState.isOneToOne].
+ */
+@Composable
+fun EditDefaultNotificationSettingView(
+ state: EditDefaultNotificationSettingState,
+ onBackPressed: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+
+ val title = if(state.isOneToOne) {
+ CommonStrings.screen_notification_settings_direct_chats
+ } else {
+ CommonStrings.screen_notification_settings_group_chats
+ }
+ PreferenceView(
+ modifier = modifier,
+ onBackPressed = onBackPressed,
+ title = stringResource(id = title)
+ ) {
+
+ // Only ALL_MESSAGES and MENTIONS_AND_KEYWORDS_ONLY are valid global defaults.
+ val validModes = listOf(RoomNotificationMode.ALL_MESSAGES, RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
+
+ val categoryTitle = if(state.isOneToOne) {
+ CommonStrings.screen_notification_settings_edit_screen_direct_section_header
+ } else {
+ CommonStrings.screen_notification_settings_edit_screen_group_section_header
+ }
+ PreferenceCategory(title = stringResource(id = categoryTitle)) {
+
+ if (state.mode != null) {
+ Column(modifier = Modifier.selectableGroup()) {
+ validModes.forEach { item ->
+ DefaultNotificationSettingOption(
+ mode = item,
+ isSelected = state.mode == item,
+ onOptionSelected = { state.eventSink(EditDefaultNotificationSettingStateEvents.SetNotificationMode(it)) }
+ )
+ }
+ }
+ }
+ }
+ }
+}
+
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt
index e90569b40e..0f297d14dd 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt
@@ -44,6 +44,7 @@ class PreferencesRootNode @AssistedInject constructor(
fun onOpenAnalytics()
fun onOpenAbout()
fun onOpenDeveloperSettings()
+ fun onOpenNotificationSettings()
}
private fun onOpenBugReport() {
@@ -72,6 +73,10 @@ class PreferencesRootNode @AssistedInject constructor(
}
}
+ private fun onOpenNotificationSettings() {
+ plugins().forEach { it.onOpenNotificationSettings() }
+ }
+
@Composable
override fun View(modifier: Modifier) {
val state = presenter.present()
@@ -87,6 +92,7 @@ class PreferencesRootNode @AssistedInject constructor(
onOpenDeveloperSettings = this::onOpenDeveloperSettings,
onSuccessLogout = { onSuccessLogout(activity, it) },
onManageAccountClicked = { onManageAccountClicked(activity, state.accountManagementUrl) },
+ onOpenNotificationSettings = this::onOpenNotificationSettings
)
}
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt
index af82a3ab2a..37ea66b31f 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt
@@ -29,6 +29,8 @@ import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.meta.BuildType
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
import io.element.android.libraries.designsystem.utils.collectSnackbarMessageAsState
+import io.element.android.libraries.featureflag.api.FeatureFlagService
+import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.api.user.getCurrentUser
@@ -46,6 +48,7 @@ class PreferencesRootPresenter @Inject constructor(
private val buildType: BuildType,
private val versionFormatter: VersionFormatter,
private val snackbarDispatcher: SnackbarDispatcher,
+ private val featureFlagService: FeatureFlagService,
) : Presenter {
@Composable
@@ -60,6 +63,11 @@ class PreferencesRootPresenter @Inject constructor(
val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState()
val hasAnalyticsProviders = remember { analyticsService.getAvailableAnalyticsProviders().isNotEmpty() }
+ val showNotificationSettings = remember { mutableStateOf(false) }
+ LaunchedEffect(Unit) {
+ showNotificationSettings.value = featureFlagService.isFeatureEnabled(FeatureFlags.NotificationSettings)
+ }
+
// We should display the 'complete verification' option if the current session can be verified
val showCompleteVerification by sessionVerificationService.canVerifySessionFlow.collectAsState(false)
@@ -81,6 +89,7 @@ class PreferencesRootPresenter @Inject constructor(
accountManagementUrl = accountManagementUrl.value,
showAnalyticsSettings = hasAnalyticsProviders,
showDeveloperSettings = showDeveloperSettings,
+ showNotificationSettings = showNotificationSettings.value,
snackbarMessage = snackbarMessage,
)
}
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt
index af3a090630..967450031a 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt
@@ -28,5 +28,6 @@ data class PreferencesRootState(
val accountManagementUrl: String?,
val showAnalyticsSettings: Boolean,
val showDeveloperSettings: Boolean,
+ val showNotificationSettings: Boolean,
val snackbarMessage: SnackbarMessage?,
)
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt
index 931a560c1d..8dd6b807f8 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt
@@ -28,5 +28,6 @@ fun aPreferencesRootState() = PreferencesRootState(
accountManagementUrl = "aUrl",
showAnalyticsSettings = true,
showDeveloperSettings = true,
+ showNotificationSettings = true,
snackbarMessage = SnackbarMessage(CommonStrings.common_verification_complete),
)
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt
index df108c03a3..a2449424e6 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt
@@ -24,6 +24,7 @@ import androidx.compose.material.icons.outlined.DeveloperMode
import androidx.compose.material.icons.outlined.Help
import androidx.compose.material.icons.outlined.InsertChart
import androidx.compose.material.icons.outlined.ManageAccounts
+import androidx.compose.material.icons.outlined.Notifications
import androidx.compose.material.icons.outlined.VerifiedUser
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@@ -58,6 +59,7 @@ fun PreferencesRootView(
onOpenAbout: () -> Unit,
onOpenDeveloperSettings: () -> Unit,
onSuccessLogout: (logoutUrlResult: String?) -> Unit,
+ onOpenNotificationSettings: () -> Unit,
modifier: Modifier = Modifier,
) {
val snackbarHostState = rememberSnackbarHostState(snackbarMessage = state.snackbarMessage)
@@ -92,6 +94,13 @@ fun PreferencesRootView(
onClick = onOpenAnalytics,
)
}
+ if(state.showNotificationSettings) {
+ PreferenceText(
+ title = stringResource(id = CommonStrings.screen_notification_settings_title),
+ icon = Icons.Outlined.Notifications,
+ onClick = onOpenNotificationSettings,
+ )
+ }
PreferenceText(
title = stringResource(id = CommonStrings.action_report_bug),
icon = Icons.Outlined.BugReport,
@@ -153,5 +162,6 @@ private fun ContentToPreview(matrixUser: MatrixUser) {
onVerifyClicked = {},
onSuccessLogout = {},
onManageAccountClicked = {},
+ onOpenNotificationSettings = {},
)
}
diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/tasks/ComputeCacheSizeUseCase.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/tasks/ComputeCacheSizeUseCase.kt
index 661f6493ec..0eb6e5b613 100644
--- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/tasks/ComputeCacheSizeUseCase.kt
+++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/tasks/ComputeCacheSizeUseCase.kt
@@ -18,8 +18,8 @@ package io.element.android.features.preferences.impl.tasks
import android.content.Context
import com.squareup.anvil.annotations.ContributesBinding
-import io.element.android.libraries.androidutils.filesize.FileSizeFormatter
import io.element.android.libraries.androidutils.file.getSizeOfFiles
+import io.element.android.libraries.androidutils.filesize.FileSizeFormatter
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.di.SessionScope
diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/about/AboutPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/about/AboutPresenterTest.kt
index ad98756992..2cfd73b614 100644
--- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/about/AboutPresenterTest.kt
+++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/about/AboutPresenterTest.kt
@@ -27,8 +27,7 @@ import org.junit.Test
class AboutPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsPresenterTest.kt
index 775ad1b183..9570258092 100644
--- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsPresenterTest.kt
+++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsPresenterTest.kt
@@ -30,8 +30,7 @@ import org.junit.Test
class AnalyticsSettingsPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt
index 17ffa2fe73..88778d4227 100644
--- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt
+++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt
@@ -35,8 +35,7 @@ import org.junit.Test
class DeveloperSettingsPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingPresenterTest.kt
index 5388e97e9e..5ca874b6d6 100644
--- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingPresenterTest.kt
+++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingPresenterTest.kt
@@ -30,8 +30,7 @@ import org.junit.Test
class ConfigureTracingPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
fun `present - initial state`() = runTest {
diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/EditDefaultNotificationSettingsPresenterTests.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/EditDefaultNotificationSettingsPresenterTests.kt
new file mode 100644
index 0000000000..e8c9ff4fe5
--- /dev/null
+++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/EditDefaultNotificationSettingsPresenterTests.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.preferences.impl.notifications
+
+import app.cash.molecule.RecompositionMode
+import app.cash.molecule.moleculeFlow
+import app.cash.turbine.test
+import com.google.common.truth.Truth
+import io.element.android.features.preferences.impl.notifications.edit.EditDefaultNotificationSettingPresenter
+import io.element.android.features.preferences.impl.notifications.edit.EditDefaultNotificationSettingStateEvents
+import io.element.android.libraries.matrix.api.room.RoomNotificationMode
+import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
+import io.element.android.tests.testutils.consumeItemsUntilPredicate
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+
+class EditDefaultNotificationSettingsPresenterTests {
+ @Test
+ fun `present - ensures initial state is correct`() = runTest {
+ val notificationSettingsService = FakeNotificationSettingsService()
+ val presenter = EditDefaultNotificationSettingPresenter(notificationSettingsService = notificationSettingsService, isOneToOne = false)
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ val initialState = awaitItem()
+ Truth.assertThat(initialState.mode).isNull()
+ Truth.assertThat(initialState.isOneToOne).isFalse()
+
+ val loadedState = consumeItemsUntilPredicate {
+ it.mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
+ }.last()
+ Truth.assertThat(loadedState.mode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
+ }
+ }
+
+ @Test
+ fun `present - edit default notification setting`() = runTest {
+ val notificationSettingsService = FakeNotificationSettingsService()
+ val presenter = EditDefaultNotificationSettingPresenter(notificationSettingsService = notificationSettingsService, isOneToOne = false)
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ awaitItem().eventSink(EditDefaultNotificationSettingStateEvents.SetNotificationMode(RoomNotificationMode.ALL_MESSAGES))
+ val loadedState = consumeItemsUntilPredicate {
+ it.mode == RoomNotificationMode.ALL_MESSAGES
+ }.last()
+ Truth.assertThat(loadedState.mode).isEqualTo(RoomNotificationMode.ALL_MESSAGES)
+ }
+ }
+}
diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/FakeSystemNotificationsEnabledProvider.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/FakeSystemNotificationsEnabledProvider.kt
new file mode 100644
index 0000000000..1a7c1b5004
--- /dev/null
+++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/FakeSystemNotificationsEnabledProvider.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.preferences.impl.notifications
+
+class FakeSystemNotificationsEnabledProvider: SystemNotificationsEnabledProvider {
+ override fun notificationsEnabled(): Boolean {
+ return true
+ }
+}
diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTests.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTests.kt
new file mode 100644
index 0000000000..70d15c7a71
--- /dev/null
+++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTests.kt
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2023 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 io.element.android.features.preferences.impl.notifications
+
+import app.cash.molecule.RecompositionMode
+import app.cash.molecule.moleculeFlow
+import app.cash.turbine.test
+import com.element.android.libraries.pushstore.test.userpushstore.FakeUserPushStoreFactory
+import com.google.common.truth.Truth
+import io.element.android.libraries.matrix.api.room.RoomNotificationMode
+import io.element.android.libraries.matrix.test.FakeMatrixClient
+import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
+import io.element.android.tests.testutils.consumeItemsUntilPredicate
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import kotlin.time.Duration.Companion.milliseconds
+
+class NotificationSettingsPresenterTests {
+ @Test
+ fun `present - ensures initial state is correct`() = runTest {
+ val presenter = aNotificationPresenter()
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ val initialState = awaitItem()
+ Truth.assertThat(initialState.appSettings.appNotificationsEnabled).isFalse()
+ Truth.assertThat(initialState.appSettings.systemNotificationsEnabled).isTrue()
+ Truth.assertThat(initialState.matrixSettings).isEqualTo(NotificationSettingsState.MatrixSettings.Uninitialized)
+
+ val loadedState = consumeItemsUntilPredicate {
+ it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid
+ }.last()
+ Truth.assertThat(loadedState.appSettings.appNotificationsEnabled).isTrue()
+ Truth.assertThat(loadedState.appSettings.systemNotificationsEnabled).isTrue()
+ val valid = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid
+ Truth.assertThat(valid?.atRoomNotificationsEnabled).isFalse()
+ Truth.assertThat(valid?.callNotificationsEnabled).isFalse()
+ Truth.assertThat(valid?.defaultGroupNotificationMode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
+ Truth.assertThat(valid?.defaultOneToOneNotificationMode).isEqualTo(RoomNotificationMode.ALL_MESSAGES)
+ cancelAndIgnoreRemainingEvents()
+ }
+ }
+
+ @Test
+ fun `present - default group notification mode changed`() = runTest {
+ val notificationSettingsService = FakeNotificationSettingsService()
+ val presenter = aNotificationPresenter(notificationSettingsService)
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+
+ notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = true, isOneToOne = false, mode = RoomNotificationMode.ALL_MESSAGES)
+ notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = false, isOneToOne = false, mode = RoomNotificationMode.ALL_MESSAGES)
+ val updatedState = consumeItemsUntilPredicate {
+ (it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)
+ ?.defaultGroupNotificationMode == RoomNotificationMode.ALL_MESSAGES
+ }.last()
+ val valid = updatedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid
+ Truth.assertThat(valid?.defaultGroupNotificationMode).isEqualTo(RoomNotificationMode.ALL_MESSAGES)
+ }
+ }
+
+ @Test
+ fun `present - notification settings mismatched`() = runTest {
+ val notificationSettingsService = FakeNotificationSettingsService()
+ val presenter = aNotificationPresenter(notificationSettingsService)
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+
+ notificationSettingsService.setDefaultRoomNotificationMode(
+ isEncrypted = true,
+ isOneToOne = false,
+ mode = RoomNotificationMode.ALL_MESSAGES
+ )
+ notificationSettingsService.setDefaultRoomNotificationMode(
+ isEncrypted = false,
+ isOneToOne = false,
+ mode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
+ )
+ val updatedState = consumeItemsUntilPredicate {
+ it.matrixSettings is NotificationSettingsState.MatrixSettings.Invalid
+ }.last()
+ Truth.assertThat(updatedState.matrixSettings).isEqualTo(NotificationSettingsState.MatrixSettings.Invalid(fixFailed = false))
+ }
+ }
+
+ @Test
+ fun `present - fix notification settings mismatched`() = runTest {
+ // Start with a mismatched configuration
+ val notificationSettingsService = FakeNotificationSettingsService(
+ initialEncryptedGroupDefaultMode = RoomNotificationMode.ALL_MESSAGES,
+ initialGroupDefaultMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
+ initialEncryptedOneToOneDefaultMode = RoomNotificationMode.ALL_MESSAGES,
+ initialOneToOneDefaultMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
+ )
+ val presenter = aNotificationPresenter(notificationSettingsService)
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ val initialState = awaitItem()
+ initialState.eventSink(NotificationSettingsEvents.FixConfigurationMismatch)
+ val fixedState = consumeItemsUntilPredicate(timeout = 2000.milliseconds) {
+ it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid
+ }.last()
+
+ val fixedMatrixState = fixedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid
+ Truth.assertThat(fixedMatrixState?.defaultGroupNotificationMode).isEqualTo(RoomNotificationMode.ALL_MESSAGES)
+ }
+ }
+
+ @Test
+ fun `present - set notifications enabled`() = runTest {
+ val presenter = aNotificationPresenter()
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ val loadedState = consumeItemsUntilPredicate {
+ it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid
+ }.last()
+ Truth.assertThat(loadedState.appSettings.appNotificationsEnabled).isTrue()
+
+ loadedState.eventSink(NotificationSettingsEvents.SetNotificationsEnabled(false))
+ val updatedState = consumeItemsUntilPredicate {
+ !it.appSettings.appNotificationsEnabled
+ }.last()
+ Truth.assertThat(updatedState.appSettings.appNotificationsEnabled).isFalse()
+ cancelAndIgnoreRemainingEvents()
+ }
+ }
+
+ @Test
+ fun `present - set call notifications enabled`() = runTest {
+ val presenter = aNotificationPresenter()
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ val loadedState = consumeItemsUntilPredicate {
+ (it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.callNotificationsEnabled == false
+ }.last()
+ val validMatrixState = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid
+ Truth.assertThat(validMatrixState?.callNotificationsEnabled).isFalse()
+
+ loadedState.eventSink(NotificationSettingsEvents.SetCallNotificationsEnabled(true))
+ val updatedState = consumeItemsUntilPredicate {
+ (it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.callNotificationsEnabled == true
+ }.last()
+ val updatedMatrixState = updatedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid
+ Truth.assertThat(updatedMatrixState?.callNotificationsEnabled).isTrue()
+ cancelAndIgnoreRemainingEvents()
+ }
+ }
+
+ @Test
+ fun `present - set atRoom notifications enabled`() = runTest {
+ val presenter = aNotificationPresenter()
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ val loadedState = consumeItemsUntilPredicate {
+ (it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.atRoomNotificationsEnabled == false
+ }.last()
+ val validMatrixState = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid
+ Truth.assertThat(validMatrixState?.atRoomNotificationsEnabled).isFalse()
+
+ loadedState.eventSink(NotificationSettingsEvents.SetAtRoomNotificationsEnabled(true))
+ val updatedState = consumeItemsUntilPredicate {
+ (it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.atRoomNotificationsEnabled == true
+ }.last()
+ val updatedMatrixState = updatedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid
+ Truth.assertThat(updatedMatrixState?.atRoomNotificationsEnabled).isTrue()
+ cancelAndIgnoreRemainingEvents()
+ }
+ }
+
+ private fun aNotificationPresenter(
+ notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService()
+ ) : NotificationSettingsPresenter {
+ val matrixClient = FakeMatrixClient(notificationSettingsService = notificationSettingsService)
+ return NotificationSettingsPresenter(
+ notificationSettingsService = notificationSettingsService,
+ userPushStoreFactory = FakeUserPushStoreFactory(),
+ matrixClient = matrixClient,
+ systemNotificationsEnabledProvider = FakeSystemNotificationsEnabledProvider(),
+ )
+ }
+}
diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt
index 5217519071..f246e8c852 100644
--- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt
+++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt
@@ -24,6 +24,7 @@ import io.element.android.features.logout.impl.DefaultLogoutPreferencePresenter
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.core.meta.BuildType
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
+import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
import io.element.android.libraries.matrix.test.A_USER_NAME
@@ -37,8 +38,7 @@ import org.junit.Test
class PreferencesRootPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
@@ -53,6 +53,7 @@ class PreferencesRootPresenterTest {
BuildType.DEBUG,
FakeVersionFormatter(),
SnackbarDispatcher(),
+ FakeFeatureFlagService()
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
diff --git a/features/rageshake/api/src/main/res/values-de/translations.xml b/features/rageshake/api/src/main/res/values-de/translations.xml
deleted file mode 100644
index f2446a4028..0000000000
--- a/features/rageshake/api/src/main/res/values-de/translations.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
- "%1$s ist bei der letzten Verwendung abgestürzt. Möchtest du uns einen Absturzbericht senden?"
- "Du scheinst frustriert das Telefon zu schütteln. Möchtest du den Fehlerberichtsbildschirm öffnen?"
-
diff --git a/features/rageshake/api/src/main/res/values-fr/translations.xml b/features/rageshake/api/src/main/res/values-fr/translations.xml
index 455ab1daef..8d57edb3e1 100644
--- a/features/rageshake/api/src/main/res/values-fr/translations.xml
+++ b/features/rageshake/api/src/main/res/values-fr/translations.xml
@@ -1,5 +1,5 @@
- "%1$s a planté la dernière fois qu\'il a été utilisé. Souhaitez-vous partager un rapport de crash avec nous ?"
+ "%1$s s\'est arrêté la dernière fois qu\'il a été utilisé. Souhaitez-vous partager un rapport d\'incident avec nous ?"
"Vous semblez secouer le téléphone de frustration. Voulez-vous ouvrir le formulaire de rapport de problème ?"
diff --git a/features/rageshake/impl/src/main/res/values-de/translations.xml b/features/rageshake/impl/src/main/res/values-de/translations.xml
deleted file mode 100644
index 51b331a86f..0000000000
--- a/features/rageshake/impl/src/main/res/values-de/translations.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
- "Bildschirmfoto anhängen"
- "Ihr könnt mich kontaktieren, wenn ihr weitere Fragen habt"
- "Kontaktiere mich"
- "Bildschirmfoto bearbeiten"
- "Beschreibe bitte den Fehler. Was hast du gemacht? Was hätte passieren sollen? Was ist passiert? Bitte beschreibe alles mit so vielen Details wie möglich."
- "Beschreibe den Fehler…"
- "Wenn möglich, verfasse die Beschreibung bitte auf Englisch."
- "Absturzprotokolle senden"
- "Logs zulassen"
- "Bildschirmfoto senden"
- "Deiner Nachricht werden Protokolle beigefügt, um sicherzustellen, dass alles ordnungsgemäß funktioniert. Um deine Nachricht ohne Logs zu senden, deaktiviere diese Einstellung."
- "%1$s ist bei der letzten Verwendung abgestürzt. Möchtest du uns einen Absturzbericht senden?"
-
diff --git a/features/rageshake/impl/src/main/res/values-fr/translations.xml b/features/rageshake/impl/src/main/res/values-fr/translations.xml
index 53b95af6be..cd800881dc 100644
--- a/features/rageshake/impl/src/main/res/values-fr/translations.xml
+++ b/features/rageshake/impl/src/main/res/values-fr/translations.xml
@@ -1,8 +1,8 @@
- "Joindre une capture d\'écran"
+ "Joindre une capture d’écran"
"Vous pouvez me contacter si vous avez des questions complémentaires."
- "Me contacter"
+ "Contactez-moi"
"Modifier la capture d\'écran"
"S\'il vous plait, veuillez décrire le bogue. Qu\'avez-vous fait ? À quoi vous attendiez-vous ? Que s\'est-il réellement passé. Veuillez ajouter le plus de détails possible."
"Décrire le bogue"
@@ -11,5 +11,5 @@
"Autoriser à inclure les journaux techniques"
"Envoyer une capture d’écran"
"Pour vérifier que les choses fonctionnent comme prévu, des journaux techniques seront envoyés avec votre message. Pour l’envoyer sans ces journaux, désactivez ce paramètre."
- "%1$s a planté la dernière fois qu\'il a été utilisé. Souhaitez-vous partager un rapport de crash avec nous ?"
+ "%1$s s\'est arrêté la dernière fois qu\'il a été utilisé. Souhaitez-vous partager un rapport d\'incident avec nous ?"
diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt
index 204cd4aea1..3360ce3dd3 100644
--- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt
+++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt
@@ -36,8 +36,7 @@ const val A_LONG_DESCRIPTION = "I have seen a bug!"
class BugReportPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/crash/ui/CrashDetectionPresenterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/crash/ui/CrashDetectionPresenterTest.kt
index e77c1fd7e2..716f424ce3 100644
--- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/crash/ui/CrashDetectionPresenterTest.kt
+++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/crash/ui/CrashDetectionPresenterTest.kt
@@ -31,8 +31,7 @@ import org.junit.Test
class CrashDetectionPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/detection/RageshakeDetectionPresenterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/detection/RageshakeDetectionPresenterTest.kt
index d1d09131ad..a8a80fdc25 100644
--- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/detection/RageshakeDetectionPresenterTest.kt
+++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/detection/RageshakeDetectionPresenterTest.kt
@@ -38,8 +38,7 @@ import org.junit.Test
class RageshakeDetectionPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
companion object {
diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/preferences/RageshakePreferencesPresenterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/preferences/RageshakePreferencesPresenterTest.kt
index 74cd1906a9..c639adb98f 100644
--- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/preferences/RageshakePreferencesPresenterTest.kt
+++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/preferences/RageshakePreferencesPresenterTest.kt
@@ -31,8 +31,7 @@ import org.junit.Test
class RageshakePreferencesPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt
index b12d4710a0..34a0b2b2e1 100644
--- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt
@@ -106,7 +106,7 @@ class RoomDetailsPresenter @Inject constructor(
}
RoomDetailsEvent.UnmuteNotification -> {
scope.launch(dispatchers.io) {
- client.notificationSettingsService().unmuteRoom(room.roomId, room.isEncrypted, room.activeMemberCount)
+ client.notificationSettingsService().unmuteRoom(room.roomId, room.isEncrypted, room.isOneToOne)
}
}
}
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsPresenter.kt
index fd9f0bc930..a6e2477bcc 100644
--- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsPresenter.kt
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsPresenter.kt
@@ -94,7 +94,7 @@ class RoomNotificationSettingsPresenter @Inject constructor(
private fun CoroutineScope.getDefaultRoomNotificationMode(defaultRoomNotificationMode: MutableState) = launch {
defaultRoomNotificationMode.value = notificationSettingsService.getDefaultRoomNotificationMode(
room.isEncrypted,
- room.activeMemberCount
+ room.isOneToOne
).getOrThrow()
}
diff --git a/features/roomdetails/impl/src/main/res/values-cs/translations.xml b/features/roomdetails/impl/src/main/res/values-cs/translations.xml
index 90a9bb0190..defedd3bfb 100644
--- a/features/roomdetails/impl/src/main/res/values-cs/translations.xml
+++ b/features/roomdetails/impl/src/main/res/values-cs/translations.xml
@@ -25,6 +25,19 @@
"Aktualizace místnosti…"
"Nevyřízeno"
"Členové místnosti"
+ "Povolit vlastní nastavení"
+ "Zapnutím této funkce přepíšete výchozí nastavení"
+ "Upozornit mě v tomto chatu na"
+ "Můžete změnit ve vašem %1$s."
+ "globální nastavení"
+ "Výchozí nastavení"
+ "Odebrat vlastní nastavení"
+ "Při načítání nastavení oznámení došlo k chybě."
+ "Obnovení výchozího režimu se nezdařilo, zkuste to prosím znovu."
+ "Nastavení režimu se nezdařilo, zkuste to prosím znovu."
+ "Všechny zprávy"
+ "Pouze zmínky a klíčová slova"
+ "V této místnosti mě upozornit na"
"Zablokovat"
"Blokovaní uživatelé vám nebudou moci posílat zprávy a všechny jejich zprávy budou skryty. Můžete je kdykoli odblokovat."
"Zablokovat uživatele"
diff --git a/features/roomdetails/impl/src/main/res/values-de/translations.xml b/features/roomdetails/impl/src/main/res/values-de/translations.xml
index 8ae23e8152..7e33583ffd 100644
--- a/features/roomdetails/impl/src/main/res/values-de/translations.xml
+++ b/features/roomdetails/impl/src/main/res/values-de/translations.xml
@@ -1,37 +1,6 @@
-
- - "%1$d Person"
- - "%1$d Personen"
-
- "Thema hinzufügen"
- "Bereits Mitglied"
- "Bereits eingeladen"
- "Raum bearbeiten"
- "Es gab einen unbekannten Fehler und die Informationen konnten nicht geändert werden."
- "Raum konnte nicht aktualisiert werden"
- "Nachrichten sind mit Schlössern gesichert. Nur du und der Empfänger haben die eindeutigen Schlüssel, um sie zu entsperren."
- "Nachrichtenverschlüsselung aktiviert"
- "Beim Laden der Benachrichtigungseinstellungen ist ein Fehler aufgetreten."
- "Das Stummschalten dieses Raums ist fehlgeschlagen. Bitte versuche es erneut."
- "Die Stummschaltung dieses Raums konnte nicht aufgehoben werden. Bitte versuchen Sie es erneut."
- "Personen einladen"
- "Benutzerdefiniert"
- "Standard"
- "Benachrichtigungen"
- "Raumname"
- "Raum teilen"
- "Aktualisiere Raum…"
- "Ausstehend"
- "Raummitglieder"
- "Blockieren"
- "Blockierte Benutzer können dir keine Nachrichten senden und alle ihre Nachrichten werden ausgeblendet. Du kannst sie jederzeit entsperren."
- "Nutzer blockieren"
- "Blockierung aufheben"
- "Du wirst alle ihre Nachrichten wieder sehen."
- "Nutzer entblockieren"
"Raum verlassen"
- "Personen"
- "Sicherheit"
+ "Menschen"
"Thema"
diff --git a/features/roomdetails/impl/src/main/res/values-fr/translations.xml b/features/roomdetails/impl/src/main/res/values-fr/translations.xml
index 34fae4fea9..d8dbd878d4 100644
--- a/features/roomdetails/impl/src/main/res/values-fr/translations.xml
+++ b/features/roomdetails/impl/src/main/res/values-fr/translations.xml
@@ -1,45 +1,49 @@
- - "%1$d membre"
- - "%1$d membres"
+ - "%1$d personne"
+ - "%1$d personnes"
- "Définir un sujet"
+ "Ajouter un sujet"
"Déjà membre"
- "Déjà invité(e)"
- "Modifier le salon"
- "Une erreur inconnue s’est produite et les informations n’ont pas pu être modifiées."
- "Impossible de mettre à jour le salon"
- "Les messages sont sécurisés par des cadenas numériques. Seuls vous et les destinataires possédez les clés uniques pour les déverrouiller."
- "Chiffrement des messages activé"
- "Une erreur s’est produite lors du chargement des paramètres de notification."
- "Impossible de désactiver les notifications de cette salle, veuillez réessayer."
- "Impossible de réactiver les notifications de cette salle, veuillez réessayer."
+ "Déjà invité"
+ "Modifier la salle"
+ "Une erreur inconnue s\'est produite et les informations n\'ont pas pu être modifiées."
+ "Impossible de mettre à jour la salle"
+ "Les messages sont sécurisés par des verrous. Seuls vous et les destinataires possédez les clés uniques pour les déverrouiller."
+ "Cryptage des messages activé"
+ "Une erreur s\'est produite lors du chargement des paramètres de notification."
+ "Échec de la mise en sourdine de cette salle, veuillez réessayer."
+ "Échec de la désactivation de la mise en sourdine de cette salle, veuillez réessayer."
"Inviter des personnes"
"Personnalisé"
- "Par défaut"
+ "Défaut"
"Notifications"
- "Nom du salon"
- "Partager le salon"
- "Mise à jour du salon…"
+ "Nom de la salle"
+ "Partager la salle"
+ "Mise à jour de la salle…"
"En attente"
+ "Membres de la salle"
"Autoriser les paramètres personnalisés"
- "Activer cette option remplacera votre paramètre par défaut"
- "Me notifier dans ce chat pour"
- "paramètres généraux"
+ "L\'activation de cette option annulera votre paramètre par défaut"
+ "Prévenez-moi dans ce chat pour"
+ "Vous pouvez le modifier dans votre %1$s."
+ "paramètres globaux"
"Paramètre par défaut"
- "Une erreur s’est produite lors du chargement des paramètres de notification."
- "Impossible de restaurer le mode par défaut, veuillez réessayer."
- "Impossible de régler le mode, veuillez réessayer."
+ "Supprimer le paramètre personnalisé"
+ "Une erreur s\'est produite lors du chargement des paramètres de notification."
+ "Échec de la restauration du mode par défaut, veuillez réessayer."
+ "Échec de la configuration du mode, veuillez réessayer."
"Tous les messages"
- "Mentions et mots-clés uniquement"
+ "Mentions et mots clés uniquement"
+ "Dans cette salle, prévenez-moi pour"
"Bloquer"
"Les utilisateurs bloqués ne pourront pas vous envoyer de messages et tous leurs messages seront masqués. Vous pouvez les débloquer à tout moment."
"Bloquer l\'utilisateur"
"Débloquer"
"Vous pourrez à nouveau voir tous leurs messages."
- "Débloquer l\'utilisateur"
- "Quitter le salon"
+ "Débloquer un utilisateur"
+ "Quitter la salle"
"Personnes"
"Sécurité"
"Sujet"
diff --git a/features/roomdetails/impl/src/main/res/values-ro/translations.xml b/features/roomdetails/impl/src/main/res/values-ro/translations.xml
index a2be94aee9..f86fbb8bb5 100644
--- a/features/roomdetails/impl/src/main/res/values-ro/translations.xml
+++ b/features/roomdetails/impl/src/main/res/values-ro/translations.xml
@@ -24,6 +24,19 @@
"Se actualizează camera…"
"În așteptare"
"Membrii camerei"
+ "Permiteți setări personalizate"
+ "Activarea acestei opțiuni va anula setările implicite."
+ "Anunțați-mă în acestă cameră pentru"
+ "Îl puteți schimba în %1$s."
+ "Setări generale"
+ "Setare implicită"
+ "Stergeți setarea personalizată"
+ "A apărut o eroare la încărcarea setărilor pentry notificari."
+ "Nu s-a reușit restaurarea modului implicit, vă rugăm să încercați din nou."
+ "Nu s-a reușit setarea modului, vă rugăm să încercați din nou."
+ "Toate mesajele"
+ "Numai mențiuni și cuvinte cheie"
+ "În această cameră, anunțați-mă pentru"
"Blocați"
"Utilizatorii blocați nu vă vor putea trimite mesaje și toate mesajele lor vor fi ascunse. Puteți anula această acțiune oricând."
"Blocați utilizatorul"
diff --git a/features/roomdetails/impl/src/main/res/values-ru/translations.xml b/features/roomdetails/impl/src/main/res/values-ru/translations.xml
index 4154f041ff..cff08460c1 100644
--- a/features/roomdetails/impl/src/main/res/values-ru/translations.xml
+++ b/features/roomdetails/impl/src/main/res/values-ru/translations.xml
@@ -31,11 +31,13 @@
"Вы можете изменить его в своем %1$s."
"Основные Настройки"
"Настройка по умолчанию"
+ "Удалить пользовательскую настройку"
"Произошла ошибка при загрузке настроек уведомлений."
"Не удалось восстановить режим по умолчанию, попробуйте еще раз."
"Не удалось настроить режим, попробуйте еще раз."
"Все сообщения"
"Только упоминания и ключевые слова"
+ "В этой комнате уведомить меня о"
"Заблокировать"
"Заблокированные пользователи не смогут отправлять вам сообщения, а все их сообщения будут скрыты. Вы можете разблокировать их в любое время."
"Заблокировать пользователя"
diff --git a/features/roomdetails/impl/src/main/res/values-sk/translations.xml b/features/roomdetails/impl/src/main/res/values-sk/translations.xml
index 687c495ce7..01e0f717e1 100644
--- a/features/roomdetails/impl/src/main/res/values-sk/translations.xml
+++ b/features/roomdetails/impl/src/main/res/values-sk/translations.xml
@@ -31,11 +31,13 @@
"Môžete to zmeniť vo svojich %1$s."
"všeobecných nastaveniach"
"Predvolené nastavenie"
+ "Odstrániť vlastné nastavenie"
"Pri načítavaní nastavení oznámení došlo k chybe."
"Nepodarilo sa obnoviť predvolený režim, skúste to prosím znova."
"Nepodarilo sa nastaviť režim, skúste to prosím znova."
"Všetky správy"
"Iba zmienky a kľúčové slová"
+ "V tejto miestnosti ma upozorniť na"
"Zablokovať"
"Blokovaní používatelia vám nebudú môcť posielať správy a všetky ich správy budú skryté. Môžete ich kedykoľvek odblokovať."
"Zablokovať používateľa"
diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt
index 71695094b3..a6ffa0a289 100644
--- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt
+++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt
@@ -57,8 +57,7 @@ import kotlin.time.Duration.Companion.milliseconds
@ExperimentalCoroutinesApi
class RoomDetailsPresenterTests {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
private fun aRoomDetailsPresenter(
room: MatrixRoom,
@@ -67,7 +66,6 @@ class RoomDetailsPresenterTests {
notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService()
): RoomDetailsPresenter {
val matrixClient = FakeMatrixClient(notificationSettingsService = notificationSettingsService)
-
val roomMemberDetailsPresenterFactory = object : RoomMemberDetailsPresenter.Factory {
override fun create(roomMemberId: UserId): RoomMemberDetailsPresenter {
return RoomMemberDetailsPresenter(matrixClient, room, roomMemberId)
@@ -386,7 +384,7 @@ class RoomDetailsPresenterTests {
@Test
fun `present - mute room notifications`() = runTest {
val leaveRoomPresenter = LeaveRoomPresenterFake()
- val notificationSettingsService = FakeNotificationSettingsService(initialMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
+ val notificationSettingsService = FakeNotificationSettingsService(initialRoomMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
val room = aMatrixRoom(notificationSettingsService = notificationSettingsService)
val presenter = aRoomDetailsPresenter(room, leaveRoomPresenter, testCoroutineDispatchers(), notificationSettingsService)
moleculeFlow(RecompositionMode.Immediate) {
@@ -405,8 +403,8 @@ class RoomDetailsPresenterTests {
fun `present - unmute room notifications`() = runTest {
val leaveRoomPresenter = LeaveRoomPresenterFake()
val notificationSettingsService = FakeNotificationSettingsService(
- initialMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
- initialDefaultMode = RoomNotificationMode.ALL_MESSAGES
+ initialRoomMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
+ initialEncryptedGroupDefaultMode = RoomNotificationMode.ALL_MESSAGES
)
val room = aMatrixRoom(notificationSettingsService = notificationSettingsService)
val presenter = aRoomDetailsPresenter(room, leaveRoomPresenter, testCoroutineDispatchers(), notificationSettingsService)
diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt
index 587cc34429..aeaefeab6e 100644
--- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt
+++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt
@@ -48,8 +48,7 @@ import java.io.File
@ExperimentalCoroutinesApi
class RoomDetailsEditPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
private lateinit var fakePickerProvider: FakePickerProvider
diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenterTest.kt
index 175e4ec48c..4033038b69 100644
--- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenterTest.kt
+++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenterTest.kt
@@ -46,8 +46,7 @@ import org.junit.Test
internal class RoomInviteMembersPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt
index 6dd8e5bf56..c7e4c6d8b5 100644
--- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt
+++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt
@@ -43,8 +43,7 @@ import org.junit.Test
@ExperimentalCoroutinesApi
class RoomMemberListPresenterTests {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTests.kt
index 2fd240237f..a249e38823 100644
--- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTests.kt
+++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTests.kt
@@ -38,8 +38,7 @@ import org.junit.Test
@ExperimentalCoroutinesApi
class RoomMemberDetailsPresenterTests {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/notificationsettings/RoomNotificationSettingsPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/notificationsettings/RoomNotificationSettingsPresenterTests.kt
index 29bec7f622..a1b7831ce2 100644
--- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/notificationsettings/RoomNotificationSettingsPresenterTests.kt
+++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/notificationsettings/RoomNotificationSettingsPresenterTests.kt
@@ -24,7 +24,6 @@ import io.element.android.features.roomdetails.aMatrixRoom
import io.element.android.features.roomdetails.impl.notificationsettings.RoomNotificationSettingsEvents
import io.element.android.features.roomdetails.impl.notificationsettings.RoomNotificationSettingsPresenter
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
-import io.element.android.libraries.matrix.test.A_ROOM_NOTIFICATION_MODE
import io.element.android.tests.testutils.consumeItemsUntilPredicate
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -67,9 +66,9 @@ class RoomNotificationSettingsPresenterTests {
initialState.eventSink(RoomNotificationSettingsEvents.RoomNotificationModeChanged(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY))
initialState.eventSink(RoomNotificationSettingsEvents.SetNotificationMode(true))
val defaultState = consumeItemsUntilPredicate {
- it.roomNotificationSettings?.mode == A_ROOM_NOTIFICATION_MODE
+ it.roomNotificationSettings?.mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
}.last()
- Truth.assertThat(defaultState.roomNotificationSettings?.mode).isEqualTo(A_ROOM_NOTIFICATION_MODE)
+ Truth.assertThat(defaultState.roomNotificationSettings?.mode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
}
}
diff --git a/features/roomlist/impl/src/main/res/values-de/translations.xml b/features/roomlist/impl/src/main/res/values-de/translations.xml
deleted file mode 100644
index 49a400b138..0000000000
--- a/features/roomlist/impl/src/main/res/values-de/translations.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
- "Ein neues Gespräch oder einen neuen Raum erstellen"
- "Beginnen, indem du jemandem eine Nachricht sendest."
- "Noch keine Chats."
- "Alle Chats"
- "Es sieht so aus, als ob du ein neues Gerät verwendest. Verifiziere, dass du es bist, um auf deine verschlüsselten Nachrichten zuzugreifen."
- "Verifiziere, dass du es bist"
-
diff --git a/features/roomlist/impl/src/main/res/values-fr/translations.xml b/features/roomlist/impl/src/main/res/values-fr/translations.xml
index a5217e1ad5..0380971724 100644
--- a/features/roomlist/impl/src/main/res/values-fr/translations.xml
+++ b/features/roomlist/impl/src/main/res/values-fr/translations.xml
@@ -1,7 +1,9 @@
- "Créer une nouvelle conversation ou un nouveau salon"
+ "Créer une nouvelle conversation ou une nouvelle salle"
+ "Commencez par envoyer un message à quelqu\'un."
+ "Aucune discussion pour le moment."
"Tous les chats"
- "Il semblerait que vous utilisiez un nouvel appareil. Lancez la vérification avec un autre appareil pour accéder à vos messages chiffrés à l’avenir."
+ "Il semblerait que vous utilisiez un nouvel appareil. Vérifiez que vous êtes bien autorisé à accéder à vos messages cryptés."
"Vérifier que c’est bien vous"
diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt
index 1a2f32e54d..b7bfd3ecb4 100644
--- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt
+++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt
@@ -61,8 +61,7 @@ import org.junit.Test
class RoomListPresenterTests {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/features/verifysession/impl/src/main/res/values-de/translations.xml b/features/verifysession/impl/src/main/res/values-de/translations.xml
index f5f149cfd9..6699697e79 100644
--- a/features/verifysession/impl/src/main/res/values-de/translations.xml
+++ b/features/verifysession/impl/src/main/res/values-de/translations.xml
@@ -1,19 +1,4 @@
- "Etwas scheint nicht zu stimmen. Entweder ist die Antwortzeit für die Anfrage abgelaufen oder die Anfrage wurde abgelehnt."
- "Bestätige, dass die folgenden Emojis mit denen deiner anderen Sitzung übereinstimmen."
- "Emojis vergleichen"
- "Deine neue Sitzung ist jetzt verifiziert. Sie hat Zugriff auf deine verschlüsselten Nachrichten und andere Benutzer werden sie als vertrauenswürdig sehen."
- "Beweise, dass du es bist, um auf deinen verschlüsselten Nachrichtenverlauf zuzugreifen."
- "Eine bestehende Sitzung öffnen"
- "Verifizierung erneut versuchen"
- "Ich bin bereit"
- "Warten auf Übereinstimmung"
- "Vergleiche die einzigartigen Emojis und achte darauf, dass sie in derselben Reihenfolge erscheinen."
- "Sie stimmen nicht überein"
- "Sie stimmen überein"
- "Akzeptiere die Aufforderung zum Starten des Verifizierungsprozesses in deiner anderen Sitzung, um fortzufahren."
- "Warten auf die Annahme der Anfrage"
- "Verifizierung abgebrochen"
- "Starten"
+ "Start"
diff --git a/features/verifysession/impl/src/main/res/values-fr/translations.xml b/features/verifysession/impl/src/main/res/values-fr/translations.xml
index dd0bb56708..83de83146f 100644
--- a/features/verifysession/impl/src/main/res/values-fr/translations.xml
+++ b/features/verifysession/impl/src/main/res/values-fr/translations.xml
@@ -1,10 +1,10 @@
- "Quelque chose ne semble pas normal. Soit la demande a dépassé le temps imparti, soit elle a été refusée."
+ "Quelque chose ne va pas. Soit la demande a expiré, soit elle a été refusée."
"Confirmez que les emojis ci-dessous correspondent à ceux affichés sur votre autre session."
"Comparez les émojis"
- "Votre nouvelle session est désormais vérifiée. Elle a accès à vos messages chiffrés et les autres utilisateurs la verront identifiée comme fiable."
- "Prouvez qu\'il s\'agit bien de vous pour accéder à l\'historique de vos messages chiffrés."
+ "Votre nouvelle session est désormais vérifiée. Elle a accès à vos messages cryptés et les autres utilisateurs la verront identifiée comme fiable."
+ "Prouvez qu\'il s\'agit bien de vous pour accéder à l\'historique de vos messages cryptés."
"Ouvrir une session existante"
"Réessayer la vérification"
"Je suis prêt.e"
diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTests.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTests.kt
index ced2c48e9b..eee6c51a07 100644
--- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTests.kt
+++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTests.kt
@@ -35,8 +35,7 @@ import org.junit.Test
@ExperimentalCoroutinesApi
class VerifySelfSessionPresenterTests {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index cc0e9454ab..a48094ae9b 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -42,7 +42,7 @@ showkase = "1.0.0-beta18"
jsoup = "1.16.1"
appyx = "1.3.0"
dependencycheck = "8.4.0"
-dependencyanalysis = "1.21.0"
+dependencyanalysis = "1.22.0"
stem = "2.3.0"
sqldelight = "1.5.5"
telephoto = "0.6.0"
diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt
index a9a17dcceb..1aee986d32 100644
--- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt
+++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt
@@ -122,7 +122,7 @@ fun Context.copyToClipboard(
* Shows notification settings for the current app.
* In android O will directly opens the notification settings, in lower version it will show the App settings
*/
-fun Context.startNotificationSettingsIntent(activityResultLauncher: ActivityResultLauncher) {
+fun Context.startNotificationSettingsIntent(activityResultLauncher: ActivityResultLauncher? = null) {
val intent = Intent()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
@@ -132,7 +132,12 @@ fun Context.startNotificationSettingsIntent(activityResultLauncher: ActivityResu
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.data = Uri.fromParts("package", packageName, null)
}
- activityResultLauncher.launch(intent)
+
+ if (activityResultLauncher != null) {
+ activityResultLauncher.launch(intent)
+ } else {
+ startActivity(intent)
+ }
}
fun Context.openAppSettingsPage(
diff --git a/libraries/androidutils/src/main/res/values-de/translations.xml b/libraries/androidutils/src/main/res/values-de/translations.xml
deleted file mode 100644
index d30d83f831..0000000000
--- a/libraries/androidutils/src/main/res/values-de/translations.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
- "Keine kompatible App für diese Aktion gefunden."
-
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/Bloom.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/Bloom.kt
index 2a178af000..b23c0cc2bb 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/Bloom.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/Bloom.kt
@@ -363,6 +363,7 @@ fun Modifier.avatarBloom(
blurSize = blurSize,
offset = offset,
clipToSize = clipToSize,
+ bottomSoftEdgeColor = bottomSoftEdgeColor,
bottomSoftEdgeHeight = bottomSoftEdgeHeight,
bottomSoftEdgeAlpha = bottomSoftEdgeAlpha,
alpha = alpha,
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorAliases.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorAliases.kt
index b9e0893836..52e0a69430 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorAliases.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorAliases.kt
@@ -65,6 +65,16 @@ val SemanticColors.messageFromOtherBackground
Color(0xFF26282D)
}
+// This color is not present in Semantic color, so put hard-coded value for now
+val SemanticColors.progressIndicatorTrackColor
+ get() = if (isLight) {
+ // We want LightDesignTokens.colorAlphaGray500
+ Color(0x33052448)
+ } else {
+ // We want DarkDesignTokens.colorAlphaGray500
+ Color(0x25F4F7FA)
+ }
+
// Temporary color, which is not in the token right now
val SemanticColors.temporaryColorBgSpecial
get() = if (isLight) Color(0xFFE4E8F0) else Color(0xFF3A4048)
diff --git a/libraries/eventformatter/impl/src/main/res/values-de/translations.xml b/libraries/eventformatter/impl/src/main/res/values-de/translations.xml
index 0ca17bc4fe..48d41bed39 100644
--- a/libraries/eventformatter/impl/src/main/res/values-de/translations.xml
+++ b/libraries/eventformatter/impl/src/main/res/values-de/translations.xml
@@ -1,57 +1,5 @@
- "(Profilbild wurde auch geändert)"
- "%1$s hat sein Profilbild geändert"
- "Du hast deinen Avatar geändert"
- "%1$s hat seinen Anzeigenamen von %2$s in %3$s geändert"
- "Du hast deinen Anzeigenamen von %1$s in %2$s geändert"
- "%1$s hat seinen Anzeigenamen entfernt (es war %2$s)"
- "Du hast deinen Anzeigenamen entfernt (es war %1$s)"
- "%1$s hat seinen Anzeigenamen zu %2$s geändert"
- "Du hast deinen Anzeigenamen auf %1$s geändert"
- "%1$s hat den Raum-Avatar geändert"
- "Du hast den Raum-Avatar geändert"
- "%1$s hat das Raumbild entfernt"
- "Du hast das Raumbild entfernt"
- "%1$s hat %2$s gebannt"
- "Du hast %1$s gebannt"
"%1$s hat den Raum erstellt"
"Du hast den Raum erstellt"
- "%1$s hat %2$s eingeladen"
- "%1$s hat die Einladung angenommen"
- "Du hast die Einladung angenommen"
- "Du hast %1$s eingeladen"
- "%1$s hat dich eingeladen"
- "%1$s ist dem Raum beigetreten"
- "Du bist dem Raum beigetreten"
- "%1$s hat um Beitritt gebeten"
- "%1$s hat %2$s erlaubt, beizutreten"
- "%1$s hat dir erlaubt beizutreten"
- "Du hast um Beitritt gebeten"
- "%1$s hat die Beitrittsanfrage von %2$s abgelehnt"
- "Du hast die Beitrittsanfrage von %1$s abgelehnt"
- "%1$s hat deine Beitrittsanfrage abgelehnt"
- "%1$s ist nicht mehr daran interessiert, beizutreten"
- "Du hast deine Beitrittsanfrage zurückgezogen"
- "%1$s hat den Raum verlassen"
- "Du hast den Raum verlassen"
- "%1$s hat den Raumnamen geändert in: %2$s"
- "Du hast den Raumnamen geändert in: %1$s"
- "%1$s hat den Raumnamen entfernt"
- "Du hast den Raumnamen entfernt"
- "%1$s hat die Einladung abgelehnt"
- "Du hast die Einladung abgelehnt"
- "%1$s hat %2$s entfernt"
- "Du hast %1$s entfernt"
- "%1$s hat eine Einladung an %2$s gesendet, um dem Raum beizutreten"
- "Du hast eine Einladung an %1$s gesendet, um dem Raum beizutreten"
- "%1$s hat die Einladung für %2$s widerrufen, dem Raum beizutreten"
- "Du hast die Einladung für %1$s widerrufen, dem Raum beizutreten"
- "%1$s hat das Thema geändert zu: %2$s"
- "Du hast das Thema geändert zu: %1$s"
- "%1$s hat das Raumthema entfernt"
- "Du hast das Raumthema entfernt"
- "%1$s hat %2$s entbannt"
- "Du hast %1$s entbannt"
- "%1$s hat eine unbekannte Änderung an seiner Mitgliedschaft vorgenommen"
diff --git a/libraries/eventformatter/impl/src/main/res/values-fr/translations.xml b/libraries/eventformatter/impl/src/main/res/values-fr/translations.xml
index e7e07c0852..b81dc80c0d 100644
--- a/libraries/eventformatter/impl/src/main/res/values-fr/translations.xml
+++ b/libraries/eventformatter/impl/src/main/res/values-fr/translations.xml
@@ -5,53 +5,53 @@
"Vous avez changé d\'avatar"
"%1$s a changé son nom d\'affichage de %2$s à %3$s"
"Vous avez changé votre nom d\'affichage de %1$s à %2$s"
- "%1$s a supprimé son nom d\'affichage (il s\'agissait de %2$s)"
- "Vous avez supprimé votre nom d\'affichage (il s\'agissait de %1$s)"
+ "%1$s a supprimé leur nom d\'affichage (c\'était %2$s)"
+ "Vous avez supprimé votre nom d\'affichage (c\'était %1$s)"
"%1$s a défini son nom d\'affichage en tant que %2$s"
- "Vous avez défini votre nom d\'affichage en tant que %1$s"
- "%1$s a changé l\'avatar du salon"
- "Vous avez changé l\'avatar du salon"
- "%1$s a supprimé l\'avatar du salon"
- "Vous avez supprimé l\'avatar du salon"
+ "Vous avez défini votre nom d\'affichage comme %1$s"
+ "%1$s a changé l\'avatar de la salle"
+ "Vous avez changé l\'avatar de la salle"
+ "%1$s a supprimé l\'avatar de la salle"
+ "Vous avez supprimé l\'avatar de la salle"
"%1$s a banni %2$s"
"Vous avez banni %1$s"
- "%1$s a créé le salon"
- "Vous avez créé le salon"
+ "%1$s a créé la salle"
+ "Vous avez créé la salle"
"%1$s a invité %2$s"
"%1$s a accepté l\'invitation"
"Vous avez accepté l\'invitation"
"Vous avez invité %1$s"
- "%1$s vous a invité."
- "%1$s a rejoint le salon"
- "Vous avez rejoint le salon"
+ "%1$s vous a invité"
+ "%1$s a rejoint la salle"
+ "Vous avez rejoint la salle"
"%1$s a demandé à rejoindre"
"%1$s a autorisé %2$s à rejoindre"
"%1$s vous a autorisé à rejoindre"
"Vous avez demandé à rejoindre"
- "%1$s a rejeté la demande d\'adhésion de %2$s"
- "Vous avez rejeté la demande d\'adhésion de %1$s"
- "%1$s a rejeté votre demande d\'adhésion"
+ "%1$s a rejeté la demande de %2$s pour rejoindre"
+ "Vous avez rejeté la demande de %1$s pour rejoindre"
+ "%1$s a rejeté votre demande pour rejoindre"
"%1$s n’est plus intéressé à rejoindre"
"Vous avez annulé votre demande d\'adhésion"
- "%1$s a quitté le salon"
- "Vous avez quitté le salon"
- "%1$s a changé le nom du salon en : %2$s"
- "Vous avez changé le nom du salon en : %1$s"
- "%1$s a supprimé le nom du salon"
- "Vous avez supprimé le nom du salon"
+ "%1$s a quitté la salle"
+ "Vous avez quitté la salle"
+ "%1$s a changé le nom de la salle en : %2$s"
+ "Vous avez changé le nom de la salle en : %1$s"
+ "%1$s a supprimé le nom de la salle"
+ "Vous avez supprimé le nom de la salle"
"%1$s a rejeté l\'invitation"
"Vous avez refusé l\'invitation"
"%1$s a supprimé %2$s"
"Vous avez supprimé %1$s"
- "%1$s a envoyé une invitation à %2$s à rejoindre le salon"
- "Vous avez envoyé une invitation à %1$s pour rejoindre le salon"
- "%1$s a révoqué l\'invitation de %2$s à rejoindre le salon"
- "Vous avez révoqué l\'invitation de %1$s à rejoindre le salon"
- "%1$s a changé le sujet en : %2$s"
- "Vous avez changé le sujet en : %1$s"
- "%1$s a supprimé le sujet du salon"
- "Vous avez supprimé le sujet du salon"
+ "%1$s a envoyé une invitation à %2$s à rejoindre le salle"
+ "Vous avez envoyé une invitation à %1$s pour rejoindre la salle"
+ "%1$s a révoqué l\'invitation de %2$s à rejoindre la salle"
+ "Vous avez révoqué l\'invitation de %1$s à rejoindre la salle"
+ "%1$s a changé le sujet pour : %2$s"
+ "Vous avez changé le sujet pour : %1$s"
+ "%1$s a supprimé le sujet de la salle"
+ "Vous avez supprimé le sujet de la salle"
"%1$s a débanni %2$s"
"Vous avez débanni %1$s"
- "%1$s a apporté une modification inconnue à son adhésion"
+ "%1$s a effectué un changement inconnu à son adhésion"
diff --git a/libraries/eventformatter/impl/src/main/res/values-zh-rTW/translations.xml b/libraries/eventformatter/impl/src/main/res/values-zh-rTW/translations.xml
index 45ab0acee1..2453e2d825 100644
--- a/libraries/eventformatter/impl/src/main/res/values-zh-rTW/translations.xml
+++ b/libraries/eventformatter/impl/src/main/res/values-zh-rTW/translations.xml
@@ -1,37 +1,37 @@
- "%1$s將他的顯示名稱從%2$s變更為%3$s"
- "您將您的顯示名稱從%1$s1變更為%2$s"
- "%1$s的顯示名稱已被本人移除(原為%2$s)"
- "您的顯示名稱已被您移除(原為%1$s)"
- "%1$s將他的顯示名稱設為%2$s"
- "您將您的顯示名稱設為%1$s"
- "%1$s建立此聊天室"
+ "%1$s 將他的顯示名稱從 %2$s 變更為 %3$s"
+ "您將您的顯示名稱從 %1$s1 變更為 %2$s"
+ "%1$s 的顯示名稱已被本人移除(原為 %2$s)"
+ "您的顯示名稱已被您移除(原為 %1$s)"
+ "%1$s 將他的顯示名稱設為 %2$s"
+ "您將您的顯示名稱設為 %1$s"
+ "%1$s 建立此聊天室"
"您建立此聊天室"
- "%1$s邀請%2$s"
- "%1$s接受邀請"
+ "%1$s 邀請 %2$s"
+ "%1$s 接受邀請"
"您接受邀請"
- "您邀請%1$s"
- "%1$s邀請您"
- "%1$s加入聊天室"
+ "您邀請 %1$s"
+ "%1$s 邀請您"
+ "%1$s 加入聊天室"
"您加入聊天室"
- "%1$s請求加入"
+ "%1$s 請求加入"
"您請求加入"
- "%1$s拒絕%2$s的加入請求"
- "您拒絕%1$s的加入請求"
- "%1$s拒絕您的加入請求"
- "%1$s離開聊天室"
+ "%1$s 拒絕 %2$s 的加入請求"
+ "您拒絕 %1$s 的加入請求"
+ "%1$s 拒絕您的加入請求"
+ "%1$s 離開聊天室"
"您離開聊天室"
- "%1$s將聊天室名稱變更為%2$s"
- "您將聊天室名稱變更為%1$s"
- "聊天室名稱已被%1$s移除"
+ "%1$s 將聊天室名稱變更為 %2$s"
+ "您將聊天室名稱變更為 %1$s"
+ "聊天室名稱已被 %1$s 移除"
"聊天室名稱已被您移除"
- "%2$s已被%1$s移除"
- "%1$s已被您移除"
- "%1$s邀請%2$s加入聊天室"
- "您邀請%1$s加入聊天室"
- "%1$s將主題變更為%2$s"
- "您將主題變更為%1$s"
- "聊天室主題已被%1$s移除"
+ "%2$s 已被 %1$s 移除"
+ "%1$s 已被您移除"
+ "%1$s 邀請 %2$s 加入聊天室"
+ "您邀請 %1$s 加入聊天室"
+ "%1$s 將主題變更為 %2$s"
+ "您將主題變更為 %1$s"
+ "聊天室主題已被 %1$s 移除"
"聊天室主題已被您移除"
diff --git a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt
index 0f27a94a2d..ca68b0cbd1 100644
--- a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt
+++ b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt
@@ -41,8 +41,7 @@ enum class FeatureFlags(
NotificationSettings(
key = "feature.notificationsettings",
title = "Show notification settings",
- // Do not forget to edit StaticFeatureFlagProvider when enabling the feature.
- defaultValue = false,
+ defaultValue = true,
),
RichTextEditor(
key = "feature.richtexteditor",
diff --git a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt
index c9b24f08e6..d7759ac474 100644
--- a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt
+++ b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt
@@ -34,7 +34,7 @@ class StaticFeatureFlagProvider @Inject constructor() :
when(feature) {
FeatureFlags.LocationSharing -> true
FeatureFlags.Polls -> true
- FeatureFlags.NotificationSettings -> false
+ FeatureFlags.NotificationSettings -> true
FeatureFlags.RichTextEditor -> true
}
} else {
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notificationsettings/NotificationSettingsService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notificationsettings/NotificationSettingsService.kt
index 0f82604aba..5a81edb052 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notificationsettings/NotificationSettingsService.kt
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notificationsettings/NotificationSettingsService.kt
@@ -27,10 +27,15 @@ interface NotificationSettingsService {
* State of the current room notification settings flow ([MatrixRoomNotificationSettingsState.Unknown] if not started).
*/
val notificationSettingsChangeFlow : SharedFlow
- suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, membersCount: Long): Result
- suspend fun getDefaultRoomNotificationMode(isEncrypted: Boolean, membersCount: Long): Result
+ suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result
+ suspend fun getDefaultRoomNotificationMode(isEncrypted: Boolean, isOneToOne: Boolean): Result
+ suspend fun setDefaultRoomNotificationMode(isEncrypted: Boolean, mode: RoomNotificationMode, isOneToOne: Boolean): Result
suspend fun setRoomNotificationMode(roomId: RoomId, mode: RoomNotificationMode): Result
suspend fun restoreDefaultRoomNotificationMode(roomId: RoomId): Result
suspend fun muteRoom(roomId: RoomId): Result
- suspend fun unmuteRoom(roomId: RoomId, isEncrypted: Boolean, membersCount: Long): Result
+ suspend fun unmuteRoom(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result
+ suspend fun isRoomMentionEnabled(): Result
+ suspend fun setRoomMentionEnabled(enabled: Boolean): Result
+ suspend fun isCallEnabled(): Result
+ suspend fun setCallEnabled(enabled: Boolean): Result
}
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt
index 142e86dcd9..1dd6101354 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt
@@ -49,6 +49,12 @@ interface MatrixRoom : Closeable {
val activeMemberCount: Long
val joinedMemberCount: Long
+ /**
+ * A one-to-one is a room with exactly 2 members.
+ * See [the Matrix spec](https://spec.matrix.org/latest/client-server-api/#default-underride-rules).
+ */
+ val isOneToOne: Boolean get() = activeMemberCount == 2L
+
/**
* The current loaded members as a StateFlow.
* Initial value is [MatrixRoomMembersState.Unknown].
@@ -178,6 +184,7 @@ interface MatrixRoom : Closeable {
suspend fun endPoll(pollStartId: EventId, text: String): Result
override fun close() = destroy()
+
}
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt
index 22f1e9af3f..090a0cf613 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt
@@ -101,7 +101,6 @@ class RustMatrixClient constructor(
client = client,
dispatchers = dispatchers,
)
-
private val notificationProcessSetup = NotificationProcessSetup.SingleProcess(syncService)
private val notificationClient = client.notificationClient(notificationProcessSetup)
.use { builder ->
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt
index 1b1d51214f..a2fffdbdfb 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt
@@ -47,16 +47,26 @@ class RustNotificationSettingsService(
notificationSettings.setDelegate(notificationSettingsDelegate)
}
- override suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, membersCount: Long): Result =
+ override suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result =
runCatching {
- notificationSettings.getRoomNotificationSettings(roomId.value, isEncrypted, isOneToOne(membersCount)).let(RoomNotificationSettingsMapper::map)
+ notificationSettings.getRoomNotificationSettings(roomId.value, isEncrypted, isOneToOne).let(RoomNotificationSettingsMapper::map)
}
- override suspend fun getDefaultRoomNotificationMode(isEncrypted: Boolean, membersCount: Long): Result =
+ override suspend fun getDefaultRoomNotificationMode(isEncrypted: Boolean, isOneToOne: Boolean): Result =
runCatching {
- notificationSettings.getDefaultRoomNotificationMode(isEncrypted, isOneToOne(membersCount)).let(RoomNotificationSettingsMapper::mapMode)
+ notificationSettings.getDefaultRoomNotificationMode(isEncrypted, isOneToOne).let(RoomNotificationSettingsMapper::mapMode)
}
+ override suspend fun setDefaultRoomNotificationMode(
+ isEncrypted: Boolean,
+ mode: RoomNotificationMode,
+ isOneToOne: Boolean
+ ): Result = withContext(dispatchers.io) {
+ runCatching {
+ notificationSettings.setDefaultRoomNotificationMode(isEncrypted, isOneToOne, mode.let(RoomNotificationSettingsMapper::mapMode))
+ }
+ }
+
override suspend fun setRoomNotificationMode(roomId: RoomId, mode: RoomNotificationMode): Result = withContext(dispatchers.io) {
runCatching {
notificationSettings.setRoomNotificationMode(roomId.value, mode.let(RoomNotificationSettingsMapper::mapMode))
@@ -71,16 +81,33 @@ class RustNotificationSettingsService(
override suspend fun muteRoom(roomId: RoomId): Result = setRoomNotificationMode(roomId, RoomNotificationMode.MUTE)
- override suspend fun unmuteRoom(roomId: RoomId, isEncrypted: Boolean, membersCount: Long) = withContext(dispatchers.io) {
+ override suspend fun unmuteRoom(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean) = withContext(dispatchers.io) {
runCatching {
- notificationSettings.unmuteRoom(roomId.value, isEncrypted, isOneToOne(membersCount))
+ notificationSettings.unmuteRoom(roomId.value, isEncrypted, isOneToOne)
}
}
- /**
- * A one-to-one is a room with exactly 2 members.
- * See [the Matrix spec](https://spec.matrix.org/latest/client-server-api/#default-underride-rules).
- * @param membersCount The active members count in a room
- */
- private fun isOneToOne(membersCount: Long) = membersCount == 2L
+ override suspend fun isRoomMentionEnabled(): Result = withContext(dispatchers.io) {
+ runCatching {
+ notificationSettings.isRoomMentionEnabled()
+ }
+ }
+
+ override suspend fun setRoomMentionEnabled(enabled: Boolean): Result = withContext(dispatchers.io) {
+ runCatching {
+ notificationSettings.setRoomMentionEnabled(enabled)
+ }
+ }
+
+ override suspend fun isCallEnabled(): Result = withContext(dispatchers.io) {
+ runCatching {
+ notificationSettings.isCallEnabled()
+ }
+ }
+
+ override suspend fun setCallEnabled(enabled: Boolean): Result = withContext(dispatchers.io) {
+ runCatching {
+ notificationSettings.setCallEnabled(enabled)
+ }
+ }
}
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt
index eb456588ee..766f27a473 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt
@@ -212,7 +212,7 @@ class RustMatrixRoom(
val currentRoomNotificationSettings = currentState.roomNotificationSettings()
_roomNotificationSettingsStateFlow.value = MatrixRoomNotificationSettingsState.Pending(prevRoomNotificationSettings = currentRoomNotificationSettings)
runCatching {
- roomNotificationSettingsService.getRoomNotificationSettings(roomId, isEncrypted, activeMemberCount).getOrThrow()
+ roomNotificationSettingsService.getRoomNotificationSettings(roomId, isEncrypted, isOneToOne).getOrThrow()
}.map {
_roomNotificationSettingsStateFlow.value = MatrixRoomNotificationSettingsState.Ready(it)
}.onFailure {
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt
index d7df699a57..0a59cfddab 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt
@@ -46,22 +46,22 @@ class EventMessageMapper {
fun map(message: Message): MessageContent = message.use {
val type = it.msgtype().use(this::mapMessageType)
- val inReplyToId = it.inReplyTo()?.eventId?.let(::EventId)
- val inReplyToEvent: InReplyTo? = it.inReplyTo()?.event?.use { details ->
- when (details) {
+ val inReplyToEvent: InReplyTo? = it.inReplyTo()?.use { details ->
+ val inReplyToId = EventId(details.eventId)
+ when (val event = details.event) {
is RepliedToEventDetails.Ready -> {
- val senderProfile = details.senderProfile as? ProfileDetails.Ready
+ val senderProfile = event.senderProfile as? ProfileDetails.Ready
InReplyTo.Ready(
- eventId = inReplyToId!!,
- content = timelineEventContentMapper.map(details.content),
- senderId = UserId(details.sender),
+ eventId = inReplyToId,
+ content = timelineEventContentMapper.map(event.content),
+ senderId = UserId(event.sender),
senderDisplayName = senderProfile?.displayName,
senderAvatarUrl = senderProfile?.avatarUrl,
)
}
is RepliedToEventDetails.Error -> InReplyTo.Error
is RepliedToEventDetails.Pending -> InReplyTo.Pending
- is RepliedToEventDetails.Unavailable -> InReplyTo.NotLoaded(inReplyToId!!)
+ is RepliedToEventDetails.Unavailable -> InReplyTo.NotLoaded(inReplyToId)
}
}
MessageContent(
diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notificationsettings/FakeNotificationSettingsService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notificationsettings/FakeNotificationSettingsService.kt
index 852496e0b3..77592d6d1f 100644
--- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notificationsettings/FakeNotificationSettingsService.kt
+++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notificationsettings/FakeNotificationSettingsService.kt
@@ -25,32 +25,75 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
class FakeNotificationSettingsService(
- initialMode: RoomNotificationMode = A_ROOM_NOTIFICATION_MODE,
- initialDefaultMode: RoomNotificationMode = A_ROOM_NOTIFICATION_MODE
+ initialRoomMode: RoomNotificationMode = A_ROOM_NOTIFICATION_MODE,
+ initialGroupDefaultMode: RoomNotificationMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
+ initialEncryptedGroupDefaultMode: RoomNotificationMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
+ initialOneToOneDefaultMode: RoomNotificationMode = RoomNotificationMode.ALL_MESSAGES,
+ initialEncryptedOneToOneDefaultMode: RoomNotificationMode = RoomNotificationMode.ALL_MESSAGES,
) : NotificationSettingsService {
- private var _roomNotificationSettingsStateFlow = MutableStateFlow(Unit)
- private var defaultRoomNotificationMode: RoomNotificationMode = initialDefaultMode
- private var roomNotificationMode: RoomNotificationMode = initialMode
+ private var _notificationSettingsStateFlow = MutableStateFlow(Unit)
+ private var defaultGroupRoomNotificationMode: RoomNotificationMode = initialGroupDefaultMode
+ private var defaultEncryptedGroupRoomNotificationMode: RoomNotificationMode = initialEncryptedGroupDefaultMode
+ private var defaultOneToOneRoomNotificationMode: RoomNotificationMode = initialOneToOneDefaultMode
+ private var defaultEncryptedOneToOneRoomNotificationMode: RoomNotificationMode = initialEncryptedOneToOneDefaultMode
+ private var roomNotificationMode: RoomNotificationMode = initialRoomMode
+ private var callNotificationsEnabled = false
+ private var atRoomNotificationsEnabled = false
override val notificationSettingsChangeFlow: SharedFlow
- get() = _roomNotificationSettingsStateFlow
+ get() = _notificationSettingsStateFlow
- override suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, membersCount: Long): Result {
- return Result.success(RoomNotificationSettings(mode = roomNotificationMode, isDefault = roomNotificationMode == defaultRoomNotificationMode))
+ override suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result {
+ return Result.success(
+ RoomNotificationSettings(
+ mode = roomNotificationMode,
+ isDefault = roomNotificationMode == defaultEncryptedGroupRoomNotificationMode
+ )
+ )
}
- override suspend fun getDefaultRoomNotificationMode(isEncrypted: Boolean, membersCount: Long): Result {
- return Result.success(defaultRoomNotificationMode)
+ override suspend fun getDefaultRoomNotificationMode(isEncrypted: Boolean, isOneToOne: Boolean): Result {
+ return if (isOneToOne) {
+ if (isEncrypted) {
+ Result.success(defaultEncryptedOneToOneRoomNotificationMode)
+ } else {
+ Result.success(defaultOneToOneRoomNotificationMode)
+ }
+ } else {
+ if (isEncrypted) {
+ Result.success(defaultEncryptedGroupRoomNotificationMode)
+ } else {
+ Result.success(defaultGroupRoomNotificationMode)
+ }
+ }
+ }
+
+ override suspend fun setDefaultRoomNotificationMode(isEncrypted: Boolean, mode: RoomNotificationMode, isOneToOne: Boolean): Result {
+ if (isOneToOne) {
+ if (isEncrypted) {
+ defaultEncryptedOneToOneRoomNotificationMode = mode
+ } else {
+ defaultOneToOneRoomNotificationMode = mode
+ }
+ } else {
+ if (isEncrypted) {
+ defaultEncryptedGroupRoomNotificationMode = mode
+ } else {
+ defaultGroupRoomNotificationMode = mode
+ }
+ }
+ _notificationSettingsStateFlow.emit(Unit)
+ return Result.success(Unit)
}
override suspend fun setRoomNotificationMode(roomId: RoomId, mode: RoomNotificationMode): Result {
roomNotificationMode = mode
- _roomNotificationSettingsStateFlow.emit(Unit)
+ _notificationSettingsStateFlow.emit(Unit)
return Result.success(Unit)
}
override suspend fun restoreDefaultRoomNotificationMode(roomId: RoomId): Result {
- roomNotificationMode = defaultRoomNotificationMode
- _roomNotificationSettingsStateFlow.emit(Unit)
+ roomNotificationMode = defaultEncryptedGroupRoomNotificationMode
+ _notificationSettingsStateFlow.emit(Unit)
return Result.success(Unit)
}
@@ -58,7 +101,25 @@ class FakeNotificationSettingsService(
return setRoomNotificationMode(roomId, RoomNotificationMode.MUTE)
}
- override suspend fun unmuteRoom(roomId: RoomId, isEncrypted: Boolean, membersCount: Long): Result {
+ override suspend fun unmuteRoom(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result {
return restoreDefaultRoomNotificationMode(roomId)
}
+
+ override suspend fun isRoomMentionEnabled(): Result {
+ return Result.success(atRoomNotificationsEnabled)
+ }
+
+ override suspend fun setRoomMentionEnabled(enabled: Boolean): Result {
+ atRoomNotificationsEnabled = enabled
+ return Result.success(Unit)
+ }
+
+ override suspend fun isCallEnabled(): Result {
+ return Result.success(callNotificationsEnabled)
+ }
+
+ override suspend fun setCallEnabled(enabled: Boolean): Result {
+ callNotificationsEnabled = enabled
+ return Result.success(Unit)
+ }
}
diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt
index 638b935000..0e8916e87e 100644
--- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt
+++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt
@@ -148,7 +148,7 @@ class FakeMatrixRoom(
}
override suspend fun updateRoomNotificationSettings(): Result = simulateLongTask {
- val notificationSettings = notificationSettingsService.getRoomNotificationSettings(roomId, isEncrypted, activeMemberCount).getOrThrow()
+ val notificationSettings = notificationSettingsService.getRoomNotificationSettings(roomId, isEncrypted, isOneToOne).getOrThrow()
roomNotificationSettingsStateFlow.value = MatrixRoomNotificationSettingsState.Ready(notificationSettings)
return Result.success(Unit)
}
diff --git a/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenterTest.kt b/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenterTest.kt
index c25e8072e7..316ce7bc67 100644
--- a/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenterTest.kt
+++ b/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenterTest.kt
@@ -35,8 +35,7 @@ const val A_PERMISSION = "A_PERMISSION"
class DefaultPermissionsPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/libraries/permissions/noop/src/test/kotlin/io/element/android/libraries/permissions/noop/NoopPermissionsPresenterTest.kt b/libraries/permissions/noop/src/test/kotlin/io/element/android/libraries/permissions/noop/NoopPermissionsPresenterTest.kt
index f837529515..828610a6fb 100644
--- a/libraries/permissions/noop/src/test/kotlin/io/element/android/libraries/permissions/noop/NoopPermissionsPresenterTest.kt
+++ b/libraries/permissions/noop/src/test/kotlin/io/element/android/libraries/permissions/noop/NoopPermissionsPresenterTest.kt
@@ -27,8 +27,7 @@ import org.junit.Test
class NoopPermissionsPresenterTest {
- @Rule
- @JvmField
+ @get:Rule
val warmUpRule = WarmUpRule()
@Test
diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt
index c3d68e52ac..7c5d24c31a 100644
--- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt
+++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt
@@ -35,6 +35,7 @@ import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@@ -123,7 +124,7 @@ class DefaultPushHandler @Inject constructor(
}
val userPushStore = userPushStoreFactory.create(userId)
- if (!userPushStore.areNotificationEnabledForDevice()) {
+ if (!userPushStore.getNotificationEnabledForDevice().first()) {
// TODO We need to check if this is an incoming call
Timber.tag(loggerTag.value).i("Notification are disabled for this device, ignore push.")
return
diff --git a/libraries/push/impl/src/main/res/values-de/translations.xml b/libraries/push/impl/src/main/res/values-de/translations.xml
index 04c7521d9e..496071416d 100644
--- a/libraries/push/impl/src/main/res/values-de/translations.xml
+++ b/libraries/push/impl/src/main/res/values-de/translations.xml
@@ -1,52 +1,7 @@
- "Anruf"
- "Warte auf Ereignisse"
- "Laute Benachrichtigungen"
- "Stumme Benachrichtigungen"
- "** Senden fehlgeschlagen - bitte Raum öffnen"
- "Beitreten"
- "Ablehnen"
- "Hat dich zum Chatten eingeladen"
- "Neue Nachrichten"
- "Reagierte mit %1$s"
- "Als gelesen markieren"
- "Hat dich eingeladen, dem Raum beizutreten"
- "Ich"
- "Du siehst die Benachrichtigung an! Klick mich an!"
"%1$s: %2$s"
"%1$s: %2$s %3$s"
"%1$s und %2$s"
- "%1$s in %2$s"
- "%1$s in %2$s und %3$s"
-
- - "%1$s: %2$d Nachricht"
- - "%1$s: %2$d Nachrichten"
-
-
- - "%d Mitteilung"
- - "%d Mitteilungen"
-
-
- - "%d Einladung"
- - "%d Einladungen"
-
-
- - "%d neue Nachricht"
- - "%d neue Nachrichten"
-
-
- - "%d ungelesene benachrichtigte Nachricht"
- - "%d ungelesene benachrichtigte Nachrichten"
-
-
- - "%d Raum"
- - "%d Räume"
-
- "Auswählen, wie Benachrichtigungen empfangen werden sollen"
- "Hintergrundsynchronisation"
- "Google-Dienste"
- "Keine gültigen Google Play-Dienste gefunden. Benachrichtigungen funktionieren möglicherweise nicht richtig."
- "Mitteilung"
- "Schnellantwort"
+ "Schnelle Antwort"
diff --git a/libraries/push/impl/src/main/res/values-fr/translations.xml b/libraries/push/impl/src/main/res/values-fr/translations.xml
index 6e6374e8f2..67309cfc27 100644
--- a/libraries/push/impl/src/main/res/values-fr/translations.xml
+++ b/libraries/push/impl/src/main/res/values-fr/translations.xml
@@ -1,27 +1,27 @@
"Appel"
- "À l\'écoute d\'événements"
+ "À l\'écoute des événements"
"Notifications bruyantes"
"Notifications silencieuses"
- "** Échec d\'envoi - veuillez ouvrir le salon"
+ "** Échec de l\'envoi - veuillez ouvrir la salle"
"Rejoindre"
- "Refuser"
+ "Rejeter"
"Vous a invité à discuter"
"Nouveaux messages"
"A réagi avec %1$s"
"Marquer comme lu"
- "Vous a invité à rejoindre le salon"
+ "Vous a invité à rejoindre la salle"
"Moi"
- "Vous êtes en train de consulter la notification ! Cliquez-moi !"
- "%1$s: %2$s"
- "%1$s: %2$s %3$s"
+ "Vous êtes en train de consulter la notification ! Cliquez sur moi !"
+ "%1$s : %2$s"
+ "%1$s : %2$s %3$s"
"%1$s et %2$s"
"%1$s dans %2$s"
"%1$s dans %2$s et %3$s"
- - "%1$s: %2$d message"
- - "%1$s: %2$d messages"
+ - "%1$s : %2$d message"
+ - "%1$s : %2$d messages"
- "%d notification"
@@ -40,10 +40,10 @@
- "%d messages notifiés non lus"
- - "%d conversation"
- - "%d conversations"
+ - "%d salle"
+ - "%d salles"
- "Choisissez comment recevoir les notifications"
+ "Choisissez le mode de réception des notifications"
"Synchronisation en arrière-plan"
"Services Google"
"Aucun service Google Play valide n\'a été trouvé. Les notifications peuvent ne pas fonctionner correctement."
diff --git a/libraries/pushstore/api/build.gradle.kts b/libraries/pushstore/api/build.gradle.kts
index fdfd794c2e..00d7776770 100644
--- a/libraries/pushstore/api/build.gradle.kts
+++ b/libraries/pushstore/api/build.gradle.kts
@@ -22,5 +22,6 @@ android {
}
dependencies {
+ implementation(libs.coroutines.core)
implementation(projects.libraries.matrix.api)
}
diff --git a/libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/UserPushStore.kt b/libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/UserPushStore.kt
index 28577ba3f8..a10413fdf5 100644
--- a/libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/UserPushStore.kt
+++ b/libraries/pushstore/api/src/main/kotlin/io/element/android/libraries/pushstore/api/UserPushStore.kt
@@ -15,6 +15,8 @@
*/
package io.element.android.libraries.pushstore.api
+import kotlinx.coroutines.flow.Flow
+
/**
* Store data related to push about a user.
@@ -25,7 +27,7 @@ interface UserPushStore {
suspend fun getCurrentRegisteredPushKey(): String?
suspend fun setCurrentRegisteredPushKey(value: String)
- suspend fun areNotificationEnabledForDevice(): Boolean
+ fun getNotificationEnabledForDevice(): Flow
suspend fun setNotificationEnabledForDevice(enabled: Boolean)
/**
diff --git a/libraries/pushstore/impl/src/androidTest/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactoryTest.kt b/libraries/pushstore/impl/src/androidTest/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactoryTest.kt
index c87c772ddf..67b07bfd1d 100644
--- a/libraries/pushstore/impl/src/androidTest/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactoryTest.kt
+++ b/libraries/pushstore/impl/src/androidTest/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactoryTest.kt
@@ -20,6 +20,7 @@ import androidx.test.platform.app.InstrumentationRegistry
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.pushstore.api.UserPushStore
import io.element.android.libraries.sessionstorage.test.observer.NoOpSessionObserver
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.Test
import kotlin.concurrent.thread
@@ -49,8 +50,8 @@ class DefaultUserPushStoreFactoryTest {
thread1.join()
thread2.join()
runBlocking {
- userPushStore1!!.areNotificationEnabledForDevice()
- userPushStore2!!.areNotificationEnabledForDevice()
+ userPushStore1!!.getNotificationEnabledForDevice().first()
+ userPushStore2!!.getNotificationEnabledForDevice().first()
}
}
}
diff --git a/libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/UserPushStoreDataStore.kt b/libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/UserPushStoreDataStore.kt
index 56867a6584..718ddb51fa 100644
--- a/libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/UserPushStoreDataStore.kt
+++ b/libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/UserPushStoreDataStore.kt
@@ -26,7 +26,9 @@ import androidx.datastore.preferences.preferencesDataStore
import io.element.android.libraries.core.bool.orTrue
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.pushstore.api.UserPushStore
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.map
/**
* Store data related to push about a user.
@@ -60,8 +62,8 @@ class UserPushStoreDataStore(
}
}
- override suspend fun areNotificationEnabledForDevice(): Boolean {
- return context.dataStore.data.first()[notificationEnabled].orTrue()
+ override fun getNotificationEnabledForDevice(): Flow {
+ return context.dataStore.data.map{ it[notificationEnabled].orTrue() }
}
override suspend fun setNotificationEnabledForDevice(enabled: Boolean) {
diff --git a/libraries/pushstore/test/build.gradle.kts b/libraries/pushstore/test/build.gradle.kts
new file mode 100644
index 0000000000..a100c40f7d
--- /dev/null
+++ b/libraries/pushstore/test/build.gradle.kts
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+plugins {
+ id("io.element.android-library")
+}
+
+android {
+ namespace = "io.element.android.libraries.pushstore.test"
+}
+
+dependencies {
+ api(projects.libraries.matrix.api)
+ api(libs.coroutines.core)
+ implementation(libs.coroutines.test)
+ implementation(projects.tests.testutils)
+ implementation(projects.libraries.pushstore.api)
+}
diff --git a/libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStore.kt b/libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStore.kt
new file mode 100644
index 0000000000..c697e0d3c9
--- /dev/null
+++ b/libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStore.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2023 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 com.element.android.libraries.pushstore.test.userpushstore
+
+import io.element.android.libraries.pushstore.api.UserPushStore
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class FakeUserPushStore: UserPushStore {
+
+ private var pushProviderName: String? = null
+ private var currentRegisteredPushKey: String? = null
+ private val notificationEnabledForDevice = MutableStateFlow(true)
+ override suspend fun getPushProviderName(): String? {
+ return pushProviderName
+ }
+
+ override suspend fun setPushProviderName(value: String) {
+ pushProviderName = value
+ }
+
+ override suspend fun getCurrentRegisteredPushKey(): String? {
+ return currentRegisteredPushKey
+ }
+
+ override suspend fun setCurrentRegisteredPushKey(value: String) {
+ currentRegisteredPushKey = value
+ }
+
+ override fun getNotificationEnabledForDevice(): Flow {
+ return notificationEnabledForDevice
+ }
+
+ override suspend fun setNotificationEnabledForDevice(enabled: Boolean) {
+ notificationEnabledForDevice.value = enabled
+ }
+
+ override fun useCompleteNotificationFormat(): Boolean {
+ return true
+ }
+
+ override suspend fun reset() {
+
+ }
+}
diff --git a/libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStoreFactory.kt b/libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStoreFactory.kt
new file mode 100644
index 0000000000..f51893b00a
--- /dev/null
+++ b/libraries/pushstore/test/src/main/kotlin/com/element/android/libraries/pushstore/test/userpushstore/FakeUserPushStoreFactory.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2023 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 com.element.android.libraries.pushstore.test.userpushstore
+
+import io.element.android.libraries.matrix.api.core.SessionId
+import io.element.android.libraries.pushstore.api.UserPushStore
+import io.element.android.libraries.pushstore.api.UserPushStoreFactory
+
+class FakeUserPushStoreFactory: UserPushStoreFactory {
+ override fun create(userId: SessionId): UserPushStore {
+ return FakeUserPushStore()
+ }
+}
+
diff --git a/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/Compose.kt b/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/Compose.kt
index 966c88fa06..7d467dc5a4 100644
--- a/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/Compose.kt
+++ b/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/Compose.kt
@@ -26,9 +26,7 @@ import androidx.compose.ui.semantics.testTagsAsResourceId
* Add a testTag to a Modifier, to be used by external tool, like TrafficLight for instance.
*/
@OptIn(ExperimentalComposeUiApi::class)
-fun Modifier.testTag(id: TestTag) = this.then(
- semantics {
- testTag = id.value
- testTagsAsResourceId = true
- }
-)
+fun Modifier.testTag(id: TestTag) = semantics {
+ testTag = id.value
+ testTagsAsResourceId = true
+}
diff --git a/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt b/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt
index d832a6168d..d90be0c25c 100644
--- a/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt
+++ b/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt
@@ -47,6 +47,11 @@ object TestTags {
* Welcome screen.
*/
val welcomeScreenTitle = TestTag("welcome_screen-title")
+
+ /**
+ * RichTextEditor.
+ */
+ val richTextEditor = TestTag("rich_text_editor")
}
diff --git a/libraries/textcomposer/impl/build.gradle.kts b/libraries/textcomposer/impl/build.gradle.kts
index 633491d3b3..86e911ca3e 100644
--- a/libraries/textcomposer/impl/build.gradle.kts
+++ b/libraries/textcomposer/impl/build.gradle.kts
@@ -31,6 +31,7 @@ dependencies {
implementation(projects.libraries.matrix.api)
implementation(projects.libraries.matrixui)
implementation(projects.libraries.designsystem)
+ implementation(projects.libraries.testtags)
implementation(libs.androidx.constraintlayout)
implementation(libs.androidx.constraintlayout.compose)
diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt
index de43424aae..b7c2b8ea40 100644
--- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt
+++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt
@@ -74,6 +74,8 @@ import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnail
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType
+import io.element.android.libraries.testtags.TestTags
+import io.element.android.libraries.testtags.testTag
import io.element.android.libraries.textcomposer.components.FormattingOption
import io.element.android.libraries.textcomposer.components.FormattingOptionState
import io.element.android.libraries.theme.ElementTheme
@@ -260,7 +262,8 @@ private fun TextInput(
start = 12.dp.applyScaleUp(),
end = 42.dp.applyScaleUp()
)
- ),
+ )
+ .testTag(TestTags.richTextEditor),
contentAlignment = Alignment.CenterStart,
) {
diff --git a/libraries/textcomposer/impl/src/main/res/values-de/translations.xml b/libraries/textcomposer/impl/src/main/res/values-fr/translations.xml
similarity index 56%
rename from libraries/textcomposer/impl/src/main/res/values-de/translations.xml
rename to libraries/textcomposer/impl/src/main/res/values-fr/translations.xml
index 6b75a1c9a7..1a7539c8c6 100644
--- a/libraries/textcomposer/impl/src/main/res/values-de/translations.xml
+++ b/libraries/textcomposer/impl/src/main/res/values-fr/translations.xml
@@ -1,4 +1,4 @@
- "Anhang hinzufügen"
+ "Ajouter une pièce jointe"
diff --git a/libraries/ui-strings/src/main/res/values-cs/translations.xml b/libraries/ui-strings/src/main/res/values-cs/translations.xml
index bbb68b53e8..0ff43bceb7 100644
--- a/libraries/ui-strings/src/main/res/values-cs/translations.xml
+++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml
@@ -96,7 +96,6 @@
"Heslo"
"Lidé"
"Trvalý odkaz"
- "Konečné hlasy: %1$s"
"Celkový počet hlasů: %1$s"
"Výsledky se zobrazí po skončení hlasování"
"Zásady ochrany osobních údajů"
diff --git a/libraries/ui-strings/src/main/res/values-de/translations.xml b/libraries/ui-strings/src/main/res/values-de/translations.xml
index 7aa02bddae..4028ece8ed 100644
--- a/libraries/ui-strings/src/main/res/values-de/translations.xml
+++ b/libraries/ui-strings/src/main/res/values-de/translations.xml
@@ -1,240 +1,65 @@
- "Passwort ausblenden"
- "Dateien senden"
- "Passwort anzeigen"
- "Benutzermenü"
- "Zustimmen"
- "Zurück"
+ "Nur Erwähnungen"
+ "Stummgeschaltet"
"Abbrechen"
- "Foto auswählen"
- "Zurücksetzen"
- "Schließen"
- "Verifizierung abschließen"
"Bestätigen"
"Weiter"
"Kopieren"
- "Link kopieren"
- "Link zur Nachricht kopieren"
- "Erstellen"
"Raum erstellen"
- "Ablehnen"
- "Deaktivieren"
- "Fertig"
+ "Erledigt"
"Bearbeiten"
"Aktivieren"
- "Passwort vergessen?"
- "Weiterleiten"
+ "Umfrage beenden"
"Einladen"
- "Freunde einladen"
- "Freunde zu %1$s einladen"
- "Personen zu %1$s einladen"
- "Einladungen"
"Mehr erfahren"
"Verlassen"
"Raum verlassen"
"Weiter"
- "Nein"
"Nicht jetzt"
"OK"
- "Öffnen mit"
- "Schnellantwort"
- "Zitieren"
- "Reagieren"
+ "Schnelle Antwort"
+ "Zitat"
"Entfernen"
- "Antworten"
+ "Antwort"
"Fehler melden"
"Inhalt melden"
"Erneut versuchen"
- "Entschlüsselung erneut versuchen"
- "Speichern"
- "Suchen"
+ "Entschlüsselung wiederholen"
"Senden"
- "Nachricht senden"
- "Teilen"
- "Link teilen"
- "Überspringen"
- "Starten"
+ "Start"
"Chat starten"
- "Verifizierung starten"
- "Zum Karte laden tippen"
- "Foto aufnehmen"
- "Quelltext anzeigen"
- "Ja"
- "Über"
- "Allgemeine Geschäftsbedingungen"
- "Analyse"
- "Audio"
- "Blasen"
- "Urheberrecht"
- "Erstelle Raum…"
- "Raum verlassen"
- "Entschlüsselungsfehler"
- "Entwickleroptionen"
+ "Überprüfung starten"
+ "Quelle anzeigen"
+ "Dekodierungsfehler"
"(bearbeitet)"
- "Bearbeiten"
- "* %1$s %2$s"
+ "Bearbeitung"
"Verschlüsselung aktiviert"
"Fehler"
"Datei"
- "Datei gespeichert unter Downloads"
- "Nachricht weiterleiten"
"GIF"
"Bild"
- "Diese Matrix-ID kann nicht gefunden werden, daher wird die Einladung möglicherweise nicht empfangen."
- "Verlasse Raum"
- "Link in Zwischenablage kopiert"
- "Lädt…"
+ "Link in die Zwischenablage kopiert"
+ "Laden…"
"Nachricht"
- "Nachrichtenlayout"
"Nachricht entfernt"
- "Modern"
- "Stummschalten"
- "Keine Ergebnisse"
- "Offline"
"Passwort"
- "Personen"
- "Dauerlink"
- "Endgültige Stimmen: %1$s"
- "Stimmen insgesamt: %1$s"
- "Ergebnisse werden nach Ende der Umfrage angezeigt"
- "Datenschutzerklärung"
+ "Menschen"
+ "Permalink"
"Reaktionen"
- "Aktualisiere…"
- "Auf %1$s antworten"
- "Einen Fehler melden"
- "Bericht gesendet"
- "Raumname"
- "z.B. dein Projektname"
- "Suche nach jemandem"
- "Suchergebnisse"
- "Sicherheit"
- "Wählen deinen Server"
- "Sendet…"
- "Server wird nicht unterstützt"
- "Server-URL"
"Einstellungen"
- "Geteilter Standort"
- "Starte Chat…"
"Sticker"
- "Erfolg"
"Vorschläge"
- "Synchronisiere…"
- "Hinweise von Drittanbietern"
+ "Text"
"Thema"
- "Worum geht es in diesem Raum?"
- "Entschlüsselung nicht möglich"
- "Einladungen konnten nicht an einen oder mehrere Benutzer gesendet werden."
- "Einladung(en) können nicht gesendet werden"
- "Stummschaltung aufheben"
"Nicht unterstütztes Ereignis"
"Benutzername"
- "Verifizierung abgebrochen"
- "Verifizierung abgeschlossen"
"Video"
- "Warte…"
- "Bestätigung"
- "Warnung"
- "Aktivitäten"
- "Flaggen"
- "Essen & Trinken"
- "Tiere & Natur"
- "Objekte"
- "Smileys & Personen"
- "Reisen & Orte"
- "Symbole"
- "Fehler beim Erstellen des Dauerlinks"
- "%1$s konnte die Karte nicht laden. Bitte versuche es später erneut."
- "Fehler beim Laden der Nachrichten"
- "%1$s konnte nicht auf deinen Standort zugreifen. Bitte versuche es später erneut."
- "%1$s hat keine Berechtigung, auf deinen Standort zuzugreifen. Du kannst den Zugriff in den Einstellungen aktivieren."
- "%1$s hat keine Berechtigung, auf deinen Standort zuzugreifen. Aktiviere den Zugriff unten."
- "Einige Nachrichten wurden nicht gesendet"
- "Entschuldigung, ein Fehler ist aufgetreten."
- "🔐️ Besuche mich auf %1$s"
- "Hey, sprich mit mir auf %1$s: %2$s"
- "Bist du sicher, dass du diesen Raum verlassen willst? Du bist die einzige Person hier. Wenn du gehst, kann in Zukunft niemand mehr beitreten, auch du nicht."
- "Bist du dir sicher, dass du den Raum verlassen möchtest? Dieser Raum ist nicht öffentlich und du kannst ihm ohne eine Einladung nicht mehr beitreten."
- "Bist du dir sicher, dass du den Raum verlassen möchtest?"
- "%1$s Android"
-
- - "%1$d Mitglied"
- - "%1$d Mitglieder"
-
-
- - "%d Stimme"
- - "%d Stimmen"
-
- "Schütteln zum Melden von Fehlern"
- "Du scheinst frustriert das Telefon zu schütteln. Möchtest du den Fehlerberichtsbildschirm öffnen?"
- "Diese Nachricht wird an deinen Heimserver-Admin gemeldet. Er wird nicht in der Lage sein, verschlüsselte Nachrichten zu lesen."
- "Grund für die Meldung dieses Inhalts"
- "Aufzählungsliste ein-/ausschalten"
- "Codeblock umschalten"
- "Nachricht…"
- "Fettformatierung anwenden"
- "Kursivformat anwenden"
- "Durchgestrichenes Format anwenden"
- "Unterstreichungsformat anwenden"
- "Vollbildmodus umschalten"
- "Einrücken"
- "Inline-Codeformat anwenden"
- "Link setzen"
- "Nummerierte Liste ein-/ausschalten"
- "Zitat umschalten"
- "Einrücken aufheben"
- "Dies ist der Anfang von %1$s."
- "Dies ist der Beginn dieser Konversation."
- "Neu"
- "Teile Analyse-Daten"
- "Medienauswahl fehlgeschlagen, bitte versuche es erneut."
- "Fehler bei der Verarbeitung von Medien zum Hochladen, bitte versuche es erneut."
- "Hochladen von Medien fehlgeschlagen, bitte versuchen Sie es erneut."
- "Zusätzliche Einstellungen"
- "Audio- und Videoanrufe"
- "Konfigurationskonflikt"
- "Wir haben die Benachrichtigungseinstellungen vereinfacht, damit Optionen leichter zu finden sind.
-
-Einige benutzerdefinierte Einstellungen, die du in der Vergangenheit ausgewählt hast, werden hier nicht angezeigt, sind aber immer noch aktiv.
-
-Wenn du fortfährst, ändern sich möglicherweise einige deine Einstellungen."
- "Direkte Chats"
- "Benutzerdefinierte Einstellung pro Chat"
- "Beim Aktualisieren der Benachrichtigungseinstellung ist ein Fehler aufgetreten."
- "Alle Nachrichten"
- "Nur Erwähnungen und Schlüsselwörter"
- "Bei direkten Chats, benachrichtigen mich für"
- "Bei Gruppenchats, benachrichtigte mich für"
- "Benachrichtigungen auf diesem Gerät aktivieren"
- "Die Konfiguration wurde nicht korrigiert. Bitte versuche es erneut."
- "Gruppenchats"
- "Erwähnungen"
- "Alle"
- "Erwähnungen"
- "Benachrichtige mich für"
- "Benachrichtige mich bei @room"
- "Um Benachrichtigungen zu erhalten, ändern bitte deine %1$s."
- "Systemeinstellungen"
- "Systembenachrichtigungen deaktiviert"
- "Benachrichtigungen"
- "Prüfe, ob du alle aktuellen und zukünftigen Nachrichten dieses Benutzers ausblenden möchtest"
- "Konto und Geräte"
- "Standort teilen"
- "Meinen Standort teilen"
- "In Apple Maps öffnen"
- "In Google Maps öffnen"
- "In OpenStreetMap öffnen"
- "Diesen Ort teilen"
- "Standort"
- "Rageshake"
- "Erkennungsschwelle"
- "Allgemein"
- "Version: %1$s (%2$s)"
- "de"
+ "Warten…"
+ "Formatierungsoptionen schließen"
+ "Einen Link erstellen"
+ "Link bearbeiten"
+ "Link"
"Fehler"
- "Erfolg"
- "Teile anonyme Nutzungsdaten, um uns bei der Identifizierung von Problemen zu helfen."
- "Du kannst alle unsere Nutzerbedingungen %1$s lesen."
"hier"
- "Nutzer blockieren"
diff --git a/libraries/ui-strings/src/main/res/values-fr/translations.xml b/libraries/ui-strings/src/main/res/values-fr/translations.xml
index 745bde6497..67b738cc21 100644
--- a/libraries/ui-strings/src/main/res/values-fr/translations.xml
+++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml
@@ -1,106 +1,122 @@
"Masquer le mot de passe"
+ "Mentions uniquement"
+ "En sourdine"
"Envoyer des fichiers"
"Afficher le mot de passe"
"Menu utilisateur"
"Accepter"
"Retour"
"Annuler"
- "Choisir une photo"
+ "Choisissez une photo"
"Effacer"
"Fermer"
- "Compléter la vérification"
+ "Terminer la vérification"
"Confirmer"
"Continuer"
"Copier"
"Copier le lien"
"Copier le lien vers le message"
"Créer"
- "Créer un salon"
+ "Créer une salle"
"Refuser"
"Désactiver"
"Terminé"
"Modifier"
"Activer"
+ "Terminer le sondage"
"Mot de passe oublié ?"
"Transférer"
"Inviter"
"Inviter des amis"
"Inviter des amis à %1$s"
- "Inviter des gens sur %1$s"
+ "Invitez des personnes à %1$s"
"Invitations"
"En savoir plus"
"Quitter"
- "Quitter le salon"
+ "Quitter la salle"
+ "Gérer le compte"
+ "Gérez les appareils"
"Suivant"
"Non"
"Pas maintenant"
"OK"
- "Ouvrir avec"
+ "Ouvrez avec"
"Réponse rapide"
"Citer"
- "Supprimer"
+ "Réagissez"
+ "Enlever"
"Répondre"
- "Signaler un bug"
+ "Répondre dans le fil de discussion"
+ "Signaler un bogue"
"Signaler le contenu"
"Réessayer"
- "Réessayer le déchiffrement"
+ "Réessayer le décryptage"
"Enregistrer"
- "Chercher"
+ "Rechercher"
"Envoyer"
"Envoyer un message"
"Partager"
"Partager le lien"
- "Passer"
+ "Sauter"
"Démarrer"
- "Commencer un chat"
+ "Démarrer la discussion"
"Commencer la vérification"
- "Touchez pour charger la carte"
+ "Appuyez pour charger la carte"
"Prendre une photo"
- "Voir la source"
+ "Afficher la source"
"Oui"
+ "Appel en cours"
+ "Appuyez pour retourner à l\'appel."
+ "☎️ Appel en cours"
"À propos"
- "Politique d’utilisation"
+ "Politique d\'utilisation acceptable"
+ "Paramètres avancés"
"Statistiques d\'utilisation"
"Audio"
"Bulles"
- "Copyright"
- "Création du salon…"
- "Le salon a été quitté"
- "Erreur de déchiffrement"
- "Options de développement"
+ "Droits d\'auteur"
+ "Création de salle…"
+ "Quitter la salle"
+ "Erreur de décryptage"
+ "Options pour les développeurs"
"(modifié)"
- "Modification en cours"
+ "Édition"
"* %1$s %2$s"
- "Chiffrement activé"
+ "Cryptage activé"
"Erreur"
"Fichier"
- "Fichier enregistré dans les Téléchargements"
+ "Fichier enregistré dans Téléchargements"
"Transférer le message"
"GIF"
"Image"
- "Nous ne pouvons pas vérifier le Matrix ID de cet utilisateur. Cette invitation pourrait être envoyée dans le vide."
- "Quitter le salon"
+ "En réponse à %1$s"
+ "Cet identifiant Matrix est introuvable, il est donc possible que l\'invitation ne soit pas reçue."
+ "Quitter la salle"
"Lien copié dans le presse-papiers"
"Chargement…"
"Message"
"Mode d\'affichage des messages"
"Message supprimé"
"Moderne"
- "Sourdine"
+ "Mettre en sourdine"
"Aucun résultat"
"Hors ligne"
"Mot de passe"
"Personnes"
"Permalien"
+ "Nombre total de votes : %1$s"
+ "Les résultats s\'afficheront une fois le sondage terminé"
"Politique de confidentialité"
+ "Réaction"
"Réactions"
"Actualisation…"
"En réponse à %1$s"
- "Signaler un problème"
- "Rapport envoyé"
- "Nom du salon"
+ "Signaler un bogue"
+ "Rapport soumis"
+ "Éditeur de texte enrichi"
+ "Nom de la salle"
"par exemple, le nom de votre projet"
"Rechercher quelqu\'un"
"Résultats de la recherche"
@@ -110,25 +126,27 @@
"Serveur non pris en charge"
"URL du serveur"
"Paramètres"
- "Position partagée"
- "Démarrage du chat…"
+ "Emplacement partagé"
+ "Démarrer le chat…"
"Autocollant"
"Succès"
"Suggestions"
"Synchronisation"
- "Mentions tierces"
+ "Texte"
+ "Avis de tiers"
+ "Fil de discussion"
"Sujet"
- "De quoi parle ce salon ?"
+ "De quoi s\'agit-il dans cette salle ?"
"Échec de déchiffrement"
- "Nous n\'avons pas réussi à envoyer des invitations à un ou plusieurs utilisateurs."
+ "Les invitations n\'ont pas pu être envoyées à un ou plusieurs utilisateurs."
"Impossible d\'envoyer une ou plusieurs invitations"
- "Réactiver"
+ "Annuler la sourdine"
"Événement non pris en charge"
"Nom d\'utilisateur"
"Vérification annulée"
"Vérification terminée"
"Vidéo"
- "Patientez…"
+ "En attente…"
"Confirmation"
"Attention"
"Activités"
@@ -140,65 +158,103 @@
"Voyages & lieux"
"Symboles"
"Échec de la création du permalien"
- "%1$s n’a pas pu charger la carte. Veuillez réessayer plus tard."
+ "%1$s n\'a pas pu charger la carte. Veuillez réessayer ultérieurement."
"Échec du chargement des messages"
+ "%1$s n\'a pas pu accéder à votre emplacement. Veuillez réessayer ultérieurement."
+ "%1$s n\'est pas autorisé à accéder à votre position. Vous pouvez activer l\'accès dans les Paramètres."
+ "%1$s n\'est pas autorisé à accéder à votre position. Activez l\'accès ci-dessous."
"Certains messages n\'ont pas été envoyés"
- "Désolé, une erreur est survenue."
+ "Désolé, une erreur s\'est produite"
"🔐️ Rejoignez-moi sur %1$s"
"Salut, parle-moi sur %1$s : %2$s"
- "Êtes-vous sûr de vouloir quitter ce salon ? Vous êtes la seule personne ici. Si vous partez, personne ne pourra plus rejoindre ce salon, y compris vous."
- "Êtes-vous sûr de vouloir quitter ce salon ? Ce salon n\'est pas public et vous ne pourrez pas le rejoindre sans invitation."
- "Êtes-vous sûr de vouloir quitter le salon ?"
+ "Êtes-vous sûr de vouloir quitter cette salle ? Vous êtes la seule personne ici. Si vous partez, personne ne pourra rejoindre la salle à l\'avenir, y compris vous."
+ "Êtes-vous sûr de vouloir quitter cette salle ? Cette salle n\'est pas publique et vous ne pourrez pas la rejoindre sans invitation."
+ "Êtes-vous sûr de vouloir quitter la salle ?"
"%1$s Android"
- "%1$d membre"
- "%1$d membres"
- "Rageshake pour signaler un bug"
+
+ - "%d vote"
+ - "%d votes"
+
+ "Rageshake pour signaler un bogue"
"Vous semblez secouer le téléphone de frustration. Voulez-vous ouvrir le formulaire de rapport de problème ?"
- "Ce message sera signalé à l’administrateur de votre serveur d\'accueil. Ils ne pourront lire aucun message chiffré."
+ "Ce message sera signalé à l’administrateur de votre serveur d\'accueil. Ils ne pourront lire aucun message crypté."
"Raison du signalement de ce contenu"
"Afficher une liste à puces"
+ "Fermer les options de formatage"
"Afficher le bloc de code"
- "Envoyer un message…"
+ "Message…"
+ "Créer un lien"
+ "Modifier le lien"
"Appliquer le format gras"
"Appliquer le format italique"
"Appliquer le format barré"
"Appliquer le format souligné"
- "Afficher en mode plein écran"
+ "Activer/désactiver le mode plein écran"
"Décaler vers la droite"
"Appliquer le formatage de code en ligne"
"Définir un lien"
"Afficher une liste numérotée"
- "Afficher une citation"
+ "Ouvrir les options de rédaction"
+ "Afficher/masquer la citation"
+ "Supprimer le lien"
"Décaler vers la gauche"
+ "Lien"
"Ceci est le début de %1$s."
"Ceci est le début de cette conversation."
"Nouveau"
- "Partager les statistiques d\'utilisation"
- "Impossible de sélectionner un média, veuillez réessayer."
- "Échec du traitement du média avant son envoi, veuillez réessayer."
- "Impossible d’envoyer le média, veuillez réessayer."
+ "Partagez des données de statistiques d\'utilisation"
+ "Échec de la sélection du média, veuillez réessayer."
+ "Échec du traitement des médias à télécharger, veuillez réessayer."
+ "Échec du téléchargement du média, veuillez réessayer."
+ "Réglages supplémentaires"
+ "Appels audio et vidéo"
+ "Incompatibilité de configuration"
+ "Nous avons simplifié les paramètres des notifications pour que les options soient plus faciles à trouver.
+
+Certains paramètres personnalisés que vous avez choisis par le passé ne sont pas affichés ici, mais ils sont toujours actifs.
+
+Si vous continuez, il est possible que certains de vos paramètres soient modifiés."
+ "Discussions directes"
+ "Paramétrage personnalisé par chat"
+ "Une erreur s\'est produite lors de la mise à jour du paramètre de notification."
+ "Tous les messages"
+ "Mentions et mots clés uniquement"
+ "Sur les chats directs, prévenez-moi pour"
+ "Lors de discussions de groupe, prévenez-moi pour"
"Activer les notifications sur cet appareil"
- "paramètres système"
- "Notifications système désactivées"
+ "La configuration n\'a pas été corrigée, veuillez réessayer."
+ "Discussions de groupe"
+ "Mentions"
+ "Tous"
+ "Mentions"
+ "Prévenez-moi pour"
+ "Prévenez-moi sur @salle"
+ "Pour recevoir des notifications, veuillez modifier votre %1$s."
+ "paramètres du système"
+ "Les notifications du système sont désactivées"
"Notifications"
"Cochez si vous souhaitez masquer tous les messages actuels et futurs de cet utilisateur."
- "Partage de position"
- "Partager ma position"
+ "Compte et appareils"
+ "Emplacement partagé"
+ "Partager mon emplacement"
"Ouvrir dans Apple Maps"
"Ouvrir dans Google Maps"
"Ouvrir dans OpenStreetMap"
- "Partager cette position"
+ "Partager cet emplacement"
+ "Emplacement"
"Rageshake"
"Seuil de détection"
"Général"
- "Version: %1$s ( %2$s )"
- "fr"
+ "Version : %1$s ( %2$s )"
+ "Ang."
"Erreur"
"Succès"
"Partagez des données d\'utilisation anonymes pour nous aider à identifier les problèmes."
- "Consultez nos conditions d\'utilisation %1$s."
+ "Vous pouvez lire toutes nos conditions %1$s."
"ici"
"Bloquer l\'utilisateur"
diff --git a/libraries/ui-strings/src/main/res/values-ro/translations.xml b/libraries/ui-strings/src/main/res/values-ro/translations.xml
index d96abca3cc..c122a37d96 100644
--- a/libraries/ui-strings/src/main/res/values-ro/translations.xml
+++ b/libraries/ui-strings/src/main/res/values-ro/translations.xml
@@ -98,7 +98,6 @@
"Parola"
"Persoane"
"Permalink"
- "Voturi finale: %1$s"
"Total voturi: %1$s"
"Rezultatele vor fi afișate după încheierea sondajului"
"Politica de confidențialitate"
diff --git a/libraries/ui-strings/src/main/res/values-ru/translations.xml b/libraries/ui-strings/src/main/res/values-ru/translations.xml
index 28284f6347..798515fa6a 100644
--- a/libraries/ui-strings/src/main/res/values-ru/translations.xml
+++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml
@@ -95,7 +95,6 @@
"Пароль"
"Пользователи"
"Постоянная ссылка"
- "Итоговые голоса: %1$s"
"Всего голосов: %1$s"
"Результаты будут показаны после завершения опроса"
"Политика конфиденциальности"
diff --git a/libraries/ui-strings/src/main/res/values-sk/translations.xml b/libraries/ui-strings/src/main/res/values-sk/translations.xml
index cad1b5a564..162ca8f9d8 100644
--- a/libraries/ui-strings/src/main/res/values-sk/translations.xml
+++ b/libraries/ui-strings/src/main/res/values-sk/translations.xml
@@ -1,6 +1,8 @@
"Skryť heslo"
+ "Iba zmienky"
+ "Stlmené"
"Odoslať súbory"
"Zobraziť heslo"
"Používateľské menu"
@@ -23,6 +25,7 @@
"Hotovo"
"Upraviť"
"Povoliť"
+ "Ukončiť anketu"
"Zabudnuté heslo?"
"Preposlať"
"Pozvať"
@@ -95,7 +98,6 @@
"Heslo"
"Ľudia"
"Trvalý odkaz"
- "Výsledné hlasovanie: %1$s"
"Celkový počet hlasov: %1$s"
"Výsledky sa zobrazia po ukončení ankety"
"Zásady ochrany osobných údajov"
@@ -120,6 +122,7 @@
"Úspech"
"Návrhy"
"Synchronizuje sa"
+ "Text"
"Oznámenia tretích strán"
"Téma"
"O čom je táto miestnosť?"
@@ -172,8 +175,11 @@
"Táto správa bude nahlásená správcovi vášho domovského servera. Nebude môcť prečítať žiadne šifrované správy."
"Dôvod nahlásenia tohto obsahu"
"Prepnúť zoznam odrážok"
+ "Zatvoriť možnosti formátovania"
"Prepnúť blok kódu"
"Správa…"
+ "Vytvoriť odkaz"
+ "Upraviť odkaz"
"Použiť tučný formát"
"Použiť formát kurzívy"
"Použiť formát prečiarknutia"
@@ -183,8 +189,10 @@
"Použiť formát riadkového kódu"
"Nastaviť odkaz"
"Prepnúť číslovaný zoznam"
+ "Otvoriť možnosti písania"
"Prepnúť citáciu"
"Zrušiť odsadenie"
+ "Odkaz"
"Toto je začiatok %1$s."
"Toto je začiatok tejto konverzácie."
"Nové"
diff --git a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml
index ddaaed0d33..b385e7c8a4 100644
--- a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml
+++ b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml
@@ -31,6 +31,8 @@
"了解更多"
"離開"
"離開聊天室"
+ "管理帳號"
+ "管理裝置"
"下一個"
"否"
"以後再說"
@@ -144,6 +146,8 @@
"切換項目編號"
"切換程式碼區塊"
"訊息"
+ "建立連結"
+ "編輯連結"
"套用粗體"
"套用斜體"
"套用刪除線"
@@ -154,6 +158,7 @@
"切換數字編號"
"切換引用"
"減少縮排"
+ "連結"
"新訊息"
"分享分析數據"
"無法上傳媒體檔案,請稍後再試。"
@@ -168,8 +173,8 @@
"通知"
"分享位置"
"分享我的位置"
- "在 Apple 地圖中開啟"
- "在 Google 地圖中開啟"
+ "在 Apple Maps 中開啟"
+ "在 Google Maps 中開啟"
"在開放街圖(OpenStreetMap) 中開啟"
"分享這個位置"
"位置"
@@ -178,5 +183,8 @@
"zh-tw"
"錯誤"
"成功"
+ "分享匿名的使用數據以協助我們釐清問題"
+ "您可以到 %1$s 閱讀我們的條款。"
+ "這裡"
"封鎖使用者"
diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml
index 3098b9b5b3..2355e4b603 100644
--- a/libraries/ui-strings/src/main/res/values/localazy.xml
+++ b/libraries/ui-strings/src/main/res/values/localazy.xml
@@ -200,6 +200,7 @@
"Toggle numbered list"
"Open compose options"
"Toggle quote"
+ "Remove link"
"Unindent"
"Link"
"This is the beginning of %1$s."
diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/WarmUpRule.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/WarmUpRule.kt
index a192cfbd63..4eeef468ff 100644
--- a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/WarmUpRule.kt
+++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/WarmUpRule.kt
@@ -32,17 +32,22 @@ import kotlin.time.Duration.Companion.seconds
* Applying this test rule ensures that the slow initialisation is not done
* inside runTest which has a short default timeout.
*/
-class WarmUpRule: TestRule {
- override fun apply(base: Statement, description: Description): Statement = object: Statement() {
- override fun evaluate() {
- runTest(timeout = 60.seconds) {
- moleculeFlow(RecompositionMode.Immediate) {
- // Do nothing
- }.test {
- awaitItem() // Await a Unit composition
- }
- }
- base.evaluate()
+class WarmUpRule : TestRule {
+ companion object {
+ init {
+ warmUpMolecule()
+ }
+ }
+
+ override fun apply(base: Statement, description: Description): Statement = base
+}
+
+private fun warmUpMolecule() {
+ runTest(timeout = 60.seconds) {
+ moleculeFlow(RecompositionMode.Immediate) {
+ // Do nothing
+ }.test {
+ awaitItem() // Await a Unit composition
}
}
}
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderViewDark_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderViewDark_0_null_0,NEXUS_5,1.0,en].png
index f7ed356451..bc70d900bc 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderViewDark_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderViewDark_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c8a6aa5c7bdddcb18453efc47f90dd0301173bcfa5622abc0821eb09cb2579d8
-size 25149
+oid sha256:856fc14eb010048f355c902419d7d346f77bfb26e79bbba7e966363a39f503f5
+size 24884
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderViewDark_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderViewDark_0_null_1,NEXUS_5,1.0,en].png
index 3b68e95447..ebb99ef2c6 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderViewDark_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderViewDark_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:b6344d9976849f1e56782a463fcca625943a7c6adf74c598c7a58245809b9db5
-size 53642
+oid sha256:d4329c822e128dcfa495df0c81126d929fa5f787e28f3c144009aaaaafae4dc6
+size 53371
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderViewLight_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderViewLight_0_null_0,NEXUS_5,1.0,en].png
index cad7ad3f59..54d60d6642 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderViewLight_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderViewLight_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:602d7585b9693d08b1a4e3e620f2cecbbbde2a9b0e6b2beeb77f244520c5e4eb
-size 25827
+oid sha256:8222670d7b08fc9f5e446e86a31fad4fa773bbac03742df9bb5dfb35189571aa
+size 25617
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderViewLight_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderViewLight_0_null_1,NEXUS_5,1.0,en].png
index 2e35a8461c..79b7a23804 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderViewLight_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.login.impl.screens.searchaccountprovider_null_SearchAccountProviderViewLight_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:fd7e6fc09011429e0fe19bff73d3c48124b1f3bed74d3cb12c9f3f0405f53980
-size 55728
+oid sha256:a4830db08b7e09971e299c31f18ce45dfa500c59a056b407c7df397b679e23d3
+size 55455
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-D-13_14_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-D-13_14_null_0,NEXUS_5,1.0,en].png
index 84f2abf39d..8ba3fa59a7 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-D-13_14_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-D-13_14_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:339e5f22a47e29b6f681ac169be3b65568b7fa5d1197b0e81fb68bef639ae5c2
-size 49033
+oid sha256:801f832469346524fdce0b5ad8654c3405daf0f21ae0601c62dc7f148b576ce8
+size 49074
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-D-13_14_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-D-13_14_null_1,NEXUS_5,1.0,en].png
index 0fbe7c6581..2af13b5df7 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-D-13_14_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-D-13_14_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c439f506df4eaed4ad35148d1734630a89b0de3ccb097864b7ff853c679772c1
-size 50964
+oid sha256:944eb2ef8abf2e8f1715d063020767940c80b40690aae1129f69d02ddafac9d0
+size 51029
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-N-13_15_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-N-13_15_null_0,NEXUS_5,1.0,en].png
index dacffc90ea..0e2fb91a11 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-N-13_15_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-N-13_15_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f85799360092c7e2d02f64f32668279c4e0c39bca3d45c9db13d503ddb7bf753
-size 46162
+oid sha256:adf6f3f79b9d8f62172171dd8a172bff1958bc4698df4586bf83c73fe4c6c6f3
+size 46198
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-N-13_15_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-N-13_15_null_1,NEXUS_5,1.0,en].png
index 40e0174919..c06bfdad7b 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-N-13_15_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_null_TimelineItemPollView-N-13_15_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:b05475d6509b15fcffeae54bdebf434a529a99af5173c48992677a88b654f3eb
-size 48336
+oid sha256:38183d1d69e36c2570259c987be079d09ecfc0a0cba7508fc43d33e95c574121
+size 48368
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventTimestampBelow_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventTimestampBelow_0_null,NEXUS_5,1.0,en].png
index 4ba636ed5c..ea0825524c 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventTimestampBelow_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components_null_TimelineItemEventTimestampBelow_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:bdcc950b0ce924a73ae742f0bb3fc7279de46f36ba01d377822a2a14b6f90343
-size 56239
+oid sha256:54a4fe8e7f968bf487168a517baa0d9c9f6fc5b2354f4eff45f56de77b043f8f
+size 56535
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerDisclosedNotSelected_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerDisclosedNotSelected_0_null,NEXUS_5,1.0,en].png
index a3e90a69fe..7a81cb7428 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerDisclosedNotSelected_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerDisclosedNotSelected_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:74508ef7f77a9c8713c75586ae4d34a9daab2608dbbd2f20de3e4d4a9a9be7e9
-size 39225
+oid sha256:9f97a2f591619aa2dd914f79a2bdfabea3e4e8239e6d80f29400e2c02c6ae21d
+size 39250
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerDisclosedSelected_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerDisclosedSelected_0_null,NEXUS_5,1.0,en].png
index 7be79c2135..5e990f7141 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerDisclosedSelected_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerDisclosedSelected_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:4ac9e0523fc99d472a1fe8f21719e64dbb7d1ec01059dd4183aa4a152f8ead55
-size 38673
+oid sha256:03b4bfab1cbbbedd9219423834f3bdf47f40e20c0da791be2c892cb2af86cf7d
+size 38694
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerEndedSelected_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerEndedSelected_0_null,NEXUS_5,1.0,en].png
index 7ee39de1a4..7ebe08ccd8 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerEndedSelected_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerEndedSelected_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ed68d9ebe67d5dad938a3efcd8c2b680444c650e635bdc04cfd140ba694d9f1d
-size 38928
+oid sha256:1672ff3a807b387b1f780920111c19484261b36884f1f68d72e108c90a4104d2
+size 38948
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerEndedWinnerNotSelected_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerEndedWinnerNotSelected_0_null,NEXUS_5,1.0,en].png
index b2d901dc0d..96596d6000 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerEndedWinnerNotSelected_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerEndedWinnerNotSelected_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:1d5c9d2042dad75b48b61cdbae5b2425d7c935eb860df5d5c6fa3bcb327d13d1
-size 38842
+oid sha256:43da602c904210df0112a1af831cd2a2f7a7c5109d26605efad464a3c5b58995
+size 38864
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerEndedWinnerSelected_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerEndedWinnerSelected_0_null,NEXUS_5,1.0,en].png
index 4998418006..5396c8592b 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerEndedWinnerSelected_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerEndedWinnerSelected_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d4ab648f5651b7457635be3eabb914be1c976154d12a163f1c1bb2cd92168824
-size 38730
+oid sha256:bf806b2490fc8a1df278bde94535cb1b5d38f66531df85d6fd1251e6671d9f56
+size 38756
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerUndisclosedNotSelected_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerUndisclosedNotSelected_0_null,NEXUS_5,1.0,en].png
index a2cd64d048..a9c192a685 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerUndisclosedNotSelected_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerUndisclosedNotSelected_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:fbb713d36f8b36ce6b55f38c10323e784a80f6187e6613ded91dc531b23a7cb7
-size 36444
+oid sha256:eeaa924482fa2fc29079f7f0d4bddacb96567a2133808bed8b49be2ad7afe80f
+size 36481
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerUndisclosedSelected_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerUndisclosedSelected_0_null,NEXUS_5,1.0,en].png
index 2d973414d1..b16b55dc1c 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerUndisclosedSelected_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollAnswerUndisclosedSelected_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:31735c42ea83974595544a0f03636a2337210f23e60d4b7f8e41d46ba21d483f
-size 35920
+oid sha256:de340de9f0f07a44c9d2c1af196abc059d32f62fc857214cdf2c1d1e1951d267
+size 35915
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentDisclosed-D-1_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentDisclosed-D-1_1_null,NEXUS_5,1.0,en].png
index 84f2abf39d..8ba3fa59a7 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentDisclosed-D-1_1_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentDisclosed-D-1_1_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:339e5f22a47e29b6f681ac169be3b65568b7fa5d1197b0e81fb68bef639ae5c2
-size 49033
+oid sha256:801f832469346524fdce0b5ad8654c3405daf0f21ae0601c62dc7f148b576ce8
+size 49074
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentDisclosed-N-1_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentDisclosed-N-1_2_null,NEXUS_5,1.0,en].png
index dacffc90ea..0e2fb91a11 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentDisclosed-N-1_2_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentDisclosed-N-1_2_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f85799360092c7e2d02f64f32668279c4e0c39bca3d45c9db13d503ddb7bf753
-size 46162
+oid sha256:adf6f3f79b9d8f62172171dd8a172bff1958bc4698df4586bf83c73fe4c6c6f3
+size 46198
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentEnded-D-2_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentEnded-D-2_2_null,NEXUS_5,1.0,en].png
index e700e58b7e..e54dcafe51 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentEnded-D-2_2_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentEnded-D-2_2_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:85fbaa916a38c9b75a527dc3fddded9e9d06a98ab20f9f3eb74596c24ba4b5b7
-size 49105
+oid sha256:7d0674f2bac8e4912bb1faec069be0e86e3b796f61e6217494cfe42cce89dca2
+size 49143
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentEnded-N-2_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentEnded-N-2_3_null,NEXUS_5,1.0,en].png
index 26b2576508..2a9c20d9c2 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentEnded-N-2_3_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentEnded-N-2_3_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:cee9e96cfefa6f84fb924857a92fd4ba04bbd196d94a6345bb821ff769fbd56d
-size 45889
+oid sha256:f54d112dc90c4e2402eeab5fe8e649ffc397e208c395a19f37889038206c8179
+size 45927
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentUndisclosed-D-0_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentUndisclosed-D-0_0_null,NEXUS_5,1.0,en].png
index 41f7fba3e5..05fb3ba861 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentUndisclosed-D-0_0_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentUndisclosed-D-0_0_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:53cffdc5ea12b16db3f87ab12c28fede82ce5a744c222c9f19e5c35ef751c583
-size 47179
+oid sha256:4fe5ffa68f8ea13ae4343a4fd915c58c1ff9157433248ac93ffa8ab6d8ecbbdf
+size 47223
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentUndisclosed-N-0_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentUndisclosed-N-0_1_null,NEXUS_5,1.0,en].png
index cb51eea54a..59cc1e7ce4 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentUndisclosed-N-0_1_null,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.api_null_PollContentUndisclosed-N-0_1_null,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:96d696014b24a2a222ba331d754d6048063a9ec573158edc41ded92ed588b60d
-size 43593
+oid sha256:137bce4fc589d2275239c84ebea5a3fc07f90a31a8dbb0a5efb830a9433aa437
+size 43650
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_0,NEXUS_5,1.0,en].png
index f60c0c7a4d..175d444754 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:6b06ec4a259dfccec114689bff7d53089bb7fc64758af23372938fd83c422071
-size 35374
+oid sha256:32651c4e32cea6891c8695b468db30dd3b6eefe72b1d441c3a5fbacfa08276a9
+size 34493
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_1,NEXUS_5,1.0,en].png
index c53d96b0e5..43bdd9bee7 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:72bb304299954abac15f9487feab06649c0151c41cbcbcbf9c887417224d499b
-size 39756
+oid sha256:95a8fd37e77521464a4af078b1e5b4113872fe875f6de90c6218d77b0168467b
+size 38913
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_2,NEXUS_5,1.0,en].png
index 8e47757aa3..9e63cc654a 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:636e6c2494e69d7f1383e0c2ef178db484cfa6802c6714ee20a66aad10f4421f
-size 40500
+oid sha256:0087805cdddb75fdb34775264949ce5974d8679c752b1eea87cfc44563e57de4
+size 40202
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_3,NEXUS_5,1.0,en].png
index 9de6f34f78..f36ed1b6ee 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-D-0_1_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:1bb6afbfd69bf254a2d222deb72d80c7f0bb4fc44bc5010a7e34f2b82420a423
-size 47529
+oid sha256:15f481a983765a5aeffff837aa0b7727e0fe87e69cba189ec78d56928630ff63
+size 46634
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_0,NEXUS_5,1.0,en].png
index ce95adf2e9..e31b3b4ce2 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:5f7fbf51ee1d86b1fc7cce949f88bfcb1c7ff7f700304e5a74242c4aa4965fcb
-size 33455
+oid sha256:9ea6ba41fb57c9353ffe26014852ab8517a634e09106168cf9352391bee45270
+size 32643
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_1,NEXUS_5,1.0,en].png
index 8e11aa2691..995416c8df 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:2c7ca3daff99c6086f114d15dbf0649a4be7ff99a5afdeb6d0e8effda383bfec
-size 36968
+oid sha256:91680df981504bad5d82760ffe38ee3aca9a7b34adf9ab4ab1b204e4381ce855
+size 36189
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_2,NEXUS_5,1.0,en].png
index 96707cb507..ad4b412b67 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:58ced9ebe2a161167f5515efa18b87eb02c340b75db47af84f91eab0866b113f
-size 36370
+oid sha256:5b2bfcc5e61adb3b41d79bc25e4a5c159b0a729da4026bf4347f9ac0a7966b5d
+size 36097
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_3,NEXUS_5,1.0,en].png
index 79faef2ce6..3839132e39 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.poll.impl.create_null_CreatePollView-N-0_2_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0680506590290c4ab5ae299abc51e0806b9a1e06a647971eae7b6a2227b0aba9
-size 44631
+oid sha256:71420ae88ea8d3e71a7799597f8132514bab5757cdf644d157bbb527248a4c4e
+size 43862
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_null_DefaultNotificationSettingOption-D-1_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_null_DefaultNotificationSettingOption-D-1_2_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..2d9578c3c2
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_null_DefaultNotificationSettingOption-D-1_2_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:954c763c082f8e4016247643408a61a038a221bb84643ba7e80f3389818528ba
+size 15587
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_null_DefaultNotificationSettingOption-N-1_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_null_DefaultNotificationSettingOption-N-1_3_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..8a32c8d742
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_null_DefaultNotificationSettingOption-N-1_3_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:30886fbb00695077a80e046cd6c6c134375be8ecb8c3962e72fdc0dff60d7069
+size 14194
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_InvalidNotificationSettingsViewDark_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_InvalidNotificationSettingsViewDark_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..f7d27ba4c0
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_InvalidNotificationSettingsViewDark_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fbece1a3764a140e2e8218d34517461809acf67ea87715140fa22b81ffa9d7c0
+size 42771
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_InvalidNotificationSettingsViewight_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_InvalidNotificationSettingsViewight_0_null,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..eee59588f1
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_InvalidNotificationSettingsViewight_0_null,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a6c2719db95641bd6c2b613c65008ebecb8f8989d74d76c8070da0d741be3e7d
+size 43506
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_NotificationSettingsViewDark_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_NotificationSettingsViewDark_0_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..926b1b641a
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_NotificationSettingsViewDark_0_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c100e53723c6ed01ed1d82bba61e355bfc89ef45152f1490f664cf056beb95ce
+size 49228
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_NotificationSettingsViewLight_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_NotificationSettingsViewLight_0_null_0,NEXUS_5,1.0,en].png
new file mode 100644
index 0000000000..dc08c6b74b
--- /dev/null
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_null_NotificationSettingsViewLight_0_null_0,NEXUS_5,1.0,en].png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3d3ebc3196c825b08f7622ad370bb69f2989e57a0fc69058f41877163b43fd1a
+size 52756
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewDark--1_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewDark--1_1_null_0,NEXUS_5,1.0,en].png
index 21903d1663..ee4debea8d 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewDark--1_1_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewDark--1_1_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:239a01988afb77f399b9488e3c314846a59d368ba2a7fd8bec1514ff60dbd8ab
-size 39772
+oid sha256:a99b724ff8e90a48557c38909fe62d3fb1b260c3b5f6a57ac9f3b25879aad1ef
+size 41532
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewDark--1_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewDark--1_1_null_1,NEXUS_5,1.0,en].png
index 2fbb0cbc02..6d6798e98a 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewDark--1_1_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewDark--1_1_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:1b3670c1508e8548a32e51c8d1bd75d2dabce73a2bc710dc673a85f98901b5fb
-size 39100
+oid sha256:1b87a3743ebe1fc4c5df49180b94652543c58d250184787ae3f32c0c713ffad8
+size 40858
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewLight--0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewLight--0_0_null_0,NEXUS_5,1.0,en].png
index b38a248d82..e57be18284 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewLight--0_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewLight--0_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ec1f4a968f58c9c7faf6d51ec5dc8e31f00d867eb506b35a466e6a2242945c74
-size 42131
+oid sha256:d4dd79afac67e5d7ada5b32dc2d9d21d7b56ae53a19d34775058df77d4abce30
+size 44169
diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewLight--0_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewLight--0_0_null_1,NEXUS_5,1.0,en].png
index fffb00c7f3..632a80ee03 100644
--- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewLight--0_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_null_PreferencesRootViewLight--0_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:4b0006728215e73456e78192ce7d774d8bc792d04fef9fcfa3b76c081dfc8deb
-size 41996
+oid sha256:53b465eddb29226425e68f50de870d85477fb847fa5bb83b6294da11f682e2d0
+size 44036