Browse Source

Merge branch 'develop' into feature/fga/timeline_thread_decoration

pull/1298/head
ganfra 1 year ago committed by GitHub
parent
commit
cc33e39dc6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .maestro/tests/account/changeServer.yaml
  2. 2
      .maestro/tests/assertions/assertAnalyticsDisplayed.yaml
  3. 2
      .maestro/tests/assertions/assertHomeDisplayed.yaml
  4. 2
      .maestro/tests/assertions/assertInitDisplayed.yaml
  5. 2
      .maestro/tests/assertions/assertLoginDisplayed.yaml
  6. 2
      .maestro/tests/assertions/assertRoomListSynced.yaml
  7. 2
      .maestro/tests/assertions/assertWelcomeScreenDisplayed.yaml
  8. 3
      .maestro/tests/roomList/timeline/messages/text.yaml
  9. 3
      appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt
  10. 3
      appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt
  11. 1
      changelog.d/1232.bugfix
  12. 1
      changelog.d/1297.bugfix
  13. 1
      changelog.d/510.misc
  14. 6
      features/analytics/impl/src/main/res/values-de/translations.xml
  15. 8
      features/analytics/impl/src/main/res/values-fr/translations.xml
  16. 5
      features/analytics/impl/src/main/res/values-zh-rTW/translations.xml
  17. 3
      features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInPresenterTest.kt
  18. 3
      features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/preferences/AnalyticsPreferencesPresenterTest.kt
  19. 11
      features/createroom/impl/src/main/res/values-de/translations.xml
  20. 18
      features/createroom/impl/src/main/res/values-fr/translations.xml
  21. 3
      features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt
  22. 3
      features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt
  23. 3
      features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt
  24. 3
      features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/userlist/DefaultUserListPresenterTests.kt
  25. 10
      features/ftue/impl/src/main/res/values-de/translations.xml
  26. 15
      features/ftue/impl/src/main/res/values-fr/translations.xml
  27. 2
      features/ftue/impl/src/main/res/values-sk/translations.xml
  28. 3
      features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenPresenterTest.kt
  29. 3
      features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenterTests.kt
  30. 9
      features/invitelist/impl/src/main/res/values-de/translations.xml
  31. 4
      features/invitelist/impl/src/main/res/values-fr/translations.xml
  32. 3
      features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt
  33. 3
      features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenterImplTest.kt
  34. 3
      features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt
  35. 3
      features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt
  36. 39
      features/login/impl/src/main/res/values-de/translations.xml
  37. 49
      features/login/impl/src/main/res/values-fr/translations.xml
  38. 1
      features/login/impl/src/main/res/values-sk/translations.xml
  39. 1
      features/login/impl/src/main/res/values-zh-rTW/translations.xml
  40. 3
      features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt
  41. 3
      features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/webview/OidcPresenterTest.kt
  42. 3
      features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt
  43. 3
      features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt
  44. 3
      features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt
  45. 3
      features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt
  46. 3
      features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListPresenterTest.kt
  47. 8
      features/logout/api/src/main/res/values-de/translations.xml
  48. 2
      features/logout/api/src/main/res/values-fr/translations.xml
  49. 3
      features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPreferencePresenterTest.kt
  50. 7
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt
  51. 38
      features/messages/impl/src/main/res/values-de/translations.xml
  52. 46
      features/messages/impl/src/main/res/values-fr/translations.xml
  53. 1
      features/messages/impl/src/main/res/values-sk/translations.xml
  54. 3
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt
  55. 3
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt
  56. 3
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt
  57. 3
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/forward/ForwardMessagesPresenterTests.kt
  58. 3
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt
  59. 3
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/report/ReportMessagePresenterTests.kt
  60. 38
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt
  61. 3
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt
  62. 3
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/customreaction/CustomReactionPresenterTests.kt
  63. 3
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/reactionsummary/ReactionSummaryPresenterTests.kt
  64. 3
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/retrysendmenu/RetrySendMenuPresenterTests.kt
  65. 4
      features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt
  66. 10
      features/onboarding/impl/src/main/res/values-de/translations.xml
  67. 10
      features/onboarding/impl/src/main/res/values-fr/translations.xml
  68. 2
      features/onboarding/impl/src/main/res/values-zh-rTW/translations.xml
  69. 3
      features/onboarding/impl/src/test/kotlin/io/element/android/features/onboarding/impl/OnBoardingPresenterTest.kt
  70. 2
      features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerView.kt
  71. 31
      features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/CreatePollView.kt
  72. 11
      features/poll/impl/src/main/res/values-de/translations.xml
  73. 12
      features/poll/impl/src/main/res/values-fr/translations.xml
  74. 12
      features/poll/impl/src/main/res/values-sk/translations.xml
  75. 2
      features/poll/impl/src/main/res/values/localazy.xml
  76. 3
      features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenterTest.kt
  77. 2
      features/preferences/impl/build.gradle.kts
  78. 24
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt
  79. 27
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsEvents.kt
  80. 57
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsNode.kt
  81. 168
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt
  82. 50
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsState.kt
  83. 41
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsStateProvider.kt
  84. 263
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt
  85. 39
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/SystemNotificationsEnabledProvider.kt
  86. 98
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/DefaultNotificationSettingOption.kt
  87. 54
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingNode.kt
  88. 92
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt
  89. 25
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingState.kt
  90. 23
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingStateEvents.kt
  91. 75
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt
  92. 6
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt
  93. 9
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt
  94. 1
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt
  95. 1
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt
  96. 10
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt
  97. 2
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/tasks/ComputeCacheSizeUseCase.kt
  98. 3
      features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/about/AboutPresenterTest.kt
  99. 3
      features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsPresenterTest.kt
  100. 3
      features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt
  101. Some files were not shown because too many files have changed in this diff Show More

2
.maestro/tests/account/changeServer.yaml

@ -15,7 +15,7 @@ appId: ${APP_ID} @@ -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

2
.maestro/tests/assertions/assertAnalyticsDisplayed.yaml

@ -2,4 +2,4 @@ appId: ${APP_ID} @@ -2,4 +2,4 @@ appId: ${APP_ID}
---
- extendedWaitUntil:
visible: "Help improve Element X dbg"
timeout: 10_000
timeout: 10000

2
.maestro/tests/assertions/assertHomeDisplayed.yaml

@ -2,4 +2,4 @@ appId: ${APP_ID} @@ -2,4 +2,4 @@ appId: ${APP_ID}
---
- extendedWaitUntil:
visible: "All Chats"
timeout: 10_000
timeout: 10000

2
.maestro/tests/assertions/assertInitDisplayed.yaml

@ -2,4 +2,4 @@ appId: ${APP_ID} @@ -2,4 +2,4 @@ appId: ${APP_ID}
---
- extendedWaitUntil:
visible: "Be in your element"
timeout: 10_000
timeout: 10000

2
.maestro/tests/assertions/assertLoginDisplayed.yaml

@ -2,4 +2,4 @@ appId: ${APP_ID} @@ -2,4 +2,4 @@ appId: ${APP_ID}
---
- extendedWaitUntil:
visible: "Change account provider"
timeout: 10_000
timeout: 10000

2
.maestro/tests/assertions/assertRoomListSynced.yaml

@ -2,4 +2,4 @@ appId: ${APP_ID} @@ -2,4 +2,4 @@ appId: ${APP_ID}
---
- extendedWaitUntil:
visible: ${ROOM_NAME}
timeout: 10_000
timeout: 10000

2
.maestro/tests/assertions/assertWelcomeScreenDisplayed.yaml

@ -3,4 +3,4 @@ appId: ${APP_ID} @@ -3,4 +3,4 @@ appId: ${APP_ID}
- extendedWaitUntil:
visible:
id: "welcome_screen-title"
timeout: 10_000
timeout: 10000

3
.maestro/tests/roomList/timeline/messages/text.yaml

@ -1,7 +1,8 @@ @@ -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

3
appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt

@ -37,8 +37,7 @@ import org.junit.Rule @@ -37,8 +37,7 @@ import org.junit.Rule
import org.junit.Test
class RootPresenterTest {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
@Test

3
appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt

@ -37,8 +37,7 @@ import org.junit.Test @@ -37,8 +37,7 @@ import org.junit.Test
class LoggedInPresenterTest {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
@Test

1
changelog.d/1232.bugfix

@ -0,0 +1 @@ @@ -0,0 +1 @@
Fix long click on simple formatted messages

1
changelog.d/1297.bugfix

@ -0,0 +1 @@ @@ -0,0 +1 @@
Fix top padding in room list when app is opened in offline mode.

1
changelog.d/510.misc

@ -0,0 +1 @@ @@ -0,0 +1 @@
Add a sub-screen "Notifications" in the existing application Settings

6
features/analytics/impl/src/main/res/values-de/translations.xml

@ -1,10 +1,4 @@ @@ -1,10 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_analytics_prompt_data_usage">"Wir werden keine personenbezogenen Daten aufzeichnen oder auswerten"</string>
<string name="screen_analytics_prompt_help_us_improve">"Teile anonyme Nutzungsdaten, um uns bei der Identifizierung von Problemen zu helfen."</string>
<string name="screen_analytics_prompt_read_terms">"Du kannst alle unsere Nutzerbedingungen %1$s lesen."</string>
<string name="screen_analytics_prompt_read_terms_content_link">"hier"</string>
<string name="screen_analytics_prompt_settings">"Du kannst dies jederzeit deaktivieren"</string>
<string name="screen_analytics_prompt_third_party_sharing">"Wir geben deine Daten nicht an Dritte weiter"</string>
<string name="screen_analytics_prompt_title">"Hilf uns, %1$s zu verbessern"</string>
</resources>

8
features/analytics/impl/src/main/res/values-fr/translations.xml

@ -1,10 +1,10 @@ @@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_analytics_prompt_data_usage">"Nous n\'enregistrerons ni ne traiterons aucune donnée personnelle"</string>
<string name="screen_analytics_prompt_data_usage">"Nous n\'enregistrerons ni ne profilerons aucune donnée personnelle"</string>
<string name="screen_analytics_prompt_help_us_improve">"Partagez des données d\'utilisation anonymes pour nous aider à identifier les problèmes."</string>
<string name="screen_analytics_prompt_read_terms">"Consultez nos conditions d\'utilisation %1$s."</string>
<string name="screen_analytics_prompt_read_terms">"Vous pouvez lire toutes nos conditions %1$s."</string>
<string name="screen_analytics_prompt_read_terms_content_link">"ici"</string>
<string name="screen_analytics_prompt_settings">"Vous pouvez désactiver cette fonction à tout moment"</string>
<string name="screen_analytics_prompt_settings">"Vous pouvez le désactiver à tout moment"</string>
<string name="screen_analytics_prompt_third_party_sharing">"Nous ne partagerons pas vos données avec des tiers"</string>
<string name="screen_analytics_prompt_title">"Aidez-nous à améliorer %1$s"</string>
<string name="screen_analytics_prompt_title">"Aidez à améliorer %1$s"</string>
</resources>

5
features/analytics/impl/src/main/res/values-zh-rTW/translations.xml

@ -1,5 +1,10 @@ @@ -1,5 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_analytics_prompt_data_usage">"我們不會紀錄或剖繪您的個人資料"</string>
<string name="screen_analytics_prompt_help_us_improve">"分享匿名的使用數據以協助我們釐清問題"</string>
<string name="screen_analytics_prompt_read_terms">"您可以到 %1$s 閱讀我們的條款。"</string>
<string name="screen_analytics_prompt_read_terms_content_link">"這裡"</string>
<string name="screen_analytics_prompt_settings">"您可以在任何時候關閉它"</string>
<string name="screen_analytics_prompt_third_party_sharing">"我們不會和第三方分享您的資料"</string>
<string name="screen_analytics_prompt_title">"讓 %1$s 變得更好"</string>
</resources>

3
features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInPresenterTest.kt

@ -31,8 +31,7 @@ import org.junit.Test @@ -31,8 +31,7 @@ import org.junit.Test
class AnalyticsOptInPresenterTest {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
@Test

3
features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/preferences/AnalyticsPreferencesPresenterTest.kt

@ -30,8 +30,7 @@ import org.junit.Test @@ -30,8 +30,7 @@ import org.junit.Test
class AnalyticsPreferencesPresenterTest {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
@Test

11
features/createroom/impl/src/main/res/values-de/translations.xml

@ -1,15 +1,4 @@ @@ -1,15 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_create_room_action_create_room">"Neuer Raum"</string>
<string name="screen_create_room_action_invite_people">"Freunde zu Element einladen"</string>
<string name="screen_create_room_add_people_title">"Personen hinzufügen"</string>
<string name="screen_create_room_error_creating_room">"Beim Erstellen des Raums ist ein Fehler aufgetreten"</string>
<string name="screen_create_room_private_option_description">"Die Nachrichten in diesem Raum sind verschlüsselt. Die Verschlüsselung kann nicht nachträglich deaktiviert werden."</string>
<string name="screen_create_room_private_option_title">"Privater Raum (nur auf Einladung)"</string>
<string name="screen_create_room_public_option_description">"Nachrichten sind nicht verschlüsselt und jeder kann sie lesen. Du kannst die Verschlüsselung zu einem späteren Zeitpunkt aktivieren."</string>
<string name="screen_create_room_public_option_title">"Öffentlicher Raum (jeder)"</string>
<string name="screen_create_room_room_name_label">"Raumname"</string>
<string name="screen_create_room_topic_label">"Thema (optional)"</string>
<string name="screen_start_chat_error_starting_chat">"Beim Versuch, einen Chat zu starten, ist ein Fehler aufgetreten"</string>
<string name="screen_create_room_title">"Raum erstellen"</string>
</resources>

18
features/createroom/impl/src/main/res/values-fr/translations.xml

@ -1,15 +1,15 @@ @@ -1,15 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_create_room_action_create_room">"Nouveau salon"</string>
<string name="screen_create_room_action_create_room">"Nouvelle salle"</string>
<string name="screen_create_room_action_invite_people">"Inviter des amis sur Element"</string>
<string name="screen_create_room_add_people_title">"Inviter des personnes"</string>
<string name="screen_create_room_error_creating_room">"Une erreur s\'est produite lors de la création du salon"</string>
<string name="screen_create_room_private_option_description">"Les messages dans ce salon sont chiffrés. Une fois activé, le chiffrement ne peut pas être désactivé."</string>
<string name="screen_create_room_private_option_title">"Salon privé (sur invitation uniquement)"</string>
<string name="screen_create_room_public_option_description">"Les messages ne sont pas chiffrés et n\'importe qui peut les lire. Vous pouvez activer le chiffrement ultérieurement."</string>
<string name="screen_create_room_public_option_title">"Salon public (n’importe qui)"</string>
<string name="screen_create_room_room_name_label">"Nom du salon"</string>
<string name="screen_create_room_topic_label">"Sujet (optionnel)"</string>
<string name="screen_create_room_error_creating_room">"Une erreur s\'est produite lors de la création de la salle"</string>
<string name="screen_create_room_private_option_description">"Les messages dans cette pièce sont cryptés. Le cryptage ne peut pas être désactivé par la suite."</string>
<string name="screen_create_room_private_option_title">"Salle privée (sur invitation seulement)"</string>
<string name="screen_create_room_public_option_description">"Les messages ne sont pas cryptés et n\'importe qui peut les lire. Vous pouvez activer le chiffrement ultérieurement."</string>
<string name="screen_create_room_public_option_title">"Salle publique (tout le monde)"</string>
<string name="screen_create_room_room_name_label">"Nom de la salle"</string>
<string name="screen_create_room_topic_label">"Sujet (facultatif)"</string>
<string name="screen_start_chat_error_starting_chat">"Une erreur s\'est produite lors de la tentative de démarrage d\'une discussion"</string>
<string name="screen_create_room_title">"Créer un salon"</string>
<string name="screen_create_room_title">"Créer une salle"</string>
</resources>

3
features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt

@ -32,8 +32,7 @@ import org.junit.Test @@ -32,8 +32,7 @@ import org.junit.Test
class AddPeoplePresenterTests {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
private lateinit var presenter: AddPeoplePresenter

3
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" @@ -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

3
features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt

@ -44,8 +44,7 @@ import org.junit.Test @@ -44,8 +44,7 @@ import org.junit.Test
class CreateRoomRootPresenterTests {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
private lateinit var userRepository: FakeUserRepository

3
features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/userlist/DefaultUserListPresenterTests.kt

@ -33,8 +33,7 @@ import org.junit.Test @@ -33,8 +33,7 @@ import org.junit.Test
class DefaultUserListPresenterTests {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
private val userRepository = FakeUserRepository()

10
features/ftue/impl/src/main/res/values-de/translations.xml

@ -1,11 +1,5 @@ @@ -1,11 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_migration_message">"Dies ist ein einmaliger Vorgang, danke fürs Warten."</string>
<string name="screen_migration_title">"Dein Konto einrichten"</string>
<string name="screen_welcome_bullet_1">"Anrufe, Umfragen, Suche und mehr werden später in diesem Jahr hinzugefügt."</string>
<string name="screen_welcome_bullet_2">"Der Nachrichtenverlauf für verschlüsselte Räume wird in diesem Update nicht verfügbar sein."</string>
<string name="screen_welcome_bullet_3">"Wir würden uns freuen, wenn du uns über die Einstellungsseite deine Meinung mitteilst."</string>
<string name="screen_welcome_button">"Los geht\'s!"</string>
<string name="screen_welcome_subtitle">"Folgendes musst du wissen:"</string>
<string name="screen_welcome_title">"Willkommen bei %1$s!"</string>
<string name="screen_notification_optin_subtitle">"Du kannst deine Einstellungen später ändern."</string>
<string name="screen_notification_optin_title">"Erlaube Benachrichtigungen und verpasse keine Nachricht"</string>
</resources>

15
features/ftue/impl/src/main/res/values-fr/translations.xml

@ -1,10 +1,13 @@ @@ -1,10 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_migration_message">"Ce processus n’a besoin d’être fait qu’une seule fois, merci de patienter."</string>
<string name="screen_migration_message">"Il s\'agit d\'un processus unique, merci d\'avoir attendu."</string>
<string name="screen_migration_title">"Configuration de votre compte."</string>
<string name="screen_welcome_bullet_2">"L’historique des messages pour les salons chiffrés ne sera pas disponible dans cette mise à jour."</string>
<string name="screen_welcome_bullet_3">"Nous serions ravis d’avoir votre avis, n’hésitez pas à nous le partager via la page des paramètres."</string>
<string name="screen_welcome_button">"C’est parti !"</string>
<string name="screen_welcome_subtitle">"Voici ce qu’il faut savoir :"</string>
<string name="screen_welcome_title">"Bienvenue sur %1$s !"</string>
<string name="screen_notification_optin_subtitle">"Vous pourrez modifier vos paramètres ultérieurement."</string>
<string name="screen_notification_optin_title">"Autorisez les notifications et ne manquez aucun message"</string>
<string name="screen_welcome_bullet_1">"Les appels, les sondages, les recherches et plus encore seront ajoutés plus tard cette année."</string>
<string name="screen_welcome_bullet_2">"L\'historique des messages pour les salles cryptées ne sera pas disponible dans cette mise à jour."</string>
<string name="screen_welcome_bullet_3">"N\'hésitez pas à nous faire part de vos commentaires via la page des paramètres."</string>
<string name="screen_welcome_button">"C\'est parti !"</string>
<string name="screen_welcome_subtitle">"Voici ce que vous devez savoir :"</string>
<string name="screen_welcome_title">"Bienvenue à %1$s !"</string>
</resources>

2
features/ftue/impl/src/main/res/values-sk/translations.xml

@ -2,6 +2,8 @@ @@ -2,6 +2,8 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_migration_message">"Ide o jednorazový proces, ďakujeme za trpezlivosť."</string>
<string name="screen_migration_title">"Nastavenie vášho účtu."</string>
<string name="screen_notification_optin_subtitle">"Svoje nastavenia môžete neskôr zmeniť."</string>
<string name="screen_notification_optin_title">"Povoľte oznámenia a nikdy nezmeškajte žiadnu správu"</string>
<string name="screen_welcome_bullet_1">"Hovory, ankety, vyhľadávanie a ďalšie funkcie pribudnú neskôr v tomto roku."</string>
<string name="screen_welcome_bullet_2">"História správ pre zašifrované miestnosti nebude v tejto aktualizácii k dispozícii."</string>
<string name="screen_welcome_bullet_3">"Radi by sme od vás počuli, dajte nám vedieť, čo si myslíte, prostredníctvom stránky nastavení."</string>

3
features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/migration/MigrationScreenPresenterTest.kt

@ -32,8 +32,7 @@ import org.junit.Test @@ -32,8 +32,7 @@ import org.junit.Test
class MigrationScreenPresenterTest {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
@Test

3
features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/notifications/NotificationsOptInPresenterTests.kt

@ -39,8 +39,7 @@ import org.junit.Test @@ -39,8 +39,7 @@ import org.junit.Test
class NotificationsOptInPresenterTests {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
private var isFinished = false

9
features/invitelist/impl/src/main/res/values-de/translations.xml

@ -1,9 +0,0 @@ @@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_invites_decline_chat_message">"Möchtest du den Beitritt zu %1$s wirklich ablehnen?"</string>
<string name="screen_invites_decline_chat_title">"Einladung ablehnen"</string>
<string name="screen_invites_decline_direct_chat_message">"Möchtest du den privaten Chat mit %1$s wirklich ablehnen?"</string>
<string name="screen_invites_decline_direct_chat_title">"Chat ablehnen"</string>
<string name="screen_invites_empty_list">"Keine Einladungen"</string>
<string name="screen_invites_invited_you">"%1$s (%2$s) hat dich eingeladen"</string>
</resources>

4
features/invitelist/impl/src/main/res/values-fr/translations.xml

@ -1,8 +1,8 @@ @@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_invites_decline_chat_message">"Voulez-vous vraiment refuser l‘invitation à rejoindre %1$s ?"</string>
<string name="screen_invites_decline_chat_message">"Êtes-vous sûr de vouloir décliner l\'invitation à participer %1$s ?"</string>
<string name="screen_invites_decline_chat_title">"Refuser l\'invitation"</string>
<string name="screen_invites_decline_direct_chat_message">"Voulez-vous vraiment refuser ce chat privé avec %1$s ?"</string>
<string name="screen_invites_decline_direct_chat_message">"Êtes-vous sûr de vouloir refuser cette discussion privée avec %1$s ?"</string>
<string name="screen_invites_decline_direct_chat_title">"Refuser le chat"</string>
<string name="screen_invites_empty_list">"Aucune invitation"</string>
<string name="screen_invites_invited_you">"%1$s (%2$s) vous a invité"</string>

3
features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt

@ -50,8 +50,7 @@ import org.junit.Rule @@ -50,8 +50,7 @@ import org.junit.Rule
import org.junit.Test
class InviteListPresenterTests {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
@Test

3
features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenterImplTest.kt

@ -39,8 +39,7 @@ import org.junit.Test @@ -39,8 +39,7 @@ import org.junit.Test
class LeaveRoomPresenterImplTest {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
@Test

3
features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt

@ -42,8 +42,7 @@ import org.junit.Test @@ -42,8 +42,7 @@ import org.junit.Test
class SendLocationPresenterTest {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
private val permissionsPresenterFake = PermissionsPresenterFake()

3
features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt

@ -35,8 +35,7 @@ import org.junit.Test @@ -35,8 +35,7 @@ import org.junit.Test
class ShowLocationPresenterTest {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
private val permissionsPresenterFake = PermissionsPresenterFake()

39
features/login/impl/src/main/res/values-de/translations.xml

@ -1,47 +1,8 @@ @@ -1,47 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_account_provider_change">"Kontoanbieter wechseln"</string>
<string name="screen_account_provider_form_hint">"Adresse des Homeservers"</string>
<string name="screen_account_provider_form_notice">"Gib einen Suchbegriff oder eine Domainadresse ein."</string>
<string name="screen_account_provider_form_subtitle">"Suche nach einem Unternehmen, einer Community oder einem privaten Server."</string>
<string name="screen_account_provider_form_title">"Finde einen Kontoanbieter"</string>
<string name="screen_account_provider_signin_subtitle">"Hier werden deine Konversationen stattfinden — genauso wie du einen E-Mail-Anbieter verwenden würdest, um deine E-Mails aufzubewahren."</string>
<string name="screen_account_provider_signin_title">"Du bist dabei dich bei %s anzumelden"</string>
<string name="screen_account_provider_signup_subtitle">"Hier werden deine Konversationen stattfinden — genauso wie du einen E-Mail-Anbieter verwenden würdest, um deine E-Mails aufzubewahren."</string>
<string name="screen_account_provider_signup_title">"Du bist dabei ein Konto auf %s zu erstellen"</string>
<string name="screen_change_account_provider_matrix_org_subtitle">"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."</string>
<string name="screen_change_account_provider_other">"Andere"</string>
<string name="screen_change_account_provider_subtitle">"Verwende einen anderen Kontoanbieter, z. B. deinen eigenen privaten Server oder ein Arbeitskonto."</string>
<string name="screen_change_account_provider_title">"Kontoanbieter ändern"</string>
<string name="screen_change_server_error_invalid_homeserver">"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."</string>
<string name="screen_change_server_error_no_sliding_sync_message">"Dieser Server unterstützt derzeit keine Sliding Sync."</string>
<string name="screen_change_server_form_header">"Homeserver-URL"</string>
<string name="screen_change_server_form_notice">"Du kannst dich nur mit einem existierenden Server verbinden, der Sliding Sync unterstützt. Dein Homeserver-Administrator muss es konfigurieren. %1$s"</string>
<string name="screen_change_server_subtitle">"Wie lautet die Adresse deines Servers?"</string>
<string name="screen_login_error_deactivated_account">"Dieses Konto wurde deaktiviert."</string>
<string name="screen_login_error_invalid_credentials">"Falscher Benutzername und/oder Passwort"</string>
<string name="screen_login_error_invalid_user_id">"Dies ist kein gültiger Benutzeridentifikator. Erwartetes Format: \'@user:homeserver.org\'"</string>
<string name="screen_login_error_unsupported_authentication">"Der ausgewählte Homeserver unterstützt kein Passwort- oder OIDC-Login. Bitte kontaktiere deinen Admin oder wähle einen anderen Homeserver."</string>
<string name="screen_login_form_header">"Gib deine Daten ein"</string>
<string name="screen_login_title">"Willkommen zurück!"</string>
<string name="screen_login_title_with_homeserver">"Bei %1$s anmelden"</string>
<string name="screen_server_confirmation_change_server">"Kontoanbieter wechseln"</string>
<string name="screen_server_confirmation_message_login_element_dot_io">"Ein privater Server für Element-Mitarbeiter."</string>
<string name="screen_server_confirmation_message_login_matrix_dot_org">"Matrix ist ein offenes Netzwerk für sichere, dezentrale Kommunikation"</string>
<string name="screen_server_confirmation_message_register">"Hier werden deine Konversationen stattfinden — genau so wie du einen E-Mail-Anbieter verwenden würdest, um deine E-Mails aufzubewahren."</string>
<string name="screen_server_confirmation_title_login">"Du bist dabei dich bei %1$s anzumelden"</string>
<string name="screen_server_confirmation_title_register">"Du bist dabei ein Konto auf %1$s zu erstellen"</string>
<string name="screen_waitlist_message">"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!"</string>
<string name="screen_waitlist_message_success">"Willkommen bei %1$s!"</string>
<string name="screen_waitlist_title">"Du hast es fast geschafft!"</string>
<string name="screen_waitlist_title_success">"Du bist dabei."</string>
<string name="screen_account_provider_continue">"Weiter"</string>
<string name="screen_change_server_submit">"Weiter"</string>
<string name="screen_change_server_title">"Wählen deinen Server"</string>
<string name="screen_login_password_hint">"Passwort"</string>
<string name="screen_login_submit">"Weiter"</string>
<string name="screen_login_subtitle">"Matrix ist ein offenes Netzwerk für sichere, dezentrale Kommunikation"</string>
<string name="screen_login_username_hint">"Benutzername"</string>
</resources>

49
features/login/impl/src/main/res/values-fr/translations.xml

@ -1,39 +1,40 @@ @@ -1,39 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_account_provider_change">"Changer de fournisseur"</string>
<string name="screen_account_provider_change">"Changer de fournisseur de compte"</string>
<string name="screen_account_provider_form_hint">"Adresse du serveur d\'accueil"</string>
<string name="screen_account_provider_form_notice">"Entrez un mot clé de recherche ou un nom de domaine."</string>
<string name="screen_account_provider_form_subtitle">"Rechercher une entreprise, une communauté ou un serveur privé."</string>
<string name="screen_account_provider_form_title">"Trouver un fournisseur de services"</string>
<string name="screen_account_provider_signin_subtitle">"C\'est ici que vos conversations seront stockées - tout comme vous utiliseriez un fournisseur de messagerie pour conserver vos e-mails."</string>
<string name="screen_account_provider_form_notice">"Entrez un terme de recherche ou une adresse de domaine."</string>
<string name="screen_account_provider_form_subtitle">"Recherchez une entreprise, une communauté ou un serveur privé."</string>
<string name="screen_account_provider_form_title">"Trouver un fournisseur de comptes"</string>
<string name="screen_account_provider_signin_subtitle">"C\'est ici que vos conversations seront enregistrées, comme vous le feriez avec un fournisseur de messagerie pour conserver vos e-mails."</string>
<string name="screen_account_provider_signin_title">"Vous êtes sur le point de vous connecter à %s"</string>
<string name="screen_account_provider_signup_subtitle">"C\'est ici que vos conversations seront stockées - tout comme vous utiliseriez un fournisseur de messagerie pour conserver vos e-mails."</string>
<string name="screen_account_provider_signup_subtitle">"C\'est ici que vos conversations seront enregistrées, comme vous le feriez avec un fournisseur de messagerie pour conserver vos e-mails."</string>
<string name="screen_account_provider_signup_title">"Vous êtes sur le point de créer un compte sur %s"</string>
<string name="screen_change_account_provider_other">"Autre"</string>
<string name="screen_change_account_provider_subtitle">"Utilisez un autre fournisseur de compte, tel que votre propre serveur ou un compte professionnel."</string>
<string name="screen_change_account_provider_title">"Changer de fournisseur"</string>
<string name="screen_change_server_error_invalid_homeserver">"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."</string>
<string name="screen_change_account_provider_matrix_org_subtitle">"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."</string>
<string name="screen_change_account_provider_other">"Autres"</string>
<string name="screen_change_account_provider_subtitle">"Utilisez un autre fournisseur de compte, tel que votre propre serveur privé ou un compte professionnel."</string>
<string name="screen_change_account_provider_title">"Changer de fournisseur de compte"</string>
<string name="screen_change_server_error_invalid_homeserver">"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."</string>
<string name="screen_change_server_error_no_sliding_sync_message">"Ce serveur ne prend actuellement pas en charge la synchronisation glissante."</string>
<string name="screen_change_server_form_header">"URL du serveur d\'accueil"</string>
<string name="screen_change_server_form_notice">"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"</string>
<string name="screen_change_server_form_notice">"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"</string>
<string name="screen_change_server_subtitle">"Quelle est l\'adresse de votre serveur ?"</string>
<string name="screen_login_error_deactivated_account">"Ce compte a été désactivé."</string>
<string name="screen_login_error_invalid_credentials">"Nom d\'utilisateur et/ou mot de passe incorrect"</string>
<string name="screen_login_error_invalid_credentials">"Nom d\'utilisateur et/ou mot de passe incorrects"</string>
<string name="screen_login_error_invalid_user_id">"Il ne s\'agit pas d\'un identifiant utilisateur valide. Format attendu : « @user:homeserver.org »"</string>
<string name="screen_login_error_unsupported_authentication">"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."</string>
<string name="screen_login_error_unsupported_authentication">"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."</string>
<string name="screen_login_form_header">"Saisir vos informations personnelles"</string>
<string name="screen_login_title">"Heureux de vous revoir!"</string>
<string name="screen_login_title_with_homeserver">"Se connecter à %1$s"</string>
<string name="screen_login_title">"Bienvenue !"</string>
<string name="screen_login_title_with_homeserver">"Connectez-vous à %1$s"</string>
<string name="screen_server_confirmation_change_server">"Changer de fournisseur de compte"</string>
<string name="screen_server_confirmation_message_login_element_dot_io">"Un serveur privé pour les employés dElement."</string>
<string name="screen_server_confirmation_message_login_matrix_dot_org">"Matrix est un réseau ouvert de communication sécurisée et décentralisée."</string>
<string name="screen_server_confirmation_message_register">"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."</string>
<string name="screen_server_confirmation_title_login">"Vous allez vous connecter à %1$s"</string>
<string name="screen_server_confirmation_title_register">"Vous allez créer un compte sur %1$s"</string>
<string name="screen_waitlist_message">"Il y a une forte demande pour %1$s sur %2$s en ce moment. Rouvrez l’app dans quelques jours et réessayez.
<string name="screen_server_confirmation_message_login_element_dot_io">"Un serveur privé pour les employés d\'Element."</string>
<string name="screen_server_confirmation_message_login_matrix_dot_org">"Matrix est un réseau ouvert pour une communication sécurisée et décentralisée."</string>
<string name="screen_server_confirmation_message_register">"C\'est ici que vos conversations seront enregistrées, comme vous le feriez avec un fournisseur de messagerie pour conserver vos e-mails."</string>
<string name="screen_server_confirmation_title_login">"Vous êtes sur le point de vous connecter à %1$s"</string>
<string name="screen_server_confirmation_title_register">"Vous êtes sur le point de créer un compte sur %1$s"</string>
<string name="screen_waitlist_message">"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 !"</string>
<string name="screen_waitlist_message_success">"Bienvenue sur %1$s !"</string>
Merci pour votre patience !"</string>
<string name="screen_waitlist_message_success">"Bienvenue à %1$s !"</string>
<string name="screen_waitlist_title">"Vous y êtes presque."</string>
<string name="screen_waitlist_title_success">"Vous y êtes."</string>
<string name="screen_account_provider_continue">"Continuer"</string>
@ -41,6 +42,6 @@ Merci de votre patience !"</string> @@ -41,6 +42,6 @@ Merci de votre patience !"</string>
<string name="screen_change_server_title">"Sélectionnez votre serveur"</string>
<string name="screen_login_password_hint">"Mot de passe"</string>
<string name="screen_login_submit">"Continuer"</string>
<string name="screen_login_subtitle">"Matrix est un réseau ouvert de communication sécurisée et décentralisée."</string>
<string name="screen_login_subtitle">"Matrix est un réseau ouvert pour une communication sécurisée et décentralisée."</string>
<string name="screen_login_username_hint">"Nom d\'utilisateur"</string>
</resources>

1
features/login/impl/src/main/res/values-sk/translations.xml

@ -9,6 +9,7 @@ @@ -9,6 +9,7 @@
<string name="screen_account_provider_signin_title">"Chystáte sa prihlásiť do %s"</string>
<string name="screen_account_provider_signup_subtitle">"Tu budú žiť vaše konverzácie — podobne ako používate poskytovateľa e-mailových služieb na uchovávanie e-mailov."</string>
<string name="screen_account_provider_signup_title">"Chystáte sa vytvoriť účet na %s"</string>
<string name="screen_change_account_provider_matrix_org_subtitle">"Matrix.org je veľký bezplatný server vo verejnej sieti Matrix na bezpečnú, decentralizovanú komunikáciu, ktorý prevádzkuje nadácia Matrix.org."</string>
<string name="screen_change_account_provider_other">"Iný"</string>
<string name="screen_change_account_provider_subtitle">"Použite iného poskytovateľa účtu, ako napríklad vlastný súkromný server alebo pracovný účet."</string>
<string name="screen_change_account_provider_title">"Zmeniť poskytovateľa účtu"</string>

1
features/login/impl/src/main/res/values-zh-rTW/translations.xml

@ -5,6 +5,7 @@ @@ -5,6 +5,7 @@
<string name="screen_change_account_provider_other">"其他"</string>
<string name="screen_change_server_error_no_sliding_sync_message">"此伺服器目前不支援 sliding sync。"</string>
<string name="screen_change_server_form_header">"家伺服器 URL"</string>
<string name="screen_login_form_header">"輸入您的詳細資料"</string>
<string name="screen_login_title">"歡迎回來!"</string>
<string name="screen_login_title_with_homeserver">"登入 %1$s"</string>
<string name="screen_server_confirmation_title_login">"您即將登入 %1$s"</string>

3
features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt

@ -33,8 +33,7 @@ import org.junit.Test @@ -33,8 +33,7 @@ import org.junit.Test
class ChangeServerPresenterTest {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
@Test

3
features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/webview/OidcPresenterTest.kt

@ -35,8 +35,7 @@ import org.junit.Test @@ -35,8 +35,7 @@ import org.junit.Test
class OidcPresenterTest {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
@Test

3
features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt

@ -31,8 +31,7 @@ import org.junit.Test @@ -31,8 +31,7 @@ import org.junit.Test
class ChangeAccountProviderPresenterTest {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
@Test

3
features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt

@ -39,8 +39,7 @@ import org.junit.Test @@ -39,8 +39,7 @@ import org.junit.Test
class ConfirmAccountProviderPresenterTest {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
@Test

3
features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt

@ -38,8 +38,7 @@ import org.junit.Test @@ -38,8 +38,7 @@ import org.junit.Test
class LoginPasswordPresenterTest {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
@Test

3
features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt

@ -38,8 +38,7 @@ import org.junit.Test @@ -38,8 +38,7 @@ import org.junit.Test
class SearchAccountProviderPresenterTest {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
@Test

3
features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListPresenterTest.kt

@ -37,8 +37,7 @@ import org.junit.Test @@ -37,8 +37,7 @@ import org.junit.Test
class WaitListPresenterTest {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
@Test

8
features/logout/api/src/main/res/values-de/translations.xml

@ -1,8 +0,0 @@ @@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_signout_confirmation_dialog_content">"Möchtest du Dich wirklich abmelden?"</string>
<string name="screen_signout_confirmation_dialog_title">"Abmelden"</string>
<string name="screen_signout_in_progress_dialog_content">"Abmeldung läuft…"</string>
<string name="screen_signout_confirmation_dialog_submit">"Abmelden"</string>
<string name="screen_signout_preference_item">"Abmelden"</string>
</resources>

2
features/logout/api/src/main/res/values-fr/translations.xml

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_signout_confirmation_dialog_content">"Êtes-vous sûr de vouloir vous déconnecter ?"</string>
<string name="screen_signout_confirmation_dialog_title">"Se déconnecter"</string>
<string name="screen_signout_in_progress_dialog_content">"Déconnexion en cours…"</string>
<string name="screen_signout_in_progress_dialog_content">"Déconnexion…"</string>
<string name="screen_signout_confirmation_dialog_submit">"Se déconnecter"</string>
<string name="screen_signout_preference_item">"Se déconnecter"</string>
</resources>

3
features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPreferencePresenterTest.kt

@ -32,8 +32,7 @@ import org.junit.Test @@ -32,8 +32,7 @@ import org.junit.Test
class LogoutPreferencePresenterTest {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
@Test

7
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt

@ -126,7 +126,12 @@ private fun HtmlBody( @@ -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 -> {

38
features/messages/impl/src/main/res/values-de/translations.xml

@ -1,41 +1,5 @@ @@ -1,41 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<plurals name="room_timeline_state_changes">
<item quantity="one">"%1$d Raumänderung"</item>
<item quantity="other">"%1$d Raumänderungen"</item>
</plurals>
<string name="screen_room_attachment_source_camera">"Kamera"</string>
<string name="screen_room_attachment_source_camera_photo">"Foto aufnehmen"</string>
<string name="screen_room_attachment_source_camera_video">"Video aufnehmen"</string>
<string name="screen_room_attachment_source_files">"Anhang"</string>
<string name="screen_room_attachment_source_gallery">"Foto- &amp; Video-Bibliothek"</string>
<string name="screen_room_attachment_source_location">"Standort"</string>
<string name="screen_room_attachment_source_poll">"Umfrage"</string>
<string name="screen_room_encrypted_history_banner">"Der Nachrichtenverlauf ist in diesem Raum derzeit nicht verfügbar"</string>
<string name="screen_room_error_failed_retrieving_user_details">"Benutzerdetails konnten nicht abgerufen werden"</string>
<string name="screen_room_invite_again_alert_message">"Möchtest du sie wieder einladen?"</string>
<string name="screen_room_invite_again_alert_title">"Du bist allein in diesem Chat"</string>
<string name="screen_room_message_copied">"Nachricht kopiert"</string>
<string name="screen_room_no_permission_to_post">"Du bist keine Berechtigung, um in diesem Raum zu posten"</string>
<string name="screen_room_notification_settings_allow_custom">"Benutzerdefinierte Einstellung zulassen"</string>
<string name="screen_room_notification_settings_allow_custom_footnote">"Das Aktivieren dieser Option wird die Standardeinstellungen überschreiben."</string>
<string name="screen_room_notification_settings_custom_settings_title">"Benachrichtige mich in diesem Chat für"</string>
<string name="screen_room_notification_settings_default_setting_footnote">"Du kannst es in deinem %1$s ändern."</string>
<string name="screen_room_notification_settings_default_setting_footnote_content_link">"Globale Einstellungen"</string>
<string name="screen_room_notification_settings_default_setting_title">"Standardeinstellung"</string>
<string name="screen_room_notification_settings_edit_remove_setting">"Benutzerdefinierte Einstellung entfernen"</string>
<string name="screen_room_notification_settings_error_loading_settings">"Beim Laden der Benachrichtigungseinstellungen ist ein Fehler aufgetreten."</string>
<string name="screen_room_notification_settings_error_restoring_default">"Wiederherstellung des Standardmodus fehlgeschlagen. Bitte versuche es erneut."</string>
<string name="screen_room_notification_settings_error_setting_mode">"Fehler beim Einstellen des Modus. Bitte versuche es erneut."</string>
<string name="screen_room_notification_settings_mode_all_messages">"Alle Nachrichten"</string>
<string name="screen_room_notification_settings_mode_mentions_and_keywords">"Nur Erwähnungen und Schlüsselwörter"</string>
<string name="screen_room_notification_settings_room_custom_settings_title">"In diesem Raum, benachrichtige mich für"</string>
<string name="screen_room_reactions_show_less">"Weniger anzeigen"</string>
<string name="screen_room_reactions_show_more">"Mehr anzeigen"</string>
<string name="screen_room_retry_send_menu_send_again_action">"Erneut senden"</string>
<string name="screen_room_retry_send_menu_title">"Ihre Nachricht konnte nicht gesendet werden"</string>
<string name="screen_room_timeline_add_reaction">"Emoji hinzufügen"</string>
<string name="screen_room_timeline_less_reactions">"Weniger anzeigen"</string>
<string name="screen_room_error_failed_processing_media">"Fehler bei der Verarbeitung von Medien zum Hochladen, bitte versuche es erneut."</string>
<string name="screen_room_attachment_text_formatting">"Textformatierung"</string>
<string name="screen_room_retry_send_menu_remove_action">"Entfernen"</string>
</resources>

46
features/messages/impl/src/main/res/values-fr/translations.xml

@ -1,36 +1,42 @@ @@ -1,36 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<plurals name="room_timeline_state_changes">
<item quantity="one">"%1$d changement dans la conversation"</item>
<item quantity="other">"%1$d changements dans la conversation"</item>
<item quantity="one">"%1$d changement de salle"</item>
<item quantity="other">"%1$d changements de salle"</item>
</plurals>
<string name="screen_room_attachment_source_camera">"Appareil photo"</string>
<string name="screen_room_attachment_source_camera_photo">"Prendre une photo"</string>
<string name="screen_room_attachment_source_camera_video">"Enregistrer une vidéo"</string>
<string name="screen_room_attachment_source_files">"Pièce-jointe"</string>
<string name="screen_room_attachment_source_gallery">"Gallerie photo et vidéo"</string>
<string name="screen_room_encrypted_history_banner">"L’historique des messages n’est pas disponible actuellement dans ce salon"</string>
<string name="screen_room_error_failed_retrieving_user_details">"Impossible de récupérer les détails de l’utilisateur"</string>
<string name="screen_room_invite_again_alert_message">"Souhaitez-vous les inviter à revenir ?"</string>
<string name="screen_room_attachment_source_files">"Pièce jointe"</string>
<string name="screen_room_attachment_source_gallery">"Photothèque et vidéothèque"</string>
<string name="screen_room_attachment_source_location">"Emplacement"</string>
<string name="screen_room_attachment_source_poll">"Sondage"</string>
<string name="screen_room_attachment_text_formatting">"Formatage du texte"</string>
<string name="screen_room_encrypted_history_banner">"L\'historique des messages n\'est actuellement pas disponible dans cette salle"</string>
<string name="screen_room_error_failed_retrieving_user_details">"Impossible de récupérer les détails de l\'utilisateur"</string>
<string name="screen_room_invite_again_alert_message">"Aimeriez-vous les inviter à revenir ?"</string>
<string name="screen_room_invite_again_alert_title">"Vous êtes seul dans ce chat"</string>
<string name="screen_room_message_copied">"Message copié"</string>
<string name="screen_room_no_permission_to_post">"Vous n‘avez pas le droit de poster dans ce salon"</string>
<string name="screen_room_no_permission_to_post">"Vous n\'êtes pas autorisé à publier dans cette salle"</string>
<string name="screen_room_notification_settings_allow_custom">"Autoriser les paramètres personnalisés"</string>
<string name="screen_room_notification_settings_allow_custom_footnote">"Activer cette option remplacera votre paramètre par défaut"</string>
<string name="screen_room_notification_settings_custom_settings_title">"Me notifier dans ce chat pour"</string>
<string name="screen_room_notification_settings_default_setting_footnote_content_link">"paramètres généraux"</string>
<string name="screen_room_notification_settings_allow_custom_footnote">"L\'activation de cette option annulera votre paramètre par défaut"</string>
<string name="screen_room_notification_settings_custom_settings_title">"Prévenez-moi dans ce chat pour"</string>
<string name="screen_room_notification_settings_default_setting_footnote">"Vous pouvez le modifier dans votre %1$s."</string>
<string name="screen_room_notification_settings_default_setting_footnote_content_link">"paramètres globaux"</string>
<string name="screen_room_notification_settings_default_setting_title">"Paramètre par défaut"</string>
<string name="screen_room_notification_settings_error_loading_settings">"Une erreur s’est produite lors du chargement des paramètres de notification."</string>
<string name="screen_room_notification_settings_error_restoring_default">"Impossible de restaurer le mode par défaut, veuillez réessayer."</string>
<string name="screen_room_notification_settings_error_setting_mode">"Impossible de régler le mode, veuillez réessayer."</string>
<string name="screen_room_notification_settings_edit_remove_setting">"Supprimer le paramètre personnalisé"</string>
<string name="screen_room_notification_settings_error_loading_settings">"Une erreur s\'est produite lors du chargement des paramètres de notification."</string>
<string name="screen_room_notification_settings_error_restoring_default">"Échec de la restauration du mode par défaut, veuillez réessayer."</string>
<string name="screen_room_notification_settings_error_setting_mode">"Échec de la configuration du mode, veuillez réessayer."</string>
<string name="screen_room_notification_settings_mode_all_messages">"Tous les messages"</string>
<string name="screen_room_notification_settings_mode_mentions_and_keywords">"Mentions et mots-clés uniquement"</string>
<string name="screen_room_notification_settings_mode_mentions_and_keywords">"Mentions et mots clés uniquement"</string>
<string name="screen_room_notification_settings_room_custom_settings_title">"Dans cette salle, prévenez-moi pour"</string>
<string name="screen_room_reactions_show_less">"Afficher moins"</string>
<string name="screen_room_reactions_show_more">"Afficher plus"</string>
<string name="screen_room_retry_send_menu_send_again_action">"Renvoyer"</string>
<string name="screen_room_retry_send_menu_send_again_action">"Envoyer à nouveau"</string>
<string name="screen_room_retry_send_menu_title">"Votre message n\'a pas pu être envoyé"</string>
<string name="screen_room_timeline_add_reaction">"Ajouter un emoji"</string>
<string name="screen_room_timeline_less_reactions">"Montrer moins"</string>
<string name="screen_room_error_failed_processing_media">"Échec du traitement du média avant son envoi, veuillez réessayer."</string>
<string name="screen_room_retry_send_menu_remove_action">"Supprimer"</string>
<string name="screen_room_timeline_add_reaction">"Ajouter un émoji"</string>
<string name="screen_room_timeline_less_reactions">"Afficher moins"</string>
<string name="screen_room_error_failed_processing_media">"Échec du traitement des médias à télécharger, veuillez réessayer."</string>
<string name="screen_room_retry_send_menu_remove_action">"Enlever"</string>
</resources>

1
features/messages/impl/src/main/res/values-sk/translations.xml

@ -12,6 +12,7 @@ @@ -12,6 +12,7 @@
<string name="screen_room_attachment_source_gallery">"Knižnica fotografií a videí"</string>
<string name="screen_room_attachment_source_location">"Poloha"</string>
<string name="screen_room_attachment_source_poll">"Anketa"</string>
<string name="screen_room_attachment_text_formatting">"Formátovanie textu"</string>
<string name="screen_room_encrypted_history_banner">"História správ v tejto miestnosti nie je momentálne k dispozícii"</string>
<string name="screen_room_error_failed_retrieving_user_details">"Nepodarilo sa získať údaje o používateľovi"</string>
<string name="screen_room_invite_again_alert_message">"Chceli by ste ich pozvať späť?"</string>

3
features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt

@ -86,8 +86,7 @@ import kotlin.time.Duration.Companion.milliseconds @@ -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")

3
features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt

@ -41,8 +41,7 @@ import org.junit.Test @@ -41,8 +41,7 @@ import org.junit.Test
class ActionListPresenterTest {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
@Test

3
features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt

@ -43,8 +43,7 @@ import org.junit.Test @@ -43,8 +43,7 @@ import org.junit.Test
class AttachmentsPreviewPresenterTest {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
private val mediaPreProcessor = FakeMediaPreProcessor()

3
features/messages/impl/src/test/kotlin/io/element/android/features/messages/forward/ForwardMessagesPresenterTests.kt

@ -39,8 +39,7 @@ import org.junit.Test @@ -39,8 +39,7 @@ import org.junit.Test
class ForwardMessagesPresenterTests {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()

3
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() @@ -44,8 +44,7 @@ private val TESTED_MEDIA_INFO = aFileInfo()
class MediaViewerPresenterTest {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()

3
features/messages/impl/src/test/kotlin/io/element/android/features/messages/report/ReportMessagePresenterTests.kt

@ -35,8 +35,7 @@ import org.junit.Test @@ -35,8 +35,7 @@ import org.junit.Test
class ReportMessagePresenterTests {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
@Test

38
features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt

@ -24,6 +24,7 @@ import app.cash.molecule.moleculeFlow @@ -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 @@ -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 @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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,
)
)
}
}

3
features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt

@ -51,8 +51,7 @@ import java.util.Date @@ -51,8 +51,7 @@ import java.util.Date
class TimelinePresenterTest {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
@Test

3
features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/customreaction/CustomReactionPresenterTests.kt

@ -33,8 +33,7 @@ import org.junit.Test @@ -33,8 +33,7 @@ import org.junit.Test
class CustomReactionPresenterTests {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
private val presenter = CustomReactionPresenter(emojibaseProvider = FakeEmojibaseProvider())

3
features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/reactionsummary/ReactionSummaryPresenterTests.kt

@ -37,8 +37,7 @@ import org.junit.Test @@ -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)

3
features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/retrysendmenu/RetrySendMenuPresenterTests.kt

@ -32,8 +32,7 @@ import org.junit.Test @@ -32,8 +32,7 @@ import org.junit.Test
class RetrySendMenuPresenterTests {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
private val room = FakeMatrixRoom()

4
features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt

@ -109,7 +109,9 @@ fun ConnectivityIndicatorContainer( @@ -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(

10
features/onboarding/impl/src/main/res/values-de/translations.xml

@ -1,10 +0,0 @@ @@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_onboarding_sign_in_manually">"Manuell anmelden"</string>
<string name="screen_onboarding_sign_in_with_qr_code">"Mit QR-Code anmelden"</string>
<string name="screen_onboarding_sign_up">"Konto erstellen"</string>
<string name="screen_onboarding_subtitle">"Sicher kommunizieren und zusammenarbeiten"</string>
<string name="screen_onboarding_welcome_message">"Willkommen beim schnellsten Element aller Zeiten. Optimiert für Geschwindigkeit und Einfachheit."</string>
<string name="screen_onboarding_welcome_subtitle">"Willkommen zur %1$s. Verbessert, für Geschwindigkeit und Einfachheit."</string>
<string name="screen_onboarding_welcome_title">"Sei in deinem Element"</string>
</resources>

10
features/onboarding/impl/src/main/res/values-fr/translations.xml

@ -1,10 +1,10 @@ @@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_onboarding_sign_in_manually">"Se connecter manuellement"</string>
<string name="screen_onboarding_sign_in_with_qr_code">"Se connecter avec un code QR"</string>
<string name="screen_onboarding_sign_in_manually">"Connectez-vous manuellement"</string>
<string name="screen_onboarding_sign_in_with_qr_code">"Connectez-vous avec le code QR"</string>
<string name="screen_onboarding_sign_up">"Créer un compte"</string>
<string name="screen_onboarding_subtitle">"Communiquer et collaborer en toute sécurité"</string>
<string name="screen_onboarding_welcome_message">"Bienvenue dans l’Element le plus rapide de tous les temps. Surpuissant pour plus de vitesse et de simplicité."</string>
<string name="screen_onboarding_welcome_subtitle">"Bienvenue dans %1$s. Affiné pour plus de rapidité et de simplicité."</string>
<string name="screen_onboarding_subtitle">"Communiquez et collaborez en toute sécurité"</string>
<string name="screen_onboarding_welcome_message">"Bienvenue dans l\'Element le plus rapide de tous les temps. Boosté pour plus de rapidité et de simplicité."</string>
<string name="screen_onboarding_welcome_subtitle">"Bienvenue sur %1$s. Boosté, pour rapidité et simplicité."</string>
<string name="screen_onboarding_welcome_title">"Soyez dans votre Element"</string>
</resources>

2
features/onboarding/impl/src/main/res/values-zh-rTW/translations.xml

@ -4,5 +4,5 @@ @@ -4,5 +4,5 @@
<string name="screen_onboarding_sign_in_with_qr_code">"使用 QR code 登入"</string>
<string name="screen_onboarding_sign_up">"建立帳號"</string>
<string name="screen_onboarding_welcome_message">"歡迎使用有史以來最快的 Element。速度超快,操作簡便。"</string>
<string name="screen_onboarding_welcome_title">"得心應手"</string>
<string name="screen_onboarding_welcome_title">"Be in your element"</string>
</resources>

3
features/onboarding/impl/src/test/kotlin/io/element/android/features/onboarding/impl/OnBoardingPresenterTest.kt

@ -29,8 +29,7 @@ import org.junit.Test @@ -29,8 +29,7 @@ import org.junit.Test
class OnBoardingPresenterTest {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
@Test

2
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 @@ -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( @@ -111,6 +112,7 @@ fun PollAnswerView(
answerItem.isSelected -> 1f
else -> 0f
},
trackColor = ElementTheme.colors.progressIndicatorTrackColor,
strokeCap = StrokeCap.Round,
)
}

31
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 @@ -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 @@ -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 @@ -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 @@ -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( @@ -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( @@ -76,6 +83,7 @@ fun CreatePollView(
onDismiss = { state.eventSink(CreatePollEvents.HideConfirmation) }
)
val questionFocusRequester = remember { FocusRequester() }
val answerFocusRequester = remember { FocusRequester() }
LaunchedEffect(Unit) {
questionFocusRequester.requestFocus()
}
@ -102,21 +110,22 @@ fun CreatePollView( @@ -102,21 +110,22 @@ fun CreatePollView(
)
},
) { paddingValues ->
val lazyListState = rememberLazyListState()
LazyColumn(
modifier = Modifier
.padding(paddingValues)
.consumeWindowInsets(paddingValues)
.imePadding()
.fillMaxSize(),
state = lazyListState,
) {
item {
Column {
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(
@ -135,7 +144,9 @@ fun CreatePollView( @@ -135,7 +144,9 @@ fun CreatePollView(
}
)
}
}
itemsIndexed(state.answers) { index, answer ->
val isLastItem = index == state.answers.size - 1
ListItem(
headlineContent = {
OutlinedTextField(
@ -143,7 +154,9 @@ fun CreatePollView( @@ -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,14 +183,19 @@ fun CreatePollView( @@ -170,14 +183,19 @@ 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 {
Column {
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)) },
@ -190,6 +208,7 @@ fun CreatePollView( @@ -190,6 +208,7 @@ fun CreatePollView(
}
}
}
}
@DayNightPreviews
@Composable

11
features/poll/impl/src/main/res/values-de/translations.xml

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_create_poll_add_option_btn">"Option hinzufügen"</string>
<string name="screen_create_poll_anonymous_desc">"Ergebnisse erst nach Ende der Umfrage anzeigen"</string>
<string name="screen_create_poll_anonymous_headline">"Anonyme Umfrage"</string>
<string name="screen_create_poll_answer_hint">"Option %1$d"</string>
<string name="screen_create_poll_discard_confirmation">"Bist du sicher, dass du diese Umfrage verwerfen willst?"</string>
<string name="screen_create_poll_discard_confirmation_title">"Umfrage verwerfen"</string>
<string name="screen_create_poll_question_desc">"Frage oder Thema"</string>
<string name="screen_create_poll_title">"Umfrage erstellen"</string>
</resources>

12
features/poll/impl/src/main/res/values-fr/translations.xml

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_create_poll_add_option_btn">"Ajouter une option"</string>
<string name="screen_create_poll_anonymous_desc">"Afficher les résultats uniquement après la fin du sondage"</string>
<string name="screen_create_poll_anonymous_headline">"Masquer les votes"</string>
<string name="screen_create_poll_answer_hint">"Option %1$d"</string>
<string name="screen_create_poll_discard_confirmation">"Êtes-vous sûr de vouloir supprimer ce sondage ?"</string>
<string name="screen_create_poll_discard_confirmation_title">"Supprimer le sondage"</string>
<string name="screen_create_poll_question_desc">"Question ou sujet"</string>
<string name="screen_create_poll_question_hint">"Quel est le sujet du sondage ?"</string>
<string name="screen_create_poll_title">"Créer un sondage"</string>
</resources>

12
features/poll/impl/src/main/res/values-sk/translations.xml

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_create_poll_add_option_btn">"Pridať možnosť"</string>
<string name="screen_create_poll_anonymous_desc">"Zobraziť výsledky až po skončení ankety"</string>
<string name="screen_create_poll_anonymous_headline">"Anonymná anketa"</string>
<string name="screen_create_poll_answer_hint">"Možnosť %1$d"</string>
<string name="screen_create_poll_discard_confirmation">"Ste si istí, že chcete túto anketu zahodiť?"</string>
<string name="screen_create_poll_discard_confirmation_title">"Odstrániť anketu"</string>
<string name="screen_create_poll_question_desc">"Otázka alebo téma"</string>
<string name="screen_create_poll_question_hint">"O čom je anketa?"</string>
<string name="screen_create_poll_title">"Vytvoriť anketu"</string>
</resources>

2
features/poll/impl/src/main/res/values/localazy.xml

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_create_poll_add_option_btn">"Add option"</string>
<string name="screen_create_poll_anonymous_desc">"Show results only after poll ends"</string>
<string name="screen_create_poll_anonymous_headline">"Anonymous Poll"</string>
<string name="screen_create_poll_anonymous_headline">"Hide votes"</string>
<string name="screen_create_poll_answer_hint">"Option %1$d"</string>
<string name="screen_create_poll_discard_confirmation">"Are you sure you want to discard this poll?"</string>
<string name="screen_create_poll_discard_confirmation_title">"Discard Poll"</string>

3
features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenterTest.kt

@ -35,8 +35,7 @@ import org.junit.Test @@ -35,8 +35,7 @@ import org.junit.Test
class CreatePollPresenterTest {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
private var navUpInvocationsCount = 0

2
features/preferences/impl/build.gradle.kts

@ -40,6 +40,8 @@ dependencies { @@ -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)

24
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 @@ -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( @@ -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( @@ -94,6 +102,10 @@ class PreferencesFlowNode @AssistedInject constructor(
override fun onOpenDeveloperSettings() {
backstack.push(NavTarget.DeveloperSettings)
}
override fun onOpenNotificationSettings() {
backstack.push(NavTarget.NotificationSettings)
}
}
createNode<PreferencesRootNode>(buildContext, plugins = listOf(callback))
}
@ -114,6 +126,18 @@ class PreferencesFlowNode @AssistedInject constructor( @@ -114,6 +126,18 @@ class PreferencesFlowNode @AssistedInject constructor(
NavTarget.AnalyticsSettings -> {
createNode<AnalyticsSettingsNode>(buildContext)
}
NavTarget.NotificationSettings -> {
val notificationSettingsCallback = object : NotificationSettingsNode.Callback {
override fun editDefaultNotificationMode(isOneToOne: Boolean) {
backstack.push(NavTarget.EditDefaultNotificationSetting(isOneToOne))
}
}
createNode<NotificationSettingsNode>(buildContext, listOf(notificationSettingsCallback))
}
is NavTarget.EditDefaultNotificationSetting -> {
val input = EditDefaultNotificationSettingNode.Inputs(navTarget.isOneToOne)
createNode<EditDefaultNotificationSettingNode>(buildContext, plugins = listOf(input))
}
}
}

27
features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsEvents.kt

@ -0,0 +1,27 @@ @@ -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
}

57
features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsNode.kt

@ -0,0 +1,57 @@ @@ -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<Plugin>,
private val presenter: NotificationSettingsPresenter,
) : Node(buildContext, plugins = plugins) {
interface Callback : Plugin {
fun editDefaultNotificationMode(isOneToOne: Boolean)
}
private val callbacks = plugins<Callback>()
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,
)
}
}

168
features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt

@ -0,0 +1,168 @@ @@ -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<NotificationSettingsState> {
@Composable
override fun present(): NotificationSettingsState {
val userPushStore = remember { userPushStoreFactory.create(matrixClient.sessionId) }
val systemNotificationsEnabled: MutableState<Boolean> = remember {
mutableStateOf(systemNotificationsEnabledProvider.notificationsEnabled())
}
val localCoroutineScope = rememberCoroutineScope()
val appNotificationsEnabled = userPushStore
.getNotificationEnabledForDevice()
.collectAsState(initial = false)
val matrixSettings: MutableState<NotificationSettingsState.MatrixSettings> = 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<NotificationSettingsState.MatrixSettings>) {
notificationSettingsService.notificationSettingsChangeFlow
.debounce(0.5.seconds)
.onEach {
fetchSettings(target)
}
.launchIn(this)
}
private fun CoroutineScope.fetchSettings(target: MutableState<NotificationSettingsState.MatrixSettings>) = 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<NotificationSettingsState.MatrixSettings>) = 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)
}
}

50
features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsState.kt

@ -0,0 +1,50 @@ @@ -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,
)
}

41
features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsStateProvider.kt

@ -0,0 +1,41 @@ @@ -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<NotificationSettingsState> {
override val values: Sequence<NotificationSettingsState>
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 = {}
)

263
features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt

@ -0,0 +1,263 @@ @@ -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 = {},
)
}

39
features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/SystemNotificationsEnabledProvider.kt

@ -0,0 +1,39 @@ @@ -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()
}
}

98
features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/DefaultNotificationSettingOption.kt

@ -0,0 +1,98 @@ @@ -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,
)
}
}

54
features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingNode.kt

@ -0,0 +1,54 @@ @@ -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<Plugin>,
presenterFactory: EditDefaultNotificationSettingPresenter.Factory
) : Node(buildContext, plugins = plugins) {
data class Inputs(
val isOneToOne: Boolean
) : NodeInputs
private val inputs = 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
)
}
}

92
features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt

@ -0,0 +1,92 @@ @@ -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<EditDefaultNotificationSettingState> {
@AssistedFactory
interface Factory {
fun create(oneToOne: Boolean): EditDefaultNotificationSettingPresenter
}
@Composable
override fun present(): EditDefaultNotificationSettingState {
val mode: MutableState<RoomNotificationMode?> = 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<RoomNotificationMode?>) = launch {
mode.value = notificationSettingsService.getDefaultRoomNotificationMode(isEncrypted = true, isOneToOne = isOneToOne).getOrThrow()
}
@OptIn(FlowPreview::class)
private fun CoroutineScope.observeNotificationSettings(mode: MutableState<RoomNotificationMode?>) {
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)
}
}

25
features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingState.kt

@ -0,0 +1,25 @@ @@ -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,
)

23
features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingStateEvents.kt

@ -0,0 +1,23 @@ @@ -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
}

75
features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt

@ -0,0 +1,75 @@ @@ -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)) }
)
}
}
}
}
}
}

6
features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt

@ -44,6 +44,7 @@ class PreferencesRootNode @AssistedInject constructor( @@ -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( @@ -72,6 +73,10 @@ class PreferencesRootNode @AssistedInject constructor(
}
}
private fun onOpenNotificationSettings() {
plugins<Callback>().forEach { it.onOpenNotificationSettings() }
}
@Composable
override fun View(modifier: Modifier) {
val state = presenter.present()
@ -87,6 +92,7 @@ class PreferencesRootNode @AssistedInject constructor( @@ -87,6 +92,7 @@ class PreferencesRootNode @AssistedInject constructor(
onOpenDeveloperSettings = this::onOpenDeveloperSettings,
onSuccessLogout = { onSuccessLogout(activity, it) },
onManageAccountClicked = { onManageAccountClicked(activity, state.accountManagementUrl) },
onOpenNotificationSettings = this::onOpenNotificationSettings
)
}

9
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 @@ -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( @@ -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<PreferencesRootState> {
@Composable
@ -60,6 +63,11 @@ class PreferencesRootPresenter @Inject constructor( @@ -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( @@ -81,6 +89,7 @@ class PreferencesRootPresenter @Inject constructor(
accountManagementUrl = accountManagementUrl.value,
showAnalyticsSettings = hasAnalyticsProviders,
showDeveloperSettings = showDeveloperSettings,
showNotificationSettings = showNotificationSettings.value,
snackbarMessage = snackbarMessage,
)
}

1
features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt

@ -28,5 +28,6 @@ data class PreferencesRootState( @@ -28,5 +28,6 @@ data class PreferencesRootState(
val accountManagementUrl: String?,
val showAnalyticsSettings: Boolean,
val showDeveloperSettings: Boolean,
val showNotificationSettings: Boolean,
val snackbarMessage: SnackbarMessage?,
)

1
features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt

@ -28,5 +28,6 @@ fun aPreferencesRootState() = PreferencesRootState( @@ -28,5 +28,6 @@ fun aPreferencesRootState() = PreferencesRootState(
accountManagementUrl = "aUrl",
showAnalyticsSettings = true,
showDeveloperSettings = true,
showNotificationSettings = true,
snackbarMessage = SnackbarMessage(CommonStrings.common_verification_complete),
)

10
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 @@ -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( @@ -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( @@ -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) { @@ -153,5 +162,6 @@ private fun ContentToPreview(matrixUser: MatrixUser) {
onVerifyClicked = {},
onSuccessLogout = {},
onManageAccountClicked = {},
onOpenNotificationSettings = {},
)
}

2
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 @@ -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

3
features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/about/AboutPresenterTest.kt

@ -27,8 +27,7 @@ import org.junit.Test @@ -27,8 +27,7 @@ import org.junit.Test
class AboutPresenterTest {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
@Test

3
features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsPresenterTest.kt

@ -30,8 +30,7 @@ import org.junit.Test @@ -30,8 +30,7 @@ import org.junit.Test
class AnalyticsSettingsPresenterTest {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
@Test

3
features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt

@ -35,8 +35,7 @@ import org.junit.Test @@ -35,8 +35,7 @@ import org.junit.Test
class DeveloperSettingsPresenterTest {
@Rule
@JvmField
@get:Rule
val warmUpRule = WarmUpRule()
@Test

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save