Browse Source

Merge pull request #1955 from vector-im/feature/bma/notificationForInvites

Add setting to disable notification for invites
pull/1959/head
Benoit Marty 10 months ago committed by GitHub
parent
commit
8f5fed48ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      changelog.d/1944.misc
  2. 2
      docs/_developer_onboarding.md
  3. 2
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsEvents.kt
  4. 11
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt
  5. 1
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsState.kt
  6. 1
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsStateProvider.kt
  7. 15
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt
  8. 31
      features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTests.kt
  9. 2
      gradle/libs.versions.toml
  10. 2
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notificationsettings/NotificationSettingsService.kt
  11. 8
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt
  12. 25
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt
  13. 13
      libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notificationsettings/FakeNotificationSettingsService.kt
  14. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-5_5_null_0,NEXUS_5,1.0,en].png
  15. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-5_5_null_1,NEXUS_5,1.0,en].png
  16. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-5_5_null_2,NEXUS_5,1.0,en].png
  17. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-5_6_null_0,NEXUS_5,1.0,en].png
  18. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-5_6_null_1,NEXUS_5,1.0,en].png
  19. BIN
      tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-5_6_null_2,NEXUS_5,1.0,en].png

1
changelog.d/1944.misc

@ -0,0 +1 @@
Add toggle in the notification settings to disable notifications for room invites.

2
docs/_developer_onboarding.md

@ -117,7 +117,7 @@ You can also have access to the aars through the [release](https://github.com/ma
#### Build the SDK locally #### Build the SDK locally
Easiest way: run the script [./tools/sdk/build_rust_sdk.sh](./tools/sdk/build_rust_sdk.sh) and just answer the questions. Easiest way: run the script [../tools/sdk/build_rust_sdk.sh](../tools/sdk/build_rust_sdk.sh) and just answer the questions.
Legacy way: Legacy way:

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

@ -17,11 +17,11 @@
package io.element.android.features.preferences.impl.notifications package io.element.android.features.preferences.impl.notifications
sealed interface NotificationSettingsEvents { sealed interface NotificationSettingsEvents {
data object RefreshSystemNotificationsEnabled : NotificationSettingsEvents data object RefreshSystemNotificationsEnabled : NotificationSettingsEvents
data class SetNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvents data class SetNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvents
data class SetAtRoomNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvents data class SetAtRoomNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvents
data class SetCallNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvents data class SetCallNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvents
data class SetInviteForMeNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvents
data object FixConfigurationMismatch : NotificationSettingsEvents data object FixConfigurationMismatch : NotificationSettingsEvents
data object ClearConfigurationMismatchError : NotificationSettingsEvents data object ClearConfigurationMismatchError : NotificationSettingsEvents
data object ClearNotificationChangeError : NotificationSettingsEvents data object ClearNotificationChangeError : NotificationSettingsEvents

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

@ -76,6 +76,9 @@ class NotificationSettingsPresenter @Inject constructor(
is NotificationSettingsEvents.SetCallNotificationsEnabled -> { is NotificationSettingsEvents.SetCallNotificationsEnabled -> {
localCoroutineScope.setCallNotificationsEnabled(event.enabled, changeNotificationSettingAction) localCoroutineScope.setCallNotificationsEnabled(event.enabled, changeNotificationSettingAction)
} }
is NotificationSettingsEvents.SetInviteForMeNotificationsEnabled -> {
localCoroutineScope.setInviteForMeNotificationsEnabled(event.enabled, changeNotificationSettingAction)
}
is NotificationSettingsEvents.SetNotificationsEnabled -> localCoroutineScope.setNotificationsEnabled(userPushStore, event.enabled) is NotificationSettingsEvents.SetNotificationsEnabled -> localCoroutineScope.setNotificationsEnabled(userPushStore, event.enabled)
NotificationSettingsEvents.ClearConfigurationMismatchError -> { NotificationSettingsEvents.ClearConfigurationMismatchError -> {
matrixSettings.value = NotificationSettingsState.MatrixSettings.Invalid(fixFailed = false) matrixSettings.value = NotificationSettingsState.MatrixSettings.Invalid(fixFailed = false)
@ -123,10 +126,12 @@ class NotificationSettingsPresenter @Inject constructor(
val callNotificationsEnabled = notificationSettingsService.isCallEnabled().getOrThrow() val callNotificationsEnabled = notificationSettingsService.isCallEnabled().getOrThrow()
val atRoomNotificationsEnabled = notificationSettingsService.isRoomMentionEnabled().getOrThrow() val atRoomNotificationsEnabled = notificationSettingsService.isRoomMentionEnabled().getOrThrow()
val inviteForMeNotificationsEnabled = notificationSettingsService.isInviteForMeEnabled().getOrThrow()
target.value = NotificationSettingsState.MatrixSettings.Valid( target.value = NotificationSettingsState.MatrixSettings.Valid(
atRoomNotificationsEnabled = atRoomNotificationsEnabled, atRoomNotificationsEnabled = atRoomNotificationsEnabled,
callNotificationsEnabled = callNotificationsEnabled, callNotificationsEnabled = callNotificationsEnabled,
inviteForMeNotificationsEnabled = inviteForMeNotificationsEnabled,
defaultGroupNotificationMode = encryptedGroupDefaultMode, defaultGroupNotificationMode = encryptedGroupDefaultMode,
defaultOneToOneNotificationMode = encryptedOneToOneDefaultMode, defaultOneToOneNotificationMode = encryptedOneToOneDefaultMode,
) )
@ -175,6 +180,12 @@ class NotificationSettingsPresenter @Inject constructor(
}.runCatchingUpdatingState(action) }.runCatchingUpdatingState(action)
} }
private fun CoroutineScope.setInviteForMeNotificationsEnabled(enabled: Boolean, action: MutableState<Async<Unit>>) = launch {
suspend {
notificationSettingsService.setInviteForMeEnabled(enabled).getOrThrow()
}.runCatchingUpdatingState(action)
}
private fun CoroutineScope.setNotificationsEnabled(userPushStore: UserPushStore, enabled: Boolean) = launch { private fun CoroutineScope.setNotificationsEnabled(userPushStore: UserPushStore, enabled: Boolean) = launch {
userPushStore.setNotificationEnabledForDevice(enabled) userPushStore.setNotificationEnabledForDevice(enabled)
} }

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

@ -32,6 +32,7 @@ data class NotificationSettingsState(
data class Valid( data class Valid(
val atRoomNotificationsEnabled: Boolean, val atRoomNotificationsEnabled: Boolean,
val callNotificationsEnabled: Boolean, val callNotificationsEnabled: Boolean,
val inviteForMeNotificationsEnabled: Boolean,
val defaultGroupNotificationMode: RoomNotificationMode?, val defaultGroupNotificationMode: RoomNotificationMode?,
val defaultOneToOneNotificationMode: RoomNotificationMode?, val defaultOneToOneNotificationMode: RoomNotificationMode?,
) : MatrixSettings ) : MatrixSettings

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

@ -35,6 +35,7 @@ fun aNotificationSettingsState(
matrixSettings = NotificationSettingsState.MatrixSettings.Valid( matrixSettings = NotificationSettingsState.MatrixSettings.Valid(
atRoomNotificationsEnabled = true, atRoomNotificationsEnabled = true,
callNotificationsEnabled = true, callNotificationsEnabled = true,
inviteForMeNotificationsEnabled = true,
defaultGroupNotificationMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY, defaultGroupNotificationMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
defaultOneToOneNotificationMode = RoomNotificationMode.ALL_MESSAGES, defaultOneToOneNotificationMode = RoomNotificationMode.ALL_MESSAGES,
), ),

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

@ -77,6 +77,7 @@ fun NotificationSettingsView(
onMentionNotificationsChanged = { state.eventSink(NotificationSettingsEvents.SetAtRoomNotificationsEnabled(it)) }, onMentionNotificationsChanged = { state.eventSink(NotificationSettingsEvents.SetAtRoomNotificationsEnabled(it)) },
// TODO We are removing the call notification toggle until support for call notifications has been added // TODO We are removing the call notification toggle until support for call notifications has been added
// onCallsNotificationsChanged = { state.eventSink(NotificationSettingsEvents.SetCallNotificationsEnabled(it)) }, // onCallsNotificationsChanged = { state.eventSink(NotificationSettingsEvents.SetCallNotificationsEnabled(it)) },
onInviteForMeNotificationsChanged = { state.eventSink(NotificationSettingsEvents.SetInviteForMeNotificationsEnabled(it)) },
) )
} }
AsyncView( AsyncView(
@ -98,6 +99,7 @@ private fun NotificationSettingsContentView(
onMentionNotificationsChanged: (Boolean) -> Unit, onMentionNotificationsChanged: (Boolean) -> Unit,
// TODO We are removing the call notification toggle until support for call notifications has been added // TODO We are removing the call notification toggle until support for call notifications has been added
// onCallsNotificationsChanged: (Boolean) -> Unit, // onCallsNotificationsChanged: (Boolean) -> Unit,
onInviteForMeNotificationsChanged: (Boolean) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val context = LocalContext.current val context = LocalContext.current
@ -147,8 +149,8 @@ private fun NotificationSettingsContentView(
onCheckedChange = onMentionNotificationsChanged onCheckedChange = onMentionNotificationsChanged
) )
} }
// TODO We are removing the call notification toggle until support for call notifications has been added PreferenceCategory(title = stringResource(id = R.string.screen_notification_settings_additional_settings_section_title)) {
// PreferenceCategory(title = stringResource(id = CommonStrings.screen_notification_settings_additional_settings_section_title)) { // TODO We are removing the call notification toggle until support for call notifications has been added
// PreferenceSwitch( // PreferenceSwitch(
// modifier = Modifier, // modifier = Modifier,
// title = stringResource(id = CommonStrings.screen_notification_settings_calls_label), // title = stringResource(id = CommonStrings.screen_notification_settings_calls_label),
@ -156,7 +158,14 @@ private fun NotificationSettingsContentView(
// switchAlignment = Alignment.Top, // switchAlignment = Alignment.Top,
// onCheckedChange = onCallsNotificationsChanged // onCheckedChange = onCallsNotificationsChanged
// ) // )
// } PreferenceSwitch(
modifier = Modifier,
title = stringResource(id = R.string.screen_notification_settings_invite_for_me_label),
isChecked = matrixSettings.inviteForMeNotificationsEnabled,
switchAlignment = Alignment.Top,
onCheckedChange = onInviteForMeNotificationsChanged
)
}
} }
} }

31
features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTests.kt

@ -41,7 +41,6 @@ class NotificationSettingsPresenterTests {
assertThat(initialState.appSettings.appNotificationsEnabled).isFalse() assertThat(initialState.appSettings.appNotificationsEnabled).isFalse()
assertThat(initialState.appSettings.systemNotificationsEnabled).isTrue() assertThat(initialState.appSettings.systemNotificationsEnabled).isTrue()
assertThat(initialState.matrixSettings).isEqualTo(NotificationSettingsState.MatrixSettings.Uninitialized) assertThat(initialState.matrixSettings).isEqualTo(NotificationSettingsState.MatrixSettings.Uninitialized)
val loadedState = consumeItemsUntilPredicate { val loadedState = consumeItemsUntilPredicate {
it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid
}.last() }.last()
@ -50,6 +49,7 @@ class NotificationSettingsPresenterTests {
val valid = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid val valid = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid
assertThat(valid?.atRoomNotificationsEnabled).isFalse() assertThat(valid?.atRoomNotificationsEnabled).isFalse()
assertThat(valid?.callNotificationsEnabled).isFalse() assertThat(valid?.callNotificationsEnabled).isFalse()
assertThat(valid?.inviteForMeNotificationsEnabled).isFalse()
assertThat(valid?.defaultGroupNotificationMode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY) assertThat(valid?.defaultGroupNotificationMode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
assertThat(valid?.defaultOneToOneNotificationMode).isEqualTo(RoomNotificationMode.ALL_MESSAGES) assertThat(valid?.defaultOneToOneNotificationMode).isEqualTo(RoomNotificationMode.ALL_MESSAGES)
cancelAndIgnoreRemainingEvents() cancelAndIgnoreRemainingEvents()
@ -63,7 +63,6 @@ class NotificationSettingsPresenterTests {
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = true, isOneToOne = false, mode = RoomNotificationMode.ALL_MESSAGES) notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = true, isOneToOne = false, mode = RoomNotificationMode.ALL_MESSAGES)
notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = false, isOneToOne = false, mode = RoomNotificationMode.ALL_MESSAGES) notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = false, isOneToOne = false, mode = RoomNotificationMode.ALL_MESSAGES)
val updatedState = consumeItemsUntilPredicate { val updatedState = consumeItemsUntilPredicate {
@ -82,7 +81,6 @@ class NotificationSettingsPresenterTests {
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
notificationSettingsService.setDefaultRoomNotificationMode( notificationSettingsService.setDefaultRoomNotificationMode(
isEncrypted = true, isEncrypted = true,
isOneToOne = false, isOneToOne = false,
@ -118,7 +116,6 @@ class NotificationSettingsPresenterTests {
val fixedState = consumeItemsUntilPredicate(timeout = 2000.milliseconds) { val fixedState = consumeItemsUntilPredicate(timeout = 2000.milliseconds) {
it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid
}.last() }.last()
val fixedMatrixState = fixedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid val fixedMatrixState = fixedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid
assertThat(fixedMatrixState?.defaultGroupNotificationMode).isEqualTo(RoomNotificationMode.ALL_MESSAGES) assertThat(fixedMatrixState?.defaultGroupNotificationMode).isEqualTo(RoomNotificationMode.ALL_MESSAGES)
} }
@ -134,7 +131,6 @@ class NotificationSettingsPresenterTests {
it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid
}.last() }.last()
assertThat(loadedState.appSettings.appNotificationsEnabled).isTrue() assertThat(loadedState.appSettings.appNotificationsEnabled).isTrue()
loadedState.eventSink(NotificationSettingsEvents.SetNotificationsEnabled(false)) loadedState.eventSink(NotificationSettingsEvents.SetNotificationsEnabled(false))
val updatedState = consumeItemsUntilPredicate { val updatedState = consumeItemsUntilPredicate {
!it.appSettings.appNotificationsEnabled !it.appSettings.appNotificationsEnabled
@ -155,7 +151,6 @@ class NotificationSettingsPresenterTests {
}.last() }.last()
val validMatrixState = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid val validMatrixState = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid
assertThat(validMatrixState?.callNotificationsEnabled).isFalse() assertThat(validMatrixState?.callNotificationsEnabled).isFalse()
loadedState.eventSink(NotificationSettingsEvents.SetCallNotificationsEnabled(true)) loadedState.eventSink(NotificationSettingsEvents.SetCallNotificationsEnabled(true))
val updatedState = consumeItemsUntilPredicate { val updatedState = consumeItemsUntilPredicate {
(it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.callNotificationsEnabled == true (it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.callNotificationsEnabled == true
@ -166,6 +161,27 @@ class NotificationSettingsPresenterTests {
} }
} }
@Test
fun `present - set invite for me notifications enabled`() = runTest {
val presenter = createNotificationSettingsPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val loadedState = consumeItemsUntilPredicate {
(it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.inviteForMeNotificationsEnabled == false
}.last()
val validMatrixState = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid
assertThat(validMatrixState?.inviteForMeNotificationsEnabled).isFalse()
loadedState.eventSink(NotificationSettingsEvents.SetInviteForMeNotificationsEnabled(true))
val updatedState = consumeItemsUntilPredicate {
(it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.inviteForMeNotificationsEnabled == true
}.last()
val updatedMatrixState = updatedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid
assertThat(updatedMatrixState?.inviteForMeNotificationsEnabled).isTrue()
cancelAndIgnoreRemainingEvents()
}
}
@Test @Test
fun `present - set atRoom notifications enabled`() = runTest { fun `present - set atRoom notifications enabled`() = runTest {
val presenter = createNotificationSettingsPresenter() val presenter = createNotificationSettingsPresenter()
@ -177,7 +193,6 @@ class NotificationSettingsPresenterTests {
}.last() }.last()
val validMatrixState = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid val validMatrixState = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid
assertThat(validMatrixState?.atRoomNotificationsEnabled).isFalse() assertThat(validMatrixState?.atRoomNotificationsEnabled).isFalse()
loadedState.eventSink(NotificationSettingsEvents.SetAtRoomNotificationsEnabled(true)) loadedState.eventSink(NotificationSettingsEvents.SetAtRoomNotificationsEnabled(true))
val updatedState = consumeItemsUntilPredicate { val updatedState = consumeItemsUntilPredicate {
(it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.atRoomNotificationsEnabled == true (it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.atRoomNotificationsEnabled == true
@ -201,14 +216,12 @@ class NotificationSettingsPresenterTests {
}.last() }.last()
val validMatrixState = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid val validMatrixState = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid
assertThat(validMatrixState?.atRoomNotificationsEnabled).isFalse() assertThat(validMatrixState?.atRoomNotificationsEnabled).isFalse()
loadedState.eventSink(NotificationSettingsEvents.SetAtRoomNotificationsEnabled(true)) loadedState.eventSink(NotificationSettingsEvents.SetAtRoomNotificationsEnabled(true))
val errorState = consumeItemsUntilPredicate { val errorState = consumeItemsUntilPredicate {
it.changeNotificationSettingAction.isFailure() it.changeNotificationSettingAction.isFailure()
}.last() }.last()
assertThat(errorState.changeNotificationSettingAction.isFailure()).isTrue() assertThat(errorState.changeNotificationSettingAction.isFailure()).isTrue()
errorState.eventSink(NotificationSettingsEvents.ClearNotificationChangeError) errorState.eventSink(NotificationSettingsEvents.ClearNotificationChangeError)
val clearErrorState = consumeItemsUntilPredicate { val clearErrorState = consumeItemsUntilPredicate {
it.changeNotificationSettingAction.isUninitialized() it.changeNotificationSettingAction.isUninitialized()
}.last() }.last()

2
gradle/libs.versions.toml

@ -147,7 +147,7 @@ jsoup = "org.jsoup:jsoup:1.17.1"
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
molecule-runtime = "app.cash.molecule:molecule-runtime:1.3.1" molecule-runtime = "app.cash.molecule:molecule-runtime:1.3.1"
timber = "com.jakewharton.timber:timber:5.0.1" timber = "com.jakewharton.timber:timber:5.0.1"
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.73" matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.74"
matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" } matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" }
matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" } matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" }
sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" } sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }

2
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notificationsettings/NotificationSettingsService.kt

@ -38,5 +38,7 @@ interface NotificationSettingsService {
suspend fun setRoomMentionEnabled(enabled: Boolean): Result<Unit> suspend fun setRoomMentionEnabled(enabled: Boolean): Result<Unit>
suspend fun isCallEnabled(): Result<Boolean> suspend fun isCallEnabled(): Result<Boolean>
suspend fun setCallEnabled(enabled: Boolean): Result<Unit> suspend fun setCallEnabled(enabled: Boolean): Result<Unit>
suspend fun isInviteForMeEnabled(): Result<Boolean>
suspend fun setInviteForMeEnabled(enabled: Boolean): Result<Unit>
suspend fun getRoomsWithUserDefinedRules(): Result<List<String>> suspend fun getRoomsWithUserDefinedRules(): Result<List<String>>
} }

8
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt

@ -119,10 +119,9 @@ class RustMatrixClient constructor(
.filterByPushRules() .filterByPushRules()
.finish() .finish()
} }
private val notificationSettings = client.getNotificationSettings()
private val notificationService = RustNotificationService(sessionId, notificationClient, dispatchers, clock) private val notificationService = RustNotificationService(sessionId, notificationClient, dispatchers, clock)
private val notificationSettingsService = RustNotificationSettingsService(notificationSettings, dispatchers) private val notificationSettingsService = RustNotificationSettingsService(client, dispatchers)
.apply { start() }
private val roomSyncSubscriber = RoomSyncSubscriber(innerRoomListService, dispatchers) private val roomSyncSubscriber = RoomSyncSubscriber(innerRoomListService, dispatchers)
private val encryptionService = RustEncryptionService( private val encryptionService = RustEncryptionService(
client = client, client = client,
@ -346,8 +345,7 @@ class RustMatrixClient constructor(
override fun close() { override fun close() {
sessionCoroutineScope.cancel() sessionCoroutineScope.cancel()
clientDelegateTaskHandle?.cancelAndDestroy() clientDelegateTaskHandle?.cancelAndDestroy()
notificationSettings.setDelegate(null) notificationSettingsService.destroy()
notificationSettings.destroy()
verificationService.destroy() verificationService.destroy()
syncService.destroy() syncService.destroy()
innerRoomListService.destroy() innerRoomListService.destroy()

25
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt

@ -26,16 +26,16 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.matrix.rustcomponents.sdk.NotificationSettings import org.matrix.rustcomponents.sdk.Client
import org.matrix.rustcomponents.sdk.NotificationSettingsDelegate import org.matrix.rustcomponents.sdk.NotificationSettingsDelegate
import org.matrix.rustcomponents.sdk.NotificationSettingsException import org.matrix.rustcomponents.sdk.NotificationSettingsException
import timber.log.Timber import timber.log.Timber
class RustNotificationSettingsService( class RustNotificationSettingsService(
private val notificationSettings: NotificationSettings, client: Client,
private val dispatchers: CoroutineDispatchers, private val dispatchers: CoroutineDispatchers,
) : NotificationSettingsService { ) : NotificationSettingsService {
private val notificationSettings = client.getNotificationSettings()
private val _notificationSettingsChangeFlow = MutableSharedFlow<Unit>(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) private val _notificationSettingsChangeFlow = MutableSharedFlow<Unit>(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
override val notificationSettingsChangeFlow: SharedFlow<Unit> = _notificationSettingsChangeFlow.asSharedFlow() override val notificationSettingsChangeFlow: SharedFlow<Unit> = _notificationSettingsChangeFlow.asSharedFlow()
@ -45,10 +45,15 @@ class RustNotificationSettingsService(
} }
} }
init { fun start() {
notificationSettings.setDelegate(notificationSettingsDelegate) notificationSettings.setDelegate(notificationSettingsDelegate)
} }
fun destroy() {
notificationSettings.setDelegate(null)
notificationSettings.destroy()
}
override suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result<RoomNotificationSettings> = override suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result<RoomNotificationSettings> =
runCatching { runCatching {
notificationSettings.getRoomNotificationSettings(roomId.value, isEncrypted, isOneToOne).let(RoomNotificationSettingsMapper::map) notificationSettings.getRoomNotificationSettings(roomId.value, isEncrypted, isOneToOne).let(RoomNotificationSettingsMapper::map)
@ -119,6 +124,18 @@ class RustNotificationSettingsService(
} }
} }
override suspend fun isInviteForMeEnabled(): Result<Boolean> = withContext(dispatchers.io) {
runCatching {
notificationSettings.isInviteForMeEnabled()
}
}
override suspend fun setInviteForMeEnabled(enabled: Boolean): Result<Unit> = withContext(dispatchers.io) {
runCatching {
notificationSettings.setInviteForMeEnabled(enabled)
}
}
override suspend fun getRoomsWithUserDefinedRules(): Result<List<String>> = override suspend fun getRoomsWithUserDefinedRules(): Result<List<String>> =
runCatching { runCatching {
notificationSettings.getRoomsWithUserDefinedRules(enabled = true) notificationSettings.getRoomsWithUserDefinedRules(enabled = true)

13
libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notificationsettings/FakeNotificationSettingsService.kt

@ -41,6 +41,7 @@ class FakeNotificationSettingsService(
private var roomNotificationMode: RoomNotificationMode = initialRoomMode private var roomNotificationMode: RoomNotificationMode = initialRoomMode
private var roomNotificationModeIsDefault: Boolean = initialRoomModeIsDefault private var roomNotificationModeIsDefault: Boolean = initialRoomModeIsDefault
private var callNotificationsEnabled = false private var callNotificationsEnabled = false
private var inviteNotificationsEnabled = false
private var atRoomNotificationsEnabled = false private var atRoomNotificationsEnabled = false
private var setNotificationModeError: Throwable? = null private var setNotificationModeError: Throwable? = null
private var restoreDefaultNotificationModeError: Throwable? = null private var restoreDefaultNotificationModeError: Throwable? = null
@ -52,7 +53,7 @@ class FakeNotificationSettingsService(
override suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result<RoomNotificationSettings> { override suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result<RoomNotificationSettings> {
return Result.success( return Result.success(
RoomNotificationSettings( RoomNotificationSettings(
mode = if(roomNotificationModeIsDefault) defaultEncryptedGroupRoomNotificationMode else roomNotificationMode, mode = if (roomNotificationModeIsDefault) defaultEncryptedGroupRoomNotificationMode else roomNotificationMode,
isDefault = roomNotificationModeIsDefault isDefault = roomNotificationModeIsDefault
) )
) )
@ -149,6 +150,15 @@ class FakeNotificationSettingsService(
return Result.success(Unit) return Result.success(Unit)
} }
override suspend fun isInviteForMeEnabled(): Result<Boolean> {
return Result.success(inviteNotificationsEnabled)
}
override suspend fun setInviteForMeEnabled(enabled: Boolean): Result<Unit> {
inviteNotificationsEnabled = enabled
return Result.success(Unit)
}
override suspend fun getRoomsWithUserDefinedRules(): Result<List<String>> { override suspend fun getRoomsWithUserDefinedRules(): Result<List<String>> {
return Result.success(if (roomNotificationModeIsDefault) listOf() else listOf(A_ROOM_ID.value)) return Result.success(if (roomNotificationModeIsDefault) listOf() else listOf(A_ROOM_ID.value))
} }
@ -168,5 +178,4 @@ class FakeNotificationSettingsService(
fun givenSetDefaultNotificationModeError(throwable: Throwable?) { fun givenSetDefaultNotificationModeError(throwable: Throwable?) {
setDefaultNotificationModeError = throwable setDefaultNotificationModeError = throwable
} }
} }

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-5_5_null_0,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-5_5_null_1,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-5_5_null_2,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-5_6_null_0,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-5_6_null_1,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.

BIN
tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-5_6_null_2,NEXUS_5,1.0,en].png (Stored with Git LFS)

Binary file not shown.
Loading…
Cancel
Save