Device Manager Notification and Pusher Fixes (#7370)
* Fixes existing pushers being overwritten on app startup * Refreshes pushers in SessionOverview screen * Fixes push toggle not working for non pusher sessions * Various code improvements * Further code improvements for safety * Fixes CI errors
This commit is contained in:
parent
ba49ee4e13
commit
75c97bc7c5
|
@ -218,7 +218,7 @@ class HomeActivity :
|
||||||
fcmHelper.ensureFcmTokenIsRetrieved(
|
fcmHelper.ensureFcmTokenIsRetrieved(
|
||||||
this,
|
this,
|
||||||
pushersManager,
|
pushersManager,
|
||||||
vectorPreferences.areNotificationEnabledForDevice()
|
homeActivityViewModel.shouldAddHttpPusher()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,6 +143,14 @@ class HomeActivityViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun shouldAddHttpPusher() = if (vectorPreferences.areNotificationEnabledForDevice()) {
|
||||||
|
val currentSession = activeSessionHolder.getActiveSession()
|
||||||
|
val currentPushers = currentSession.pushersService().getPushers()
|
||||||
|
currentPushers.none { it.deviceId == currentSession.sessionParams.deviceId }
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fun observeLocalNotificationsSilenced() {
|
fun observeLocalNotificationsSilenced() {
|
||||||
val currentSession = activeSessionHolder.getActiveSession()
|
val currentSession = activeSessionHolder.getActiveSession()
|
||||||
val deviceId = currentSession.cryptoService().getMyDevice().deviceId
|
val deviceId = currentSession.cryptoService().getMyDevice().deviceId
|
||||||
|
|
|
@ -47,7 +47,6 @@ 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
|
||||||
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.pushers.Pusher
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -178,7 +177,7 @@ class SessionOverviewFragment :
|
||||||
updateEntryDetails(state.deviceId)
|
updateEntryDetails(state.deviceId)
|
||||||
updateSessionInfo(state)
|
updateSessionInfo(state)
|
||||||
updateLoading(state.isLoading)
|
updateLoading(state.isLoading)
|
||||||
updatePushNotificationToggle(state.deviceId, state.pushers.invoke().orEmpty())
|
updatePushNotificationToggle(state.deviceId, state.notificationsEnabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateToolbar(viewState: SessionOverviewViewState) {
|
private fun updateToolbar(viewState: SessionOverviewViewState) {
|
||||||
|
@ -219,14 +218,10 @@ class SessionOverviewFragment :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updatePushNotificationToggle(deviceId: String, pushers: List<Pusher>) {
|
private fun updatePushNotificationToggle(deviceId: String, enabled: Boolean) {
|
||||||
views.sessionOverviewPushNotifications.apply {
|
views.sessionOverviewPushNotifications.apply {
|
||||||
if (pushers.isEmpty()) {
|
|
||||||
isVisible = false
|
|
||||||
} else {
|
|
||||||
val allPushersAreEnabled = pushers.all { it.enabled }
|
|
||||||
setOnCheckedChangeListener(null)
|
setOnCheckedChangeListener(null)
|
||||||
setChecked(allPushersAreEnabled)
|
setChecked(enabled)
|
||||||
post {
|
post {
|
||||||
setOnCheckedChangeListener { _, isChecked ->
|
setOnCheckedChangeListener { _, isChecked ->
|
||||||
viewModel.handle(SessionOverviewAction.TogglePushNotifications(deviceId, isChecked))
|
viewModel.handle(SessionOverviewAction.TogglePushNotifications(deviceId, isChecked))
|
||||||
|
@ -234,7 +229,6 @@ class SessionOverviewFragment :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateLoading(isLoading: Boolean) {
|
private fun updateLoading(isLoading: Boolean) {
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
|
|
|
@ -36,24 +36,27 @@ 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.Session
|
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.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
|
||||||
|
|
||||||
class SessionOverviewViewModel @AssistedInject constructor(
|
class SessionOverviewViewModel @AssistedInject constructor(
|
||||||
@Assisted val initialState: SessionOverviewViewState,
|
@Assisted val initialState: SessionOverviewViewState,
|
||||||
private val session: Session,
|
|
||||||
private val stringProvider: StringProvider,
|
private val stringProvider: StringProvider,
|
||||||
private val getDeviceFullInfoUseCase: GetDeviceFullInfoUseCase,
|
private val getDeviceFullInfoUseCase: GetDeviceFullInfoUseCase,
|
||||||
private val checkIfCurrentSessionCanBeVerifiedUseCase: CheckIfCurrentSessionCanBeVerifiedUseCase,
|
private val checkIfCurrentSessionCanBeVerifiedUseCase: CheckIfCurrentSessionCanBeVerifiedUseCase,
|
||||||
|
@ -61,6 +64,7 @@ class SessionOverviewViewModel @AssistedInject constructor(
|
||||||
private val interceptSignoutFlowResponseUseCase: InterceptSignoutFlowResponseUseCase,
|
private val interceptSignoutFlowResponseUseCase: InterceptSignoutFlowResponseUseCase,
|
||||||
private val pendingAuthHandler: PendingAuthHandler,
|
private val pendingAuthHandler: PendingAuthHandler,
|
||||||
private val activeSessionHolder: ActiveSessionHolder,
|
private val activeSessionHolder: ActiveSessionHolder,
|
||||||
|
private val togglePushNotificationUseCase: TogglePushNotificationUseCase,
|
||||||
refreshDevicesUseCase: RefreshDevicesUseCase,
|
refreshDevicesUseCase: RefreshDevicesUseCase,
|
||||||
) : VectorSessionsListViewModel<SessionOverviewViewState, SessionOverviewAction, SessionOverviewViewEvent>(
|
) : VectorSessionsListViewModel<SessionOverviewViewState, SessionOverviewAction, SessionOverviewViewEvent>(
|
||||||
initialState, activeSessionHolder, refreshDevicesUseCase
|
initialState, activeSessionHolder, refreshDevicesUseCase
|
||||||
|
@ -74,11 +78,16 @@ class SessionOverviewViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
refreshPushers()
|
||||||
observeSessionInfo(initialState.deviceId)
|
observeSessionInfo(initialState.deviceId)
|
||||||
observeCurrentSessionInfo()
|
observeCurrentSessionInfo()
|
||||||
observePushers(initialState.deviceId)
|
observePushers(initialState.deviceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun refreshPushers() {
|
||||||
|
activeSessionHolder.getSafeActiveSession()?.pushersService()?.refreshPushers()
|
||||||
|
}
|
||||||
|
|
||||||
private fun observeSessionInfo(deviceId: String) {
|
private fun observeSessionInfo(deviceId: String) {
|
||||||
getDeviceFullInfoUseCase.execute(deviceId)
|
getDeviceFullInfoUseCase.execute(deviceId)
|
||||||
.onEach { setState { copy(deviceInfo = Success(it)) } }
|
.onEach { setState { copy(deviceInfo = Success(it)) } }
|
||||||
|
@ -99,10 +108,20 @@ class SessionOverviewViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observePushers(deviceId: String) {
|
private fun observePushers(deviceId: String) {
|
||||||
session.flow()
|
val session = activeSessionHolder.getSafeActiveSession() ?: return
|
||||||
|
val pusherFlow = session.flow()
|
||||||
.livePushers()
|
.livePushers()
|
||||||
.map { it.filter { pusher -> pusher.deviceId == deviceId } }
|
.map { it.filter { pusher -> pusher.deviceId == deviceId } }
|
||||||
.execute { copy(pushers = it) }
|
.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)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handle(action: SessionOverviewAction) {
|
override fun handle(action: SessionOverviewAction) {
|
||||||
|
@ -213,10 +232,8 @@ class SessionOverviewViewModel @AssistedInject constructor(
|
||||||
|
|
||||||
private fun handleTogglePusherAction(action: SessionOverviewAction.TogglePushNotifications) {
|
private fun handleTogglePusherAction(action: SessionOverviewAction.TogglePushNotifications) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val devicePushers = awaitState().pushers.invoke()?.filter { it.deviceId == action.deviceId }
|
togglePushNotificationUseCase.execute(action.deviceId, action.enabled)
|
||||||
devicePushers?.forEach { pusher ->
|
setState { copy(notificationsEnabled = action.enabled) }
|
||||||
session.pushersService().togglePusher(pusher, action.enabled)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,14 +20,13 @@ 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 org.matrix.android.sdk.api.session.pushers.Pusher
|
|
||||||
|
|
||||||
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 pushers: Async<List<Pusher>> = Uninitialized,
|
val notificationsEnabled: Boolean = false,
|
||||||
) : MavericksState {
|
) : MavericksState {
|
||||||
constructor(args: SessionOverviewArgs) : this(
|
constructor(args: SessionOverviewArgs) : this(
|
||||||
deviceId = args.deviceId
|
deviceId = args.deviceId
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* 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.overview
|
||||||
|
|
||||||
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
|
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
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class TogglePushNotificationUseCase @Inject constructor(
|
||||||
|
private val activeSessionHolder: ActiveSessionHolder,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun execute(deviceId: String, enabled: Boolean) {
|
||||||
|
val session = activeSessionHolder.getSafeActiveSession() ?: return
|
||||||
|
val devicePusher = session.pushersService().getPushers().firstOrNull { it.deviceId == deviceId }
|
||||||
|
devicePusher?.let { pusher ->
|
||||||
|
session.pushersService().togglePusher(pusher, enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
val accountData = session.accountDataService().getUserAccountDataEvent(UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId)
|
||||||
|
if (accountData != null) {
|
||||||
|
val newNotificationSettingsContent = LocalNotificationSettingsContent(isSilenced = !enabled)
|
||||||
|
session.accountDataService().updateUserAccountData(
|
||||||
|
UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId,
|
||||||
|
newNotificationSettingsContent.toContent(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -114,6 +114,6 @@ class PushersManagerTest {
|
||||||
|
|
||||||
pushersManager.togglePusherForCurrentSession(true)
|
pushersManager.togglePusherForCurrentSession(true)
|
||||||
|
|
||||||
pushersService.verifyOnlyGetPushersAndTogglePusherCalled(pusher, true)
|
pushersService.verifyTogglePusherCalled(pusher, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ package im.vector.app.features.settings.devices.v2.overview
|
||||||
|
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||||
import com.airbnb.mvrx.Loading
|
|
||||||
import com.airbnb.mvrx.Success
|
import com.airbnb.mvrx.Success
|
||||||
import com.airbnb.mvrx.test.MavericksTestRule
|
import com.airbnb.mvrx.test.MavericksTestRule
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
|
@ -30,8 +29,8 @@ import im.vector.app.features.settings.devices.v2.signout.SignoutSessionUseCase
|
||||||
import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSessionCanBeVerifiedUseCase
|
import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSessionCanBeVerifiedUseCase
|
||||||
import im.vector.app.test.fakes.FakeActiveSessionHolder
|
import im.vector.app.test.fakes.FakeActiveSessionHolder
|
||||||
import im.vector.app.test.fakes.FakePendingAuthHandler
|
import im.vector.app.test.fakes.FakePendingAuthHandler
|
||||||
import im.vector.app.test.fakes.FakeSession
|
|
||||||
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.FakeVerificationService
|
import im.vector.app.test.fakes.FakeVerificationService
|
||||||
import im.vector.app.test.fixtures.PusherFixture.aPusher
|
import im.vector.app.test.fixtures.PusherFixture.aPusher
|
||||||
import im.vector.app.test.test
|
import im.vector.app.test.test
|
||||||
|
@ -79,7 +78,6 @@ class SessionOverviewViewModelTest {
|
||||||
private val args = SessionOverviewArgs(
|
private val args = SessionOverviewArgs(
|
||||||
deviceId = A_SESSION_ID_1
|
deviceId = A_SESSION_ID_1
|
||||||
)
|
)
|
||||||
private val fakeSession = FakeSession()
|
|
||||||
private val getDeviceFullInfoUseCase = mockk<GetDeviceFullInfoUseCase>(relaxed = true)
|
private val getDeviceFullInfoUseCase = mockk<GetDeviceFullInfoUseCase>(relaxed = true)
|
||||||
private val fakeActiveSessionHolder = FakeActiveSessionHolder()
|
private val fakeActiveSessionHolder = FakeActiveSessionHolder()
|
||||||
private val fakeStringProvider = FakeStringProvider()
|
private val fakeStringProvider = FakeStringProvider()
|
||||||
|
@ -88,10 +86,10 @@ class SessionOverviewViewModelTest {
|
||||||
private val interceptSignoutFlowResponseUseCase = mockk<InterceptSignoutFlowResponseUseCase>()
|
private val interceptSignoutFlowResponseUseCase = mockk<InterceptSignoutFlowResponseUseCase>()
|
||||||
private val fakePendingAuthHandler = FakePendingAuthHandler()
|
private val fakePendingAuthHandler = FakePendingAuthHandler()
|
||||||
private val refreshDevicesUseCase = mockk<RefreshDevicesUseCase>()
|
private val refreshDevicesUseCase = mockk<RefreshDevicesUseCase>()
|
||||||
|
private val togglePushNotificationUseCase = FakeTogglePushNotificationUseCase()
|
||||||
|
|
||||||
private fun createViewModel() = SessionOverviewViewModel(
|
private fun createViewModel() = SessionOverviewViewModel(
|
||||||
initialState = SessionOverviewViewState(args),
|
initialState = SessionOverviewViewState(args),
|
||||||
session = fakeSession,
|
|
||||||
stringProvider = fakeStringProvider.instance,
|
stringProvider = fakeStringProvider.instance,
|
||||||
getDeviceFullInfoUseCase = getDeviceFullInfoUseCase,
|
getDeviceFullInfoUseCase = getDeviceFullInfoUseCase,
|
||||||
checkIfCurrentSessionCanBeVerifiedUseCase = checkIfCurrentSessionCanBeVerifiedUseCase,
|
checkIfCurrentSessionCanBeVerifiedUseCase = checkIfCurrentSessionCanBeVerifiedUseCase,
|
||||||
|
@ -100,6 +98,7 @@ class SessionOverviewViewModelTest {
|
||||||
pendingAuthHandler = fakePendingAuthHandler.instance,
|
pendingAuthHandler = fakePendingAuthHandler.instance,
|
||||||
activeSessionHolder = fakeActiveSessionHolder.instance,
|
activeSessionHolder = fakeActiveSessionHolder.instance,
|
||||||
refreshDevicesUseCase = refreshDevicesUseCase,
|
refreshDevicesUseCase = refreshDevicesUseCase,
|
||||||
|
togglePushNotificationUseCase = togglePushNotificationUseCase.instance,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
@ -116,6 +115,13 @@ class SessionOverviewViewModelTest {
|
||||||
unmockkAll()
|
unmockkAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given the viewModel has been initialized then pushers are refreshed`() {
|
||||||
|
createViewModel()
|
||||||
|
|
||||||
|
fakeActiveSessionHolder.fakeSession.pushersService().verifyRefreshPushers()
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given the viewModel has been initialized then viewState is updated with session info`() {
|
fun `given the viewModel has been initialized then viewState is updated with session info`() {
|
||||||
val deviceFullInfo = mockk<DeviceFullInfo>()
|
val deviceFullInfo = mockk<DeviceFullInfo>()
|
||||||
|
@ -125,7 +131,7 @@ class SessionOverviewViewModelTest {
|
||||||
deviceId = A_SESSION_ID_1,
|
deviceId = A_SESSION_ID_1,
|
||||||
deviceInfo = Success(deviceFullInfo),
|
deviceInfo = Success(deviceFullInfo),
|
||||||
isCurrentSessionTrusted = true,
|
isCurrentSessionTrusted = true,
|
||||||
pushers = Loading(),
|
notificationsEnabled = true,
|
||||||
)
|
)
|
||||||
|
|
||||||
val viewModel = createViewModel()
|
val viewModel = createViewModel()
|
||||||
|
@ -221,7 +227,7 @@ class SessionOverviewViewModelTest {
|
||||||
isCurrentSessionTrusted = true,
|
isCurrentSessionTrusted = true,
|
||||||
deviceInfo = Success(deviceFullInfo),
|
deviceInfo = Success(deviceFullInfo),
|
||||||
isLoading = false,
|
isLoading = false,
|
||||||
pushers = Loading(),
|
notificationsEnabled = true,
|
||||||
)
|
)
|
||||||
|
|
||||||
// When
|
// When
|
||||||
|
@ -258,7 +264,7 @@ class SessionOverviewViewModelTest {
|
||||||
isCurrentSessionTrusted = true,
|
isCurrentSessionTrusted = true,
|
||||||
deviceInfo = Success(deviceFullInfo),
|
deviceInfo = Success(deviceFullInfo),
|
||||||
isLoading = false,
|
isLoading = false,
|
||||||
pushers = Loading(),
|
notificationsEnabled = true,
|
||||||
)
|
)
|
||||||
fakeStringProvider.given(R.string.authentication_error, AUTH_ERROR_MESSAGE)
|
fakeStringProvider.given(R.string.authentication_error, AUTH_ERROR_MESSAGE)
|
||||||
|
|
||||||
|
@ -293,7 +299,7 @@ class SessionOverviewViewModelTest {
|
||||||
isCurrentSessionTrusted = true,
|
isCurrentSessionTrusted = true,
|
||||||
deviceInfo = Success(deviceFullInfo),
|
deviceInfo = Success(deviceFullInfo),
|
||||||
isLoading = false,
|
isLoading = false,
|
||||||
pushers = Loading(),
|
notificationsEnabled = true,
|
||||||
)
|
)
|
||||||
fakeStringProvider.given(R.string.matrix_error, AN_ERROR_MESSAGE)
|
fakeStringProvider.given(R.string.matrix_error, AN_ERROR_MESSAGE)
|
||||||
|
|
||||||
|
@ -461,26 +467,22 @@ 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 pushers = listOf(aPusher(deviceId = A_SESSION_ID_1))
|
||||||
fakeSession.pushersService().givenPushersLive(pushers)
|
fakeActiveSessionHolder.fakeSession.pushersService().givenPushersLive(pushers)
|
||||||
|
|
||||||
val viewModel = createViewModel()
|
val viewModel = createViewModel()
|
||||||
|
|
||||||
viewModel.test()
|
viewModel.test()
|
||||||
.assertLatestState { state -> state.pushers.invoke() == pushers }
|
.assertLatestState { state -> state.notificationsEnabled }
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `when handle TogglePushNotifications, then toggle enabled for device pushers`() {
|
fun `when handle TogglePushNotifications, then execute use case and update state`() {
|
||||||
val pushers = listOf(
|
|
||||||
aPusher(deviceId = A_SESSION_ID_1, enabled = false),
|
|
||||||
aPusher(deviceId = "another id", enabled = false)
|
|
||||||
)
|
|
||||||
fakeSession.pushersService().givenPushersLive(pushers)
|
|
||||||
|
|
||||||
val viewModel = createViewModel()
|
val viewModel = createViewModel()
|
||||||
|
|
||||||
viewModel.handle(SessionOverviewAction.TogglePushNotifications(A_SESSION_ID_1, true))
|
viewModel.handle(SessionOverviewAction.TogglePushNotifications(A_SESSION_ID_1, true))
|
||||||
|
|
||||||
fakeSession.pushersService().verifyOnlyTogglePusherCalled(pushers.first(), true)
|
togglePushNotificationUseCase.verifyExecute(A_SESSION_ID_1, true)
|
||||||
|
viewModel.test().assertLatestState { state -> state.notificationsEnabled }.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* 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.overview
|
||||||
|
|
||||||
|
import im.vector.app.test.fakes.FakeActiveSessionHolder
|
||||||
|
import im.vector.app.test.fixtures.PusherFixture
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
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
|
||||||
|
|
||||||
|
class TogglePushNotificationUseCaseTest {
|
||||||
|
|
||||||
|
private val activeSessionHolder = FakeActiveSessionHolder()
|
||||||
|
private val togglePushNotificationUseCase = TogglePushNotificationUseCase(activeSessionHolder.instance)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when execute, then toggle enabled for device pushers`() = runTest {
|
||||||
|
val sessionId = "a_session_id"
|
||||||
|
val pushers = listOf(
|
||||||
|
PusherFixture.aPusher(deviceId = sessionId, enabled = false),
|
||||||
|
PusherFixture.aPusher(deviceId = "another id", enabled = false)
|
||||||
|
)
|
||||||
|
activeSessionHolder.fakeSession.pushersService().givenPushersLive(pushers)
|
||||||
|
activeSessionHolder.fakeSession.pushersService().givenGetPushers(pushers)
|
||||||
|
|
||||||
|
togglePushNotificationUseCase.execute(sessionId, true)
|
||||||
|
|
||||||
|
activeSessionHolder.fakeSession.pushersService().verifyTogglePusherCalled(pushers.first(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when execute, then toggle local notification settings`() = runTest {
|
||||||
|
val sessionId = "a_session_id"
|
||||||
|
val pushers = listOf(
|
||||||
|
PusherFixture.aPusher(deviceId = sessionId, enabled = false),
|
||||||
|
PusherFixture.aPusher(deviceId = "another id", enabled = false)
|
||||||
|
)
|
||||||
|
activeSessionHolder.fakeSession.pushersService().givenPushersLive(pushers)
|
||||||
|
activeSessionHolder.fakeSession.accountDataService().givenGetUserAccountDataEventReturns(
|
||||||
|
UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + sessionId,
|
||||||
|
LocalNotificationSettingsContent(isSilenced = true).toContent()
|
||||||
|
)
|
||||||
|
|
||||||
|
togglePushNotificationUseCase.execute(sessionId, true)
|
||||||
|
|
||||||
|
activeSessionHolder.fakeSession.accountDataService().verifyUpdateUserAccountDataEventSucceeds(
|
||||||
|
UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + sessionId,
|
||||||
|
LocalNotificationSettingsContent(isSilenced = false).toContent(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,6 @@
|
||||||
package im.vector.app.test.fakes
|
package im.vector.app.test.fakes
|
||||||
|
|
||||||
import androidx.lifecycle.liveData
|
import androidx.lifecycle.liveData
|
||||||
import io.mockk.Ordering
|
|
||||||
import io.mockk.coVerify
|
import io.mockk.coVerify
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.justRun
|
import io.mockk.justRun
|
||||||
|
@ -38,16 +37,8 @@ class FakePushersService : PushersService by mockk(relaxed = true) {
|
||||||
every { getPushersLive() } returns liveData { emit(pushers) }
|
every { getPushersLive() } returns liveData { emit(pushers) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun verifyOnlyGetPushersAndTogglePusherCalled(pusher: Pusher, enable: Boolean) {
|
fun verifyTogglePusherCalled(pusher: Pusher, enable: Boolean) {
|
||||||
coVerify(ordering = Ordering.ALL) {
|
coVerify {
|
||||||
getPushers()
|
|
||||||
togglePusher(pusher, enable)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun verifyOnlyTogglePusherCalled(pusher: Pusher, enable: Boolean) {
|
|
||||||
coVerify(ordering = Ordering.ALL) {
|
|
||||||
getPushersLive() // verifies only getPushersLive and the following togglePusher was called
|
|
||||||
togglePusher(pusher, enable)
|
togglePusher(pusher, enable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ import org.matrix.android.sdk.api.session.accountdata.SessionAccountDataService
|
||||||
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
|
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
|
||||||
import org.matrix.android.sdk.api.session.events.model.Content
|
import org.matrix.android.sdk.api.session.events.model.Content
|
||||||
|
|
||||||
class FakeSessionAccountDataService : SessionAccountDataService by mockk() {
|
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 UserAccountDataEvent(type, content)
|
||||||
|
|
|
@ -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.test.fakes
|
||||||
|
|
||||||
|
import im.vector.app.features.settings.devices.v2.overview.TogglePushNotificationUseCase
|
||||||
|
import io.mockk.coJustRun
|
||||||
|
import io.mockk.coVerify
|
||||||
|
import io.mockk.mockk
|
||||||
|
|
||||||
|
class FakeTogglePushNotificationUseCase {
|
||||||
|
|
||||||
|
val instance = mockk<TogglePushNotificationUseCase> {
|
||||||
|
coJustRun { execute(any(), any()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verifyExecute(deviceId: String, enabled: Boolean) {
|
||||||
|
coVerify { instance.execute(deviceId, enabled) }
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue