Merge pull request #7476 from vector-im/fix/mna/push-toggle-check-support

[Session manager] Hide push notification toggle when there is no server support (PSG-970)
This commit is contained in:
Maxime NATUREL 2022-11-04 09:19:04 +01:00 committed by GitHub
commit 4621488f21
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 572 additions and 65 deletions

1
changelog.d/7457.bugfix Normal file
View file

@ -0,0 +1 @@
[Session manager] Hide push notification toggle when there is no server support

View file

@ -70,6 +70,11 @@ data class HomeServerCapabilities(
* True if the home server supports threaded read receipts and unread notifications. * True if the home server supports threaded read receipts and unread notifications.
*/ */
val canUseThreadReadReceiptsAndNotifications: Boolean = false, val canUseThreadReadReceiptsAndNotifications: Boolean = false,
/**
* True if the home server supports remote toggle of Pusher for a given device.
*/
val canRemotelyTogglePushNotificationsOfDevices: Boolean = false,
) { ) {
enum class RoomCapabilitySupport { enum class RoomCapabilitySupport {

View file

@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.auth.version
import com.squareup.moshi.Json import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.extensions.orFalse
/** /**
* Model for https://matrix.org/docs/spec/client_server/latest#get-matrix-client-versions. * Model for https://matrix.org/docs/spec/client_server/latest#get-matrix-client-versions.
@ -56,6 +57,7 @@ private const val FEATURE_THREADS_MSC3440_STABLE = "org.matrix.msc3440.stable"
private const val FEATURE_QR_CODE_LOGIN = "org.matrix.msc3882" private const val FEATURE_QR_CODE_LOGIN = "org.matrix.msc3882"
private const val FEATURE_THREADS_MSC3771 = "org.matrix.msc3771" private const val FEATURE_THREADS_MSC3771 = "org.matrix.msc3771"
private const val FEATURE_THREADS_MSC3773 = "org.matrix.msc3773" private const val FEATURE_THREADS_MSC3773 = "org.matrix.msc3773"
private const val FEATURE_REMOTE_TOGGLE_PUSH_NOTIFICATIONS_MSC3881 = "org.matrix.msc3881"
/** /**
* Return true if the SDK supports this homeserver version. * Return true if the SDK supports this homeserver version.
@ -142,3 +144,12 @@ private fun Versions.getMaxVersion(): HomeServerVersion {
?.maxOrNull() ?.maxOrNull()
?: HomeServerVersion.r0_0_0 ?: HomeServerVersion.r0_0_0
} }
/**
* Indicate if the server supports MSC3881: https://github.com/matrix-org/matrix-spec-proposals/pull/3881.
*
* @return true if remote toggle of push notifications is supported
*/
internal fun Versions.doesServerSupportRemoteToggleOfPushNotifications(): Boolean {
return unstableFeatures?.get(FEATURE_REMOTE_TOGGLE_PUSH_NOTIFICATIONS_MSC3881).orFalse()
}

View file

@ -58,6 +58,7 @@ import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo038
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo039 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo039
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo040 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo040
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo041 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo041
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo042
import org.matrix.android.sdk.internal.util.Normalizer import org.matrix.android.sdk.internal.util.Normalizer
import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration
import javax.inject.Inject import javax.inject.Inject
@ -66,7 +67,7 @@ internal class RealmSessionStoreMigration @Inject constructor(
private val normalizer: Normalizer private val normalizer: Normalizer
) : MatrixRealmMigration( ) : MatrixRealmMigration(
dbName = "Session", dbName = "Session",
schemaVersion = 41L, schemaVersion = 42L,
) { ) {
/** /**
* Forces all RealmSessionStoreMigration instances to be equal. * Forces all RealmSessionStoreMigration instances to be equal.
@ -117,5 +118,6 @@ internal class RealmSessionStoreMigration @Inject constructor(
if (oldVersion < 39) MigrateSessionTo039(realm).perform() if (oldVersion < 39) MigrateSessionTo039(realm).perform()
if (oldVersion < 40) MigrateSessionTo040(realm).perform() if (oldVersion < 40) MigrateSessionTo040(realm).perform()
if (oldVersion < 41) MigrateSessionTo041(realm).perform() if (oldVersion < 41) MigrateSessionTo041(realm).perform()
if (oldVersion < 42) MigrateSessionTo042(realm).perform()
} }
} }

View file

@ -45,7 +45,8 @@ internal object HomeServerCapabilitiesMapper {
canUseThreading = entity.canUseThreading, canUseThreading = entity.canUseThreading,
canControlLogoutDevices = entity.canControlLogoutDevices, canControlLogoutDevices = entity.canControlLogoutDevices,
canLoginWithQrCode = entity.canLoginWithQrCode, canLoginWithQrCode = entity.canLoginWithQrCode,
canUseThreadReadReceiptsAndNotifications = entity.canUseThreadReadReceiptsAndNotifications canUseThreadReadReceiptsAndNotifications = entity.canUseThreadReadReceiptsAndNotifications,
canRemotelyTogglePushNotificationsOfDevices = entity.canRemotelyTogglePushNotificationsOfDevices,
) )
} }

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.database.migration
import io.realm.DynamicRealm
import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntityFields
import org.matrix.android.sdk.internal.extensions.forceRefreshOfHomeServerCapabilities
import org.matrix.android.sdk.internal.util.database.RealmMigrator
internal class MigrateSessionTo042(realm: DynamicRealm) : RealmMigrator(realm, 42) {
override fun doMigrate(realm: DynamicRealm) {
realm.schema.get("HomeServerCapabilitiesEntity")
?.addField(HomeServerCapabilitiesEntityFields.CAN_REMOTELY_TOGGLE_PUSH_NOTIFICATIONS_OF_DEVICES, Boolean::class.java)
?.forceRefreshOfHomeServerCapabilities()
}
}

View file

@ -33,6 +33,7 @@ internal open class HomeServerCapabilitiesEntity(
var canControlLogoutDevices: Boolean = false, var canControlLogoutDevices: Boolean = false,
var canLoginWithQrCode: Boolean = false, var canLoginWithQrCode: Boolean = false,
var canUseThreadReadReceiptsAndNotifications: Boolean = false, var canUseThreadReadReceiptsAndNotifications: Boolean = false,
var canRemotelyTogglePushNotificationsOfDevices: Boolean = false,
) : RealmObject() { ) : RealmObject() {
companion object companion object

View file

@ -25,6 +25,7 @@ import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
import org.matrix.android.sdk.internal.auth.version.Versions import org.matrix.android.sdk.internal.auth.version.Versions
import org.matrix.android.sdk.internal.auth.version.doesServerSupportLogoutDevices import org.matrix.android.sdk.internal.auth.version.doesServerSupportLogoutDevices
import org.matrix.android.sdk.internal.auth.version.doesServerSupportQrCodeLogin import org.matrix.android.sdk.internal.auth.version.doesServerSupportQrCodeLogin
import org.matrix.android.sdk.internal.auth.version.doesServerSupportRemoteToggleOfPushNotifications
import org.matrix.android.sdk.internal.auth.version.doesServerSupportThreadUnreadNotifications import org.matrix.android.sdk.internal.auth.version.doesServerSupportThreadUnreadNotifications
import org.matrix.android.sdk.internal.auth.version.doesServerSupportThreads import org.matrix.android.sdk.internal.auth.version.doesServerSupportThreads
import org.matrix.android.sdk.internal.auth.version.isLoginAndRegistrationSupportedBySdk import org.matrix.android.sdk.internal.auth.version.isLoginAndRegistrationSupportedBySdk
@ -141,13 +142,18 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
} }
if (getVersionResult != null) { if (getVersionResult != null) {
homeServerCapabilitiesEntity.lastVersionIdentityServerSupported = getVersionResult.isLoginAndRegistrationSupportedBySdk() homeServerCapabilitiesEntity.lastVersionIdentityServerSupported =
homeServerCapabilitiesEntity.canControlLogoutDevices = getVersionResult.doesServerSupportLogoutDevices() getVersionResult.isLoginAndRegistrationSupportedBySdk()
homeServerCapabilitiesEntity.canControlLogoutDevices =
getVersionResult.doesServerSupportLogoutDevices()
homeServerCapabilitiesEntity.canUseThreading = /* capabilities?.threads?.enabled.orFalse() || */ homeServerCapabilitiesEntity.canUseThreading = /* capabilities?.threads?.enabled.orFalse() || */
getVersionResult.doesServerSupportThreads() getVersionResult.doesServerSupportThreads()
homeServerCapabilitiesEntity.canUseThreadReadReceiptsAndNotifications = homeServerCapabilitiesEntity.canUseThreadReadReceiptsAndNotifications =
getVersionResult.doesServerSupportThreadUnreadNotifications() getVersionResult.doesServerSupportThreadUnreadNotifications()
homeServerCapabilitiesEntity.canLoginWithQrCode = getVersionResult.doesServerSupportQrCodeLogin() homeServerCapabilitiesEntity.canLoginWithQrCode =
getVersionResult.doesServerSupportQrCodeLogin()
homeServerCapabilitiesEntity.canRemotelyTogglePushNotificationsOfDevices =
getVersionResult.doesServerSupportRemoteToggleOfPushNotifications()
} }
if (getWellknownResult != null && getWellknownResult is WellknownResult.Prompt) { if (getWellknownResult != null && getWellknownResult is WellknownResult.Prompt) {

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.settings.devices.v2.notification
import im.vector.app.core.di.ActiveSessionHolder
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
import javax.inject.Inject
class CheckIfCanTogglePushNotificationsViaAccountDataUseCase @Inject constructor(
private val activeSessionHolder: ActiveSessionHolder,
) {
fun execute(deviceId: String): Boolean {
return activeSessionHolder
.getSafeActiveSession()
?.accountDataService()
?.getUserAccountDataEvent(UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId) != null
}
}

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.settings.devices.v2.notification
import im.vector.app.core.di.ActiveSessionHolder
import org.matrix.android.sdk.api.extensions.orFalse
import javax.inject.Inject
class CheckIfCanTogglePushNotificationsViaPusherUseCase @Inject constructor(
private val activeSessionHolder: ActiveSessionHolder,
) {
fun execute(): Boolean {
return activeSessionHolder
.getSafeActiveSession()
?.homeServerCapabilitiesService()
?.getHomeServerCapabilities()
?.canRemotelyTogglePushNotificationsOfDevices
.orFalse()
}
}

View file

@ -0,0 +1,60 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.settings.devices.v2.notification
import im.vector.app.core.di.ActiveSessionHolder
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.flow.flow
import org.matrix.android.sdk.flow.unwrap
import javax.inject.Inject
class GetNotificationsStatusUseCase @Inject constructor(
private val activeSessionHolder: ActiveSessionHolder,
private val checkIfCanTogglePushNotificationsViaPusherUseCase: CheckIfCanTogglePushNotificationsViaPusherUseCase,
private val checkIfCanTogglePushNotificationsViaAccountDataUseCase: CheckIfCanTogglePushNotificationsViaAccountDataUseCase,
) {
fun execute(deviceId: String): Flow<NotificationsStatus> {
val session = activeSessionHolder.getSafeActiveSession()
return when {
session == null -> flowOf(NotificationsStatus.NOT_SUPPORTED)
checkIfCanTogglePushNotificationsViaAccountDataUseCase.execute(deviceId) -> {
session.flow()
.liveUserAccountData(UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId)
.unwrap()
.map { it.content.toModel<LocalNotificationSettingsContent>()?.isSilenced?.not() }
.map { if (it == true) NotificationsStatus.ENABLED else NotificationsStatus.DISABLED }
.distinctUntilChanged()
}
checkIfCanTogglePushNotificationsViaPusherUseCase.execute() -> {
session.flow()
.livePushers()
.map { it.filter { pusher -> pusher.deviceId == deviceId } }
.map { it.takeIf { it.isNotEmpty() }?.any { pusher -> pusher.enabled } }
.map { if (it == true) NotificationsStatus.ENABLED else NotificationsStatus.DISABLED }
.distinctUntilChanged()
}
else -> flowOf(NotificationsStatus.NOT_SUPPORTED)
}
}
}

View file

@ -0,0 +1,23 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.settings.devices.v2.notification
enum class NotificationsStatus {
ENABLED,
DISABLED,
NOT_SUPPORTED,
}

View file

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package im.vector.app.features.settings.devices.v2.overview package im.vector.app.features.settings.devices.v2.notification
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent
@ -24,17 +24,21 @@ import javax.inject.Inject
class TogglePushNotificationUseCase @Inject constructor( class TogglePushNotificationUseCase @Inject constructor(
private val activeSessionHolder: ActiveSessionHolder, private val activeSessionHolder: ActiveSessionHolder,
private val checkIfCanTogglePushNotificationsViaPusherUseCase: CheckIfCanTogglePushNotificationsViaPusherUseCase,
private val checkIfCanTogglePushNotificationsViaAccountDataUseCase: CheckIfCanTogglePushNotificationsViaAccountDataUseCase,
) { ) {
suspend fun execute(deviceId: String, enabled: Boolean) { suspend fun execute(deviceId: String, enabled: Boolean) {
val session = activeSessionHolder.getSafeActiveSession() ?: return val session = activeSessionHolder.getSafeActiveSession() ?: return
if (checkIfCanTogglePushNotificationsViaPusherUseCase.execute()) {
val devicePusher = session.pushersService().getPushers().firstOrNull { it.deviceId == deviceId } val devicePusher = session.pushersService().getPushers().firstOrNull { it.deviceId == deviceId }
devicePusher?.let { pusher -> devicePusher?.let { pusher ->
session.pushersService().togglePusher(pusher, enabled) session.pushersService().togglePusher(pusher, enabled)
} }
}
val accountData = session.accountDataService().getUserAccountDataEvent(UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId) if (checkIfCanTogglePushNotificationsViaAccountDataUseCase.execute(deviceId)) {
if (accountData != null) {
val newNotificationSettingsContent = LocalNotificationSettingsContent(isSilenced = !enabled) val newNotificationSettingsContent = LocalNotificationSettingsContent(isSilenced = !enabled)
session.accountDataService().updateUserAccountData( session.accountDataService().updateUserAccountData(
UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId, UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId,

View file

@ -24,6 +24,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.airbnb.mvrx.Success import com.airbnb.mvrx.Success
import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.fragmentViewModel
@ -43,6 +44,7 @@ import im.vector.app.features.auth.ReAuthActivity
import im.vector.app.features.crypto.recover.SetupMode import im.vector.app.features.crypto.recover.SetupMode
import im.vector.app.features.settings.devices.v2.list.SessionInfoViewState import im.vector.app.features.settings.devices.v2.list.SessionInfoViewState
import im.vector.app.features.settings.devices.v2.more.SessionLearnMoreBottomSheet import im.vector.app.features.settings.devices.v2.more.SessionLearnMoreBottomSheet
import im.vector.app.features.settings.devices.v2.notification.NotificationsStatus
import im.vector.app.features.workers.signout.SignOutUiWorker import im.vector.app.features.workers.signout.SignOutUiWorker
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orFalse
@ -177,7 +179,7 @@ class SessionOverviewFragment :
updateEntryDetails(state.deviceId) updateEntryDetails(state.deviceId)
updateSessionInfo(state) updateSessionInfo(state)
updateLoading(state.isLoading) updateLoading(state.isLoading)
updatePushNotificationToggle(state.deviceId, state.notificationsEnabled) updatePushNotificationToggle(state.deviceId, state.notificationsStatus)
} }
private fun updateToolbar(viewState: SessionOverviewViewState) { private fun updateToolbar(viewState: SessionOverviewViewState) {
@ -218,16 +220,20 @@ class SessionOverviewFragment :
} }
} }
private fun updatePushNotificationToggle(deviceId: String, enabled: Boolean) { private fun updatePushNotificationToggle(deviceId: String, notificationsStatus: NotificationsStatus) {
views.sessionOverviewPushNotifications.apply { views.sessionOverviewPushNotifications.isGone = notificationsStatus == NotificationsStatus.NOT_SUPPORTED
setOnCheckedChangeListener(null) when (notificationsStatus) {
setChecked(enabled) NotificationsStatus.ENABLED, NotificationsStatus.DISABLED -> {
post { views.sessionOverviewPushNotifications.setOnCheckedChangeListener(null)
setOnCheckedChangeListener { _, isChecked -> views.sessionOverviewPushNotifications.setChecked(notificationsStatus == NotificationsStatus.ENABLED)
views.sessionOverviewPushNotifications.post {
views.sessionOverviewPushNotifications.setOnCheckedChangeListener { _, isChecked ->
viewModel.handle(SessionOverviewAction.TogglePushNotifications(deviceId, isChecked)) viewModel.handle(SessionOverviewAction.TogglePushNotifications(deviceId, isChecked))
} }
} }
} }
else -> Unit
}
} }
private fun updateLoading(isLoading: Boolean) { private fun updateLoading(isLoading: Boolean) {

View file

@ -29,6 +29,8 @@ import im.vector.app.core.resources.StringProvider
import im.vector.app.features.auth.PendingAuthHandler import im.vector.app.features.auth.PendingAuthHandler
import im.vector.app.features.settings.devices.v2.RefreshDevicesUseCase import im.vector.app.features.settings.devices.v2.RefreshDevicesUseCase
import im.vector.app.features.settings.devices.v2.VectorSessionsListViewModel import im.vector.app.features.settings.devices.v2.VectorSessionsListViewModel
import im.vector.app.features.settings.devices.v2.notification.GetNotificationsStatusUseCase
import im.vector.app.features.settings.devices.v2.notification.TogglePushNotificationUseCase
import im.vector.app.features.settings.devices.v2.signout.InterceptSignoutFlowResponseUseCase import im.vector.app.features.settings.devices.v2.signout.InterceptSignoutFlowResponseUseCase
import im.vector.app.features.settings.devices.v2.signout.SignoutSessionResult import im.vector.app.features.settings.devices.v2.signout.SignoutSessionResult
import im.vector.app.features.settings.devices.v2.signout.SignoutSessionUseCase import im.vector.app.features.settings.devices.v2.signout.SignoutSessionUseCase
@ -36,21 +38,15 @@ import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSes
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent
import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.api.auth.UIABaseAuth
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth
import org.matrix.android.sdk.flow.flow
import org.matrix.android.sdk.flow.unwrap
import timber.log.Timber import timber.log.Timber
import javax.net.ssl.HttpsURLConnection import javax.net.ssl.HttpsURLConnection
import kotlin.coroutines.Continuation import kotlin.coroutines.Continuation
@ -65,6 +61,7 @@ class SessionOverviewViewModel @AssistedInject constructor(
private val pendingAuthHandler: PendingAuthHandler, private val pendingAuthHandler: PendingAuthHandler,
private val activeSessionHolder: ActiveSessionHolder, private val activeSessionHolder: ActiveSessionHolder,
private val togglePushNotificationUseCase: TogglePushNotificationUseCase, private val togglePushNotificationUseCase: TogglePushNotificationUseCase,
private val getNotificationsStatusUseCase: GetNotificationsStatusUseCase,
refreshDevicesUseCase: RefreshDevicesUseCase, refreshDevicesUseCase: RefreshDevicesUseCase,
) : VectorSessionsListViewModel<SessionOverviewViewState, SessionOverviewAction, SessionOverviewViewEvent>( ) : VectorSessionsListViewModel<SessionOverviewViewState, SessionOverviewAction, SessionOverviewViewEvent>(
initialState, activeSessionHolder, refreshDevicesUseCase initialState, activeSessionHolder, refreshDevicesUseCase
@ -81,7 +78,7 @@ class SessionOverviewViewModel @AssistedInject constructor(
refreshPushers() refreshPushers()
observeSessionInfo(initialState.deviceId) observeSessionInfo(initialState.deviceId)
observeCurrentSessionInfo() observeCurrentSessionInfo()
observePushers(initialState.deviceId) observeNotificationsStatus(initialState.deviceId)
} }
private fun refreshPushers() { private fun refreshPushers() {
@ -107,20 +104,9 @@ class SessionOverviewViewModel @AssistedInject constructor(
} }
} }
private fun observePushers(deviceId: String) { private fun observeNotificationsStatus(deviceId: String) {
val session = activeSessionHolder.getSafeActiveSession() ?: return getNotificationsStatusUseCase.execute(deviceId)
val pusherFlow = session.flow() .onEach { setState { copy(notificationsStatus = it) } }
.livePushers()
.map { it.filter { pusher -> pusher.deviceId == deviceId } }
.map { it.takeIf { it.isNotEmpty() }?.any { pusher -> pusher.enabled } }
val accountDataFlow = session.flow()
.liveUserAccountData(TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId)
.unwrap()
.map { it.content.toModel<LocalNotificationSettingsContent>()?.isSilenced?.not() }
merge(pusherFlow, accountDataFlow)
.onEach { it?.let { setState { copy(notificationsEnabled = it) } } }
.launchIn(viewModelScope) .launchIn(viewModelScope)
} }
@ -233,7 +219,6 @@ class SessionOverviewViewModel @AssistedInject constructor(
private fun handleTogglePusherAction(action: SessionOverviewAction.TogglePushNotifications) { private fun handleTogglePusherAction(action: SessionOverviewAction.TogglePushNotifications) {
viewModelScope.launch { viewModelScope.launch {
togglePushNotificationUseCase.execute(action.deviceId, action.enabled) togglePushNotificationUseCase.execute(action.deviceId, action.enabled)
setState { copy(notificationsEnabled = action.enabled) }
} }
} }
} }

View file

@ -20,13 +20,14 @@ import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksState
import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.Uninitialized
import im.vector.app.features.settings.devices.v2.DeviceFullInfo import im.vector.app.features.settings.devices.v2.DeviceFullInfo
import im.vector.app.features.settings.devices.v2.notification.NotificationsStatus
data class SessionOverviewViewState( data class SessionOverviewViewState(
val deviceId: String, val deviceId: String,
val isCurrentSessionTrusted: Boolean = false, val isCurrentSessionTrusted: Boolean = false,
val deviceInfo: Async<DeviceFullInfo> = Uninitialized, val deviceInfo: Async<DeviceFullInfo> = Uninitialized,
val isLoading: Boolean = false, val isLoading: Boolean = false,
val notificationsEnabled: Boolean = false, val notificationsStatus: NotificationsStatus = NotificationsStatus.NOT_SUPPORTED,
) : MavericksState { ) : MavericksState {
constructor(args: SessionOverviewArgs) : this( constructor(args: SessionOverviewArgs) : this(
deviceId = args.deviceId deviceId = args.deviceId

View file

@ -0,0 +1,71 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.settings.devices.v2.notification
import im.vector.app.test.fakes.FakeActiveSessionHolder
import io.mockk.mockk
import org.amshove.kluent.shouldBeEqualTo
import org.junit.Test
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
private const val A_DEVICE_ID = "device-id"
class CheckIfCanTogglePushNotificationsViaAccountDataUseCaseTest {
private val fakeActiveSessionHolder = FakeActiveSessionHolder()
private val checkIfCanTogglePushNotificationsViaAccountDataUseCase =
CheckIfCanTogglePushNotificationsViaAccountDataUseCase(
activeSessionHolder = fakeActiveSessionHolder.instance,
)
@Test
fun `given current session and an account data for the device id when execute then result is true`() {
// Given
fakeActiveSessionHolder
.fakeSession
.accountDataService()
.givenGetUserAccountDataEventReturns(
type = UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + A_DEVICE_ID,
content = mockk(),
)
// When
val result = checkIfCanTogglePushNotificationsViaAccountDataUseCase.execute(A_DEVICE_ID)
// Then
result shouldBeEqualTo true
}
@Test
fun `given current session and NO account data for the device id when execute then result is false`() {
// Given
fakeActiveSessionHolder
.fakeSession
.accountDataService()
.givenGetUserAccountDataEventReturns(
type = UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + A_DEVICE_ID,
content = null,
)
// When
val result = checkIfCanTogglePushNotificationsViaAccountDataUseCase.execute(A_DEVICE_ID)
// Then
result shouldBeEqualTo false
}
}

View file

@ -0,0 +1,61 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.settings.devices.v2.notification
import im.vector.app.test.fakes.FakeActiveSessionHolder
import im.vector.app.test.fixtures.aHomeServerCapabilities
import org.amshove.kluent.shouldBeEqualTo
import org.junit.Test
private val A_HOMESERVER_CAPABILITIES = aHomeServerCapabilities(canRemotelyTogglePushNotificationsOfDevices = true)
class CheckIfCanTogglePushNotificationsViaPusherUseCaseTest {
private val fakeActiveSessionHolder = FakeActiveSessionHolder()
private val checkIfCanTogglePushNotificationsViaPusherUseCase =
CheckIfCanTogglePushNotificationsViaPusherUseCase(
activeSessionHolder = fakeActiveSessionHolder.instance,
)
@Test
fun `given current session when execute then toggle capability is returned`() {
// Given
fakeActiveSessionHolder
.fakeSession
.fakeHomeServerCapabilitiesService
.givenCapabilities(A_HOMESERVER_CAPABILITIES)
// When
val result = checkIfCanTogglePushNotificationsViaPusherUseCase.execute()
// Then
result shouldBeEqualTo A_HOMESERVER_CAPABILITIES.canRemotelyTogglePushNotificationsOfDevices
}
@Test
fun `given no current session when execute then false is returned`() {
// Given
fakeActiveSessionHolder.givenGetSafeActiveSessionReturns(null)
// When
val result = checkIfCanTogglePushNotificationsViaPusherUseCase.execute()
// Then
result shouldBeEqualTo false
}
}

View file

@ -0,0 +1,141 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.settings.devices.v2.notification
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import im.vector.app.test.fakes.FakeActiveSessionHolder
import im.vector.app.test.fixtures.PusherFixture
import im.vector.app.test.testDispatcher
import io.mockk.every
import io.mockk.mockk
import io.mockk.verifyOrder
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import org.amshove.kluent.shouldBeEqualTo
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
import org.matrix.android.sdk.api.session.events.model.toContent
private const val A_DEVICE_ID = "device-id"
class GetNotificationsStatusUseCaseTest {
@get:Rule
val instantTaskExecutorRule = InstantTaskExecutorRule()
private val fakeActiveSessionHolder = FakeActiveSessionHolder()
private val fakeCheckIfCanTogglePushNotificationsViaPusherUseCase =
mockk<CheckIfCanTogglePushNotificationsViaPusherUseCase>()
private val fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase =
mockk<CheckIfCanTogglePushNotificationsViaAccountDataUseCase>()
private val getNotificationsStatusUseCase =
GetNotificationsStatusUseCase(
activeSessionHolder = fakeActiveSessionHolder.instance,
checkIfCanTogglePushNotificationsViaPusherUseCase = fakeCheckIfCanTogglePushNotificationsViaPusherUseCase,
checkIfCanTogglePushNotificationsViaAccountDataUseCase = fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase,
)
@Before
fun setup() {
Dispatchers.setMain(testDispatcher)
}
@After
fun tearDown() {
Dispatchers.resetMain()
}
@Test
fun `given NO current session when execute then resulting flow contains NOT_SUPPORTED value`() = runTest {
// Given
fakeActiveSessionHolder.givenGetSafeActiveSessionReturns(null)
// When
val result = getNotificationsStatusUseCase.execute(A_DEVICE_ID)
// Then
result.firstOrNull() shouldBeEqualTo NotificationsStatus.NOT_SUPPORTED
}
@Test
fun `given current session and toggle is not supported when execute then resulting flow contains NOT_SUPPORTED value`() = runTest {
// Given
every { fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute() } returns false
every { fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase.execute(A_DEVICE_ID) } returns false
// When
val result = getNotificationsStatusUseCase.execute(A_DEVICE_ID)
// Then
result.firstOrNull() shouldBeEqualTo NotificationsStatus.NOT_SUPPORTED
verifyOrder {
// we should first check account data
fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase.execute(A_DEVICE_ID)
fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute()
}
}
@Test
fun `given current session and toggle via pusher is supported when execute then resulting flow contains status based on pusher value`() = runTest {
// Given
val pushers = listOf(
PusherFixture.aPusher(
deviceId = A_DEVICE_ID,
enabled = true,
)
)
fakeActiveSessionHolder.fakeSession.pushersService().givenPushersLive(pushers)
every { fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute() } returns true
every { fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase.execute(A_DEVICE_ID) } returns false
// When
val result = getNotificationsStatusUseCase.execute(A_DEVICE_ID)
// Then
result.firstOrNull() shouldBeEqualTo NotificationsStatus.ENABLED
}
@Test
fun `given current session and toggle via account data is supported when execute then resulting flow contains status based on settings value`() = runTest {
// Given
fakeActiveSessionHolder
.fakeSession
.accountDataService()
.givenGetUserAccountDataEventReturns(
type = UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + A_DEVICE_ID,
content = LocalNotificationSettingsContent(
isSilenced = false
).toContent(),
)
every { fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute() } returns false
every { fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase.execute(A_DEVICE_ID) } returns true
// When
val result = getNotificationsStatusUseCase.execute(A_DEVICE_ID)
// Then
result.firstOrNull() shouldBeEqualTo NotificationsStatus.ENABLED
}
}

View file

@ -14,10 +14,12 @@
* limitations under the License. * limitations under the License.
*/ */
package im.vector.app.features.settings.devices.v2.overview package im.vector.app.features.settings.devices.v2.notification
import im.vector.app.test.fakes.FakeActiveSessionHolder import im.vector.app.test.fakes.FakeActiveSessionHolder
import im.vector.app.test.fixtures.PusherFixture import im.vector.app.test.fixtures.PusherFixture
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Test import org.junit.Test
import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent
@ -27,10 +29,21 @@ import org.matrix.android.sdk.api.session.events.model.toContent
class TogglePushNotificationUseCaseTest { class TogglePushNotificationUseCaseTest {
private val activeSessionHolder = FakeActiveSessionHolder() private val activeSessionHolder = FakeActiveSessionHolder()
private val togglePushNotificationUseCase = TogglePushNotificationUseCase(activeSessionHolder.instance) private val fakeCheckIfCanTogglePushNotificationsViaPusherUseCase =
mockk<CheckIfCanTogglePushNotificationsViaPusherUseCase>()
private val fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase =
mockk<CheckIfCanTogglePushNotificationsViaAccountDataUseCase>()
private val togglePushNotificationUseCase =
TogglePushNotificationUseCase(
activeSessionHolder = activeSessionHolder.instance,
checkIfCanTogglePushNotificationsViaPusherUseCase = fakeCheckIfCanTogglePushNotificationsViaPusherUseCase,
checkIfCanTogglePushNotificationsViaAccountDataUseCase = fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase,
)
@Test @Test
fun `when execute, then toggle enabled for device pushers`() = runTest { fun `when execute, then toggle enabled for device pushers`() = runTest {
// Given
val sessionId = "a_session_id" val sessionId = "a_session_id"
val pushers = listOf( val pushers = listOf(
PusherFixture.aPusher(deviceId = sessionId, enabled = false), PusherFixture.aPusher(deviceId = sessionId, enabled = false),
@ -38,14 +51,19 @@ class TogglePushNotificationUseCaseTest {
) )
activeSessionHolder.fakeSession.pushersService().givenPushersLive(pushers) activeSessionHolder.fakeSession.pushersService().givenPushersLive(pushers)
activeSessionHolder.fakeSession.pushersService().givenGetPushers(pushers) activeSessionHolder.fakeSession.pushersService().givenGetPushers(pushers)
every { fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute() } returns true
every { fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase.execute(sessionId) } returns false
// When
togglePushNotificationUseCase.execute(sessionId, true) togglePushNotificationUseCase.execute(sessionId, true)
// Then
activeSessionHolder.fakeSession.pushersService().verifyTogglePusherCalled(pushers.first(), true) activeSessionHolder.fakeSession.pushersService().verifyTogglePusherCalled(pushers.first(), true)
} }
@Test @Test
fun `when execute, then toggle local notification settings`() = runTest { fun `when execute, then toggle local notification settings`() = runTest {
// Given
val sessionId = "a_session_id" val sessionId = "a_session_id"
val pushers = listOf( val pushers = listOf(
PusherFixture.aPusher(deviceId = sessionId, enabled = false), PusherFixture.aPusher(deviceId = sessionId, enabled = false),
@ -56,9 +74,13 @@ class TogglePushNotificationUseCaseTest {
UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + sessionId, UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + sessionId,
LocalNotificationSettingsContent(isSilenced = true).toContent() LocalNotificationSettingsContent(isSilenced = true).toContent()
) )
every { fakeCheckIfCanTogglePushNotificationsViaPusherUseCase.execute() } returns false
every { fakeCheckIfCanTogglePushNotificationsViaAccountDataUseCase.execute(sessionId) } returns true
// When
togglePushNotificationUseCase.execute(sessionId, true) togglePushNotificationUseCase.execute(sessionId, true)
// Then
activeSessionHolder.fakeSession.accountDataService().verifyUpdateUserAccountDataEventSucceeds( activeSessionHolder.fakeSession.accountDataService().verifyUpdateUserAccountDataEventSucceeds(
UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + sessionId, UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + sessionId,
LocalNotificationSettingsContent(isSilenced = false).toContent(), LocalNotificationSettingsContent(isSilenced = false).toContent(),

View file

@ -23,6 +23,8 @@ import com.airbnb.mvrx.test.MavericksTestRule
import im.vector.app.R import im.vector.app.R
import im.vector.app.features.settings.devices.v2.DeviceFullInfo import im.vector.app.features.settings.devices.v2.DeviceFullInfo
import im.vector.app.features.settings.devices.v2.RefreshDevicesUseCase import im.vector.app.features.settings.devices.v2.RefreshDevicesUseCase
import im.vector.app.features.settings.devices.v2.notification.GetNotificationsStatusUseCase
import im.vector.app.features.settings.devices.v2.notification.NotificationsStatus
import im.vector.app.features.settings.devices.v2.signout.InterceptSignoutFlowResponseUseCase import im.vector.app.features.settings.devices.v2.signout.InterceptSignoutFlowResponseUseCase
import im.vector.app.features.settings.devices.v2.signout.SignoutSessionResult import im.vector.app.features.settings.devices.v2.signout.SignoutSessionResult
import im.vector.app.features.settings.devices.v2.signout.SignoutSessionUseCase import im.vector.app.features.settings.devices.v2.signout.SignoutSessionUseCase
@ -32,7 +34,6 @@ import im.vector.app.test.fakes.FakePendingAuthHandler
import im.vector.app.test.fakes.FakeStringProvider import im.vector.app.test.fakes.FakeStringProvider
import im.vector.app.test.fakes.FakeTogglePushNotificationUseCase import im.vector.app.test.fakes.FakeTogglePushNotificationUseCase
import im.vector.app.test.fakes.FakeVerificationService import im.vector.app.test.fakes.FakeVerificationService
import im.vector.app.test.fixtures.PusherFixture.aPusher
import im.vector.app.test.test import im.vector.app.test.test
import im.vector.app.test.testDispatcher import im.vector.app.test.testDispatcher
import io.mockk.coEvery import io.mockk.coEvery
@ -87,6 +88,8 @@ class SessionOverviewViewModelTest {
private val fakePendingAuthHandler = FakePendingAuthHandler() private val fakePendingAuthHandler = FakePendingAuthHandler()
private val refreshDevicesUseCase = mockk<RefreshDevicesUseCase>() private val refreshDevicesUseCase = mockk<RefreshDevicesUseCase>()
private val togglePushNotificationUseCase = FakeTogglePushNotificationUseCase() private val togglePushNotificationUseCase = FakeTogglePushNotificationUseCase()
private val fakeGetNotificationsStatusUseCase = mockk<GetNotificationsStatusUseCase>()
private val notificationsStatus = NotificationsStatus.ENABLED
private fun createViewModel() = SessionOverviewViewModel( private fun createViewModel() = SessionOverviewViewModel(
initialState = SessionOverviewViewState(args), initialState = SessionOverviewViewState(args),
@ -99,6 +102,7 @@ class SessionOverviewViewModelTest {
activeSessionHolder = fakeActiveSessionHolder.instance, activeSessionHolder = fakeActiveSessionHolder.instance,
refreshDevicesUseCase = refreshDevicesUseCase, refreshDevicesUseCase = refreshDevicesUseCase,
togglePushNotificationUseCase = togglePushNotificationUseCase.instance, togglePushNotificationUseCase = togglePushNotificationUseCase.instance,
getNotificationsStatusUseCase = fakeGetNotificationsStatusUseCase,
) )
@Before @Before
@ -108,6 +112,7 @@ class SessionOverviewViewModelTest {
every { SystemClock.elapsedRealtime() } returns 1234 every { SystemClock.elapsedRealtime() } returns 1234
givenVerificationService() givenVerificationService()
every { fakeGetNotificationsStatusUseCase.execute(A_SESSION_ID_1) } returns flowOf(notificationsStatus)
} }
@After @After
@ -131,7 +136,7 @@ class SessionOverviewViewModelTest {
deviceId = A_SESSION_ID_1, deviceId = A_SESSION_ID_1,
deviceInfo = Success(deviceFullInfo), deviceInfo = Success(deviceFullInfo),
isCurrentSessionTrusted = true, isCurrentSessionTrusted = true,
notificationsEnabled = true, notificationsStatus = notificationsStatus,
) )
val viewModel = createViewModel() val viewModel = createViewModel()
@ -227,7 +232,7 @@ class SessionOverviewViewModelTest {
isCurrentSessionTrusted = true, isCurrentSessionTrusted = true,
deviceInfo = Success(deviceFullInfo), deviceInfo = Success(deviceFullInfo),
isLoading = false, isLoading = false,
notificationsEnabled = true, notificationsStatus = notificationsStatus,
) )
// When // When
@ -264,7 +269,7 @@ class SessionOverviewViewModelTest {
isCurrentSessionTrusted = true, isCurrentSessionTrusted = true,
deviceInfo = Success(deviceFullInfo), deviceInfo = Success(deviceFullInfo),
isLoading = false, isLoading = false,
notificationsEnabled = true, notificationsStatus = notificationsStatus,
) )
fakeStringProvider.given(R.string.authentication_error, AUTH_ERROR_MESSAGE) fakeStringProvider.given(R.string.authentication_error, AUTH_ERROR_MESSAGE)
@ -299,7 +304,7 @@ class SessionOverviewViewModelTest {
isCurrentSessionTrusted = true, isCurrentSessionTrusted = true,
deviceInfo = Success(deviceFullInfo), deviceInfo = Success(deviceFullInfo),
isLoading = false, isLoading = false,
notificationsEnabled = true, notificationsStatus = notificationsStatus,
) )
fakeStringProvider.given(R.string.matrix_error, AN_ERROR_MESSAGE) fakeStringProvider.given(R.string.matrix_error, AN_ERROR_MESSAGE)
@ -466,13 +471,13 @@ class SessionOverviewViewModelTest {
@Test @Test
fun `when viewModel init, then observe pushers and emit to state`() { fun `when viewModel init, then observe pushers and emit to state`() {
val pushers = listOf(aPusher(deviceId = A_SESSION_ID_1)) val notificationStatus = NotificationsStatus.ENABLED
fakeActiveSessionHolder.fakeSession.pushersService().givenPushersLive(pushers) every { fakeGetNotificationsStatusUseCase.execute(A_SESSION_ID_1) } returns flowOf(notificationStatus)
val viewModel = createViewModel() val viewModel = createViewModel()
viewModel.test() viewModel.test()
.assertLatestState { state -> state.notificationsEnabled } .assertLatestState { state -> state.notificationsStatus == notificationStatus }
.finish() .finish()
} }
@ -483,6 +488,6 @@ class SessionOverviewViewModelTest {
viewModel.handle(SessionOverviewAction.TogglePushNotifications(A_SESSION_ID_1, true)) viewModel.handle(SessionOverviewAction.TogglePushNotifications(A_SESSION_ID_1, true))
togglePushNotificationUseCase.verifyExecute(A_SESSION_ID_1, true) togglePushNotificationUseCase.verifyExecute(A_SESSION_ID_1, true)
viewModel.test().assertLatestState { state -> state.notificationsEnabled }.finish() viewModel.test().assertLatestState { state -> state.notificationsStatus == NotificationsStatus.ENABLED }.finish()
} }
} }

View file

@ -28,8 +28,8 @@ import org.matrix.android.sdk.api.session.events.model.Content
class FakeSessionAccountDataService : SessionAccountDataService by mockk(relaxed = true) { class FakeSessionAccountDataService : SessionAccountDataService by mockk(relaxed = true) {
fun givenGetUserAccountDataEventReturns(type: String, content: Content) { fun givenGetUserAccountDataEventReturns(type: String, content: Content?) {
every { getUserAccountDataEvent(type) } returns UserAccountDataEvent(type, content) every { getUserAccountDataEvent(type) } returns content?.let { UserAccountDataEvent(type, it) }
} }
fun givenUpdateUserAccountDataEventSucceeds() { fun givenUpdateUserAccountDataEventSucceeds() {

View file

@ -16,7 +16,7 @@
package im.vector.app.test.fakes package im.vector.app.test.fakes
import im.vector.app.features.settings.devices.v2.overview.TogglePushNotificationUseCase import im.vector.app.features.settings.devices.v2.notification.TogglePushNotificationUseCase
import io.mockk.coJustRun import io.mockk.coJustRun
import io.mockk.coVerify import io.mockk.coVerify
import io.mockk.mockk import io.mockk.mockk

View file

@ -27,14 +27,16 @@ fun aHomeServerCapabilities(
maxUploadFileSize: Long = 100L, maxUploadFileSize: Long = 100L,
lastVersionIdentityServerSupported: Boolean = false, lastVersionIdentityServerSupported: Boolean = false,
defaultIdentityServerUrl: String? = null, defaultIdentityServerUrl: String? = null,
roomVersions: RoomVersionCapabilities? = null roomVersions: RoomVersionCapabilities? = null,
canRemotelyTogglePushNotificationsOfDevices: Boolean = true,
) = HomeServerCapabilities( ) = HomeServerCapabilities(
canChangePassword, canChangePassword = canChangePassword,
canChangeDisplayName, canChangeDisplayName = canChangeDisplayName,
canChangeAvatar, canChangeAvatar = canChangeAvatar,
canChange3pid, canChange3pid = canChange3pid,
maxUploadFileSize, maxUploadFileSize = maxUploadFileSize,
lastVersionIdentityServerSupported, lastVersionIdentityServerSupported = lastVersionIdentityServerSupported,
defaultIdentityServerUrl, defaultIdentityServerUrl = defaultIdentityServerUrl,
roomVersions roomVersions = roomVersions,
canRemotelyTogglePushNotificationsOfDevices = canRemotelyTogglePushNotificationsOfDevices,
) )